[NEW] Update room item animations (#4024)
* Upgrade react-native-gesture-handler to 2.3.0 * Update room item animations to reanimated v2 * Add Parallax animation on fav and hide buttons and additional swipe gesture to toggleFav * Fix tests * Ignore typescript error for setTimeout function * Update pods * Fix blank area on swiping all the way right/left * Fix Action Buttons on devices with notch * Update snapshot * Use colors from useTheme * Destructure props * Proper types for nativeEvent and event * Remove toggleFav gesture * Clean bits * Fix lint error * Fix position of Room Action Buttons on MasterDetail * Remove comment * Update animations logic * Add haptic feedback on swipe * Add haptic feedback on unswipe gesture * Update react-native-gesture-handler to 2.4.2 * update pods * Migrating off RNGHEnabledRootView * Update types to ReturnType<typeof setTimeout> Co-authored-by: GleidsonDaniel <gleidson10daniel@hotmail.com>
This commit is contained in:
parent
c76a1e6a4c
commit
02c1bc50b9
|
@ -12,7 +12,6 @@ import com.facebook.react.ReactRootView;
|
|||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactFragmentActivity;
|
||||
|
||||
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
|
||||
import com.zoontek.rnbootsplash.RNBootSplash;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
|
@ -51,16 +50,6 @@ public class MainActivity extends ReactFragmentActivity {
|
|||
return "RocketChatRN";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new ReactActivityDelegate(this, getMainComponentName()) {
|
||||
@Override
|
||||
protected ReactRootView createRootView() {
|
||||
return new RNGestureHandlerEnabledRootView(MainActivity.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// from react-native-orientation
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
|
|
|
@ -3,21 +3,10 @@ package chat.rocket.reactnative.share;
|
|||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
|
||||
|
||||
public class ShareActivity extends ReactActivity {
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "ShareRocketChatRN";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new ReactActivityDelegate(this, getMainComponentName()) {
|
||||
@Override
|
||||
protected ReactRootView createRootView() {
|
||||
return new RNGestureHandlerEnabledRootView(ShareActivity.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,25 +1,32 @@
|
|||
import React from 'react';
|
||||
import { Animated, View } from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
interpolate,
|
||||
withSpring,
|
||||
runOnJS,
|
||||
useAnimatedReaction,
|
||||
useSharedValue
|
||||
} from 'react-native-reanimated';
|
||||
import { RectButton } from 'react-native-gesture-handler';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
|
||||
import { isRTL } from '../../i18n';
|
||||
import { CustomIcon } from '../CustomIcon';
|
||||
import { DisplayMode, themes } from '../../lib/constants';
|
||||
import { DisplayMode } from '../../lib/constants';
|
||||
import styles, { ACTION_WIDTH, LONG_SWIPE, ROW_HEIGHT_CONDENSED } from './styles';
|
||||
import { ILeftActionsProps, IRightActionsProps } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
const reverse = new Animated.Value(isRTL() ? -1 : 1);
|
||||
const CONDENSED_ICON_SIZE = 24;
|
||||
const EXPANDED_ICON_SIZE = 28;
|
||||
|
||||
export const LeftActions = React.memo(({ theme, transX, isRead, width, onToggleReadPress, displayMode }: ILeftActionsProps) => {
|
||||
const translateX = Animated.multiply(
|
||||
transX.interpolate({
|
||||
inputRange: [0, ACTION_WIDTH],
|
||||
outputRange: [-ACTION_WIDTH, 0]
|
||||
}),
|
||||
reverse
|
||||
);
|
||||
export const LeftActions = React.memo(({ transX, isRead, width, onToggleReadPress, displayMode }: ILeftActionsProps) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const animatedStyles = useAnimatedStyle(() => ({
|
||||
transform: [{ translateX: transX.value }]
|
||||
}));
|
||||
|
||||
const isCondensed = displayMode === DisplayMode.Condensed;
|
||||
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
||||
|
@ -29,20 +36,16 @@ export const LeftActions = React.memo(({ theme, transX, isRead, width, onToggleR
|
|||
<Animated.View
|
||||
style={[
|
||||
styles.actionLeftButtonContainer,
|
||||
{
|
||||
right: width - ACTION_WIDTH,
|
||||
width,
|
||||
transform: [{ translateX }],
|
||||
backgroundColor: themes[theme].tintColor
|
||||
},
|
||||
viewHeight
|
||||
{ width: width * 2, backgroundColor: colors.tintColor, right: '100%' },
|
||||
viewHeight,
|
||||
animatedStyles
|
||||
]}>
|
||||
<View style={[styles.actionLeftButtonContainer, viewHeight]}>
|
||||
<RectButton style={styles.actionButton} onPress={onToggleReadPress}>
|
||||
<CustomIcon
|
||||
size={isCondensed ? CONDENSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
name={isRead ? 'flag' : 'check'}
|
||||
color={themes[theme].buttonText}
|
||||
color={colors.buttonText}
|
||||
/>
|
||||
</RectButton>
|
||||
</View>
|
||||
|
@ -51,64 +54,102 @@ export const LeftActions = React.memo(({ theme, transX, isRead, width, onToggleR
|
|||
);
|
||||
});
|
||||
|
||||
export const RightActions = React.memo(
|
||||
({ transX, favorite, width, toggleFav, onHidePress, theme, displayMode }: IRightActionsProps) => {
|
||||
const translateXFav = Animated.multiply(
|
||||
transX.interpolate({
|
||||
inputRange: [-width / 2, -ACTION_WIDTH * 2, 0],
|
||||
outputRange: [width / 2, width - ACTION_WIDTH * 2, width]
|
||||
}),
|
||||
reverse
|
||||
);
|
||||
const translateXHide = Animated.multiply(
|
||||
transX.interpolate({
|
||||
inputRange: [-width, -LONG_SWIPE, -ACTION_WIDTH * 2, 0],
|
||||
outputRange: [0, width - LONG_SWIPE, width - ACTION_WIDTH, width]
|
||||
}),
|
||||
reverse
|
||||
);
|
||||
export const RightActions = React.memo(({ transX, favorite, width, toggleFav, onHidePress, displayMode }: IRightActionsProps) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const isCondensed = displayMode === DisplayMode.Condensed;
|
||||
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
||||
const animatedFavStyles = useAnimatedStyle(() => ({ transform: [{ translateX: transX.value }] }));
|
||||
|
||||
return (
|
||||
<View style={[styles.actionsLeftContainer, viewHeight]} pointerEvents='box-none'>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.actionRightButtonContainer,
|
||||
{
|
||||
width,
|
||||
transform: [{ translateX: translateXFav }],
|
||||
backgroundColor: themes[theme].hideBackground
|
||||
},
|
||||
viewHeight
|
||||
]}>
|
||||
<RectButton style={[styles.actionButton, { backgroundColor: themes[theme].favoriteBackground }]} onPress={toggleFav}>
|
||||
<CustomIcon
|
||||
size={isCondensed ? CONDENSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
name={favorite ? 'star-filled' : 'star'}
|
||||
color={themes[theme].buttonText}
|
||||
/>
|
||||
</RectButton>
|
||||
</Animated.View>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.actionRightButtonContainer,
|
||||
{
|
||||
width,
|
||||
transform: [{ translateX: translateXHide }]
|
||||
},
|
||||
isCondensed && { height: ROW_HEIGHT_CONDENSED }
|
||||
]}>
|
||||
<RectButton style={[styles.actionButton, { backgroundColor: themes[theme].hideBackground }]} onPress={onHidePress}>
|
||||
<CustomIcon
|
||||
size={isCondensed ? CONDENSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
name='unread-on-top-disabled'
|
||||
color={themes[theme].buttonText}
|
||||
/>
|
||||
</RectButton>
|
||||
</Animated.View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
);
|
||||
const translateXHide = useSharedValue(0);
|
||||
|
||||
const triggerHideAnimation = (toValue: number) => {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||
translateXHide.value = withSpring(toValue, { overshootClamping: true, mass: 0.7 });
|
||||
};
|
||||
|
||||
useAnimatedReaction(
|
||||
() => transX.value,
|
||||
(currentTransX, previousTransX) => {
|
||||
// Triggers the animation and hapticFeedback if swipe reaches/unreaches the threshold.
|
||||
if (I18n.isRTL) {
|
||||
if (previousTransX && currentTransX > LONG_SWIPE && previousTransX <= LONG_SWIPE) {
|
||||
runOnJS(triggerHideAnimation)(ACTION_WIDTH);
|
||||
} else if (previousTransX && currentTransX <= LONG_SWIPE && previousTransX > LONG_SWIPE) {
|
||||
runOnJS(triggerHideAnimation)(0);
|
||||
}
|
||||
} else if (previousTransX && currentTransX < -LONG_SWIPE && previousTransX >= -LONG_SWIPE) {
|
||||
runOnJS(triggerHideAnimation)(-ACTION_WIDTH);
|
||||
} else if (previousTransX && currentTransX >= -LONG_SWIPE && previousTransX < -LONG_SWIPE) {
|
||||
runOnJS(triggerHideAnimation)(0);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const animatedHideStyles = useAnimatedStyle(() => {
|
||||
if (I18n.isRTL) {
|
||||
if (transX.value < LONG_SWIPE && transX.value >= 2 * ACTION_WIDTH) {
|
||||
const parallaxSwipe = interpolate(
|
||||
transX.value,
|
||||
[2 * ACTION_WIDTH, LONG_SWIPE],
|
||||
[ACTION_WIDTH, ACTION_WIDTH + 0.1 * transX.value]
|
||||
);
|
||||
return { transform: [{ translateX: parallaxSwipe + translateXHide.value }] };
|
||||
}
|
||||
return { transform: [{ translateX: transX.value - ACTION_WIDTH + translateXHide.value }] };
|
||||
}
|
||||
if (transX.value > -LONG_SWIPE && transX.value <= -2 * ACTION_WIDTH) {
|
||||
const parallaxSwipe = interpolate(
|
||||
transX.value,
|
||||
[-2 * ACTION_WIDTH, -LONG_SWIPE],
|
||||
[-ACTION_WIDTH, -ACTION_WIDTH + 0.1 * transX.value]
|
||||
);
|
||||
return { transform: [{ translateX: parallaxSwipe + translateXHide.value }] };
|
||||
}
|
||||
return { transform: [{ translateX: transX.value + ACTION_WIDTH + translateXHide.value }] };
|
||||
});
|
||||
|
||||
const isCondensed = displayMode === DisplayMode.Condensed;
|
||||
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
||||
|
||||
return (
|
||||
<View style={[styles.actionsLeftContainer, viewHeight]} pointerEvents='box-none'>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.actionRightButtonContainer,
|
||||
{
|
||||
width,
|
||||
backgroundColor: colors.favoriteBackground,
|
||||
left: '100%'
|
||||
},
|
||||
viewHeight,
|
||||
animatedFavStyles
|
||||
]}>
|
||||
<RectButton style={[styles.actionButton, { backgroundColor: colors.favoriteBackground }]} onPress={toggleFav}>
|
||||
<CustomIcon
|
||||
size={isCondensed ? CONDENSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
name={favorite ? 'star-filled' : 'star'}
|
||||
color={colors.buttonText}
|
||||
/>
|
||||
</RectButton>
|
||||
</Animated.View>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.actionRightButtonContainer,
|
||||
{
|
||||
width: width * 2,
|
||||
backgroundColor: colors.hideBackground,
|
||||
left: '100%'
|
||||
},
|
||||
isCondensed && { height: ROW_HEIGHT_CONDENSED },
|
||||
animatedHideStyles
|
||||
]}>
|
||||
<RectButton style={[styles.actionButton, { backgroundColor: colors.hideBackground }]} onPress={onHidePress}>
|
||||
<CustomIcon
|
||||
size={isCondensed ? CONDENSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
name='unread-on-top-disabled'
|
||||
color={colors.buttonText}
|
||||
/>
|
||||
</RectButton>
|
||||
</Animated.View>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -66,7 +66,6 @@ const RoomItem = ({
|
|||
hideChannel={hideChannel}
|
||||
testID={testID}
|
||||
type={type}
|
||||
theme={theme}
|
||||
isFocused={isFocused}
|
||||
swipeEnabled={swipeEnabled}
|
||||
displayMode={displayMode}>
|
||||
|
|
|
@ -1,207 +1,98 @@
|
|||
import React from 'react';
|
||||
import { Animated } from 'react-native';
|
||||
import Animated, {
|
||||
useAnimatedGestureHandler,
|
||||
useSharedValue,
|
||||
useAnimatedStyle,
|
||||
withSpring,
|
||||
runOnJS
|
||||
} from 'react-native-reanimated';
|
||||
import {
|
||||
GestureEvent,
|
||||
HandlerStateChangeEventPayload,
|
||||
LongPressGestureHandler,
|
||||
PanGestureHandler,
|
||||
PanGestureHandlerEventPayload,
|
||||
State
|
||||
State,
|
||||
HandlerStateChangeEventPayload,
|
||||
PanGestureHandlerEventPayload
|
||||
} from 'react-native-gesture-handler';
|
||||
|
||||
import Touch from '../../utils/touch';
|
||||
import { ACTION_WIDTH, LONG_SWIPE, SMALL_SWIPE } from './styles';
|
||||
import { isRTL } from '../../i18n';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { LeftActions, RightActions } from './Actions';
|
||||
import { ITouchableProps } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
class Touchable extends React.Component<ITouchableProps, any> {
|
||||
private dragX: Animated.Value;
|
||||
private rowOffSet: Animated.Value;
|
||||
private reverse: Animated.Value;
|
||||
private transX: Animated.AnimatedAddition;
|
||||
private transXReverse: Animated.AnimatedMultiplication;
|
||||
private _onGestureEvent: (event: GestureEvent<PanGestureHandlerEventPayload>) => void;
|
||||
private _value: number;
|
||||
const Touchable = ({
|
||||
children,
|
||||
type,
|
||||
onPress,
|
||||
onLongPress,
|
||||
testID,
|
||||
width,
|
||||
favorite,
|
||||
isRead,
|
||||
rid,
|
||||
toggleFav,
|
||||
toggleRead,
|
||||
hideChannel,
|
||||
isFocused,
|
||||
swipeEnabled,
|
||||
displayMode
|
||||
}: ITouchableProps): React.ReactElement => {
|
||||
const { theme, colors } = useTheme();
|
||||
|
||||
constructor(props: ITouchableProps) {
|
||||
super(props);
|
||||
this.dragX = new Animated.Value(0);
|
||||
this.rowOffSet = new Animated.Value(0);
|
||||
this.reverse = new Animated.Value(isRTL() ? -1 : 1);
|
||||
this.transX = Animated.add(this.rowOffSet, this.dragX);
|
||||
this.transXReverse = Animated.multiply(this.transX, this.reverse);
|
||||
this.state = {
|
||||
rowState: 0 // 0: closed, 1: right opened, -1: left opened
|
||||
};
|
||||
this._onGestureEvent = Animated.event([{ nativeEvent: { translationX: this.dragX } }], { useNativeDriver: true });
|
||||
this._value = 0;
|
||||
}
|
||||
const rowOffSet = useSharedValue(0);
|
||||
const transX = useSharedValue(0);
|
||||
const rowState = useSharedValue(0); // 0: closed, 1: right opened, -1: left opened
|
||||
let _value = 0;
|
||||
|
||||
_onHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload & PanGestureHandlerEventPayload }) => {
|
||||
if (nativeEvent.oldState === State.ACTIVE) {
|
||||
this._handleRelease(nativeEvent);
|
||||
}
|
||||
const close = () => {
|
||||
rowState.value = 0;
|
||||
transX.value = withSpring(0, { overshootClamping: true });
|
||||
rowOffSet.value = 0;
|
||||
};
|
||||
|
||||
onLongPressHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
|
||||
if (nativeEvent.state === State.ACTIVE) {
|
||||
this.onLongPress();
|
||||
}
|
||||
};
|
||||
|
||||
_handleRelease = (nativeEvent: PanGestureHandlerEventPayload) => {
|
||||
const { translationX } = nativeEvent;
|
||||
const { rowState } = this.state;
|
||||
this._value += translationX;
|
||||
|
||||
let toValue = 0;
|
||||
if (rowState === 0) {
|
||||
// if no option is opened
|
||||
if (translationX > 0 && translationX < LONG_SWIPE) {
|
||||
// open leading option if he swipe right but not enough to trigger action
|
||||
if (isRTL()) {
|
||||
toValue = 2 * ACTION_WIDTH;
|
||||
} else {
|
||||
toValue = ACTION_WIDTH;
|
||||
}
|
||||
this.setState({ rowState: -1 });
|
||||
} else if (translationX >= LONG_SWIPE) {
|
||||
toValue = 0;
|
||||
if (isRTL()) {
|
||||
this.hideChannel();
|
||||
} else {
|
||||
this.toggleRead();
|
||||
}
|
||||
} else if (translationX < 0 && translationX > -LONG_SWIPE) {
|
||||
// open trailing option if he swipe left
|
||||
if (isRTL()) {
|
||||
toValue = -ACTION_WIDTH;
|
||||
} else {
|
||||
toValue = -2 * ACTION_WIDTH;
|
||||
}
|
||||
this.setState({ rowState: 1 });
|
||||
} else if (translationX <= -LONG_SWIPE) {
|
||||
toValue = 0;
|
||||
this.setState({ rowState: 0 });
|
||||
if (isRTL()) {
|
||||
this.toggleRead();
|
||||
} else {
|
||||
this.hideChannel();
|
||||
}
|
||||
} else {
|
||||
toValue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (rowState === -1) {
|
||||
// if left option is opened
|
||||
if (this._value < SMALL_SWIPE) {
|
||||
toValue = 0;
|
||||
this.setState({ rowState: 0 });
|
||||
} else if (this._value > LONG_SWIPE) {
|
||||
toValue = 0;
|
||||
this.setState({ rowState: 0 });
|
||||
if (isRTL()) {
|
||||
this.hideChannel();
|
||||
} else {
|
||||
this.toggleRead();
|
||||
}
|
||||
} else if (isRTL()) {
|
||||
toValue = 2 * ACTION_WIDTH;
|
||||
} else {
|
||||
toValue = ACTION_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
if (rowState === 1) {
|
||||
// if right option is opened
|
||||
if (this._value > -2 * SMALL_SWIPE) {
|
||||
toValue = 0;
|
||||
this.setState({ rowState: 0 });
|
||||
} else if (this._value < -LONG_SWIPE) {
|
||||
toValue = 0;
|
||||
this.setState({ rowState: 0 });
|
||||
if (isRTL()) {
|
||||
this.toggleRead();
|
||||
} else {
|
||||
this.hideChannel();
|
||||
}
|
||||
} else if (isRTL()) {
|
||||
toValue = -ACTION_WIDTH;
|
||||
} else {
|
||||
toValue = -2 * ACTION_WIDTH;
|
||||
}
|
||||
}
|
||||
this._animateRow(toValue);
|
||||
};
|
||||
|
||||
_animateRow = (toValue: number) => {
|
||||
this.rowOffSet.setValue(this._value);
|
||||
this._value = toValue;
|
||||
this.dragX.setValue(0);
|
||||
Animated.spring(this.rowOffSet, {
|
||||
toValue,
|
||||
bounciness: 0,
|
||||
useNativeDriver: true
|
||||
}).start();
|
||||
};
|
||||
|
||||
close = () => {
|
||||
this.setState({ rowState: 0 });
|
||||
this._animateRow(0);
|
||||
};
|
||||
|
||||
toggleFav = () => {
|
||||
const { toggleFav, rid, favorite } = this.props;
|
||||
const handleToggleFav = () => {
|
||||
if (toggleFav) {
|
||||
toggleFav(rid, favorite);
|
||||
}
|
||||
this.close();
|
||||
close();
|
||||
};
|
||||
|
||||
toggleRead = () => {
|
||||
const { toggleRead, rid, isRead } = this.props;
|
||||
const handleToggleRead = () => {
|
||||
if (toggleRead) {
|
||||
toggleRead(rid, isRead);
|
||||
}
|
||||
};
|
||||
|
||||
hideChannel = () => {
|
||||
const { hideChannel, rid, type } = this.props;
|
||||
const handleHideChannel = () => {
|
||||
if (hideChannel) {
|
||||
hideChannel(rid, type);
|
||||
}
|
||||
};
|
||||
|
||||
onToggleReadPress = () => {
|
||||
this.toggleRead();
|
||||
this.close();
|
||||
const onToggleReadPress = () => {
|
||||
handleToggleRead();
|
||||
close();
|
||||
};
|
||||
|
||||
onHidePress = () => {
|
||||
this.hideChannel();
|
||||
this.close();
|
||||
const onHidePress = () => {
|
||||
handleHideChannel();
|
||||
close();
|
||||
};
|
||||
|
||||
onPress = () => {
|
||||
const { rowState } = this.state;
|
||||
if (rowState !== 0) {
|
||||
this.close();
|
||||
const handlePress = () => {
|
||||
if (rowState.value !== 0) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
const { onPress } = this.props;
|
||||
if (onPress) {
|
||||
onPress();
|
||||
}
|
||||
};
|
||||
|
||||
onLongPress = () => {
|
||||
const { rowState } = this.state;
|
||||
const { onLongPress } = this.props;
|
||||
if (rowState !== 0) {
|
||||
this.close();
|
||||
const handleLongPress = () => {
|
||||
if (rowState.value !== 0) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -210,55 +101,139 @@ class Touchable extends React.Component<ITouchableProps, any> {
|
|||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { testID, isRead, width, favorite, children, theme, isFocused, swipeEnabled, displayMode } = this.props;
|
||||
const onLongPressHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
|
||||
if (nativeEvent.state === State.ACTIVE) {
|
||||
handleLongPress();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<LongPressGestureHandler onHandlerStateChange={this.onLongPressHandlerStateChange}>
|
||||
<Animated.View>
|
||||
<PanGestureHandler
|
||||
minDeltaX={20}
|
||||
onGestureEvent={this._onGestureEvent}
|
||||
onHandlerStateChange={this._onHandlerStateChange}
|
||||
enabled={swipeEnabled}>
|
||||
<Animated.View>
|
||||
<LeftActions
|
||||
transX={this.transXReverse}
|
||||
isRead={isRead}
|
||||
width={width}
|
||||
onToggleReadPress={this.onToggleReadPress}
|
||||
const handleRelease = (event: PanGestureHandlerEventPayload) => {
|
||||
const { translationX } = event;
|
||||
_value += translationX;
|
||||
let toValue = 0;
|
||||
if (rowState.value === 0) {
|
||||
// if no option is opened
|
||||
if (translationX > 0 && translationX < LONG_SWIPE) {
|
||||
if (I18n.isRTL) {
|
||||
toValue = 2 * ACTION_WIDTH;
|
||||
} else {
|
||||
toValue = ACTION_WIDTH;
|
||||
}
|
||||
rowState.value = -1;
|
||||
} else if (translationX >= LONG_SWIPE) {
|
||||
toValue = 0;
|
||||
if (I18n.isRTL) {
|
||||
handleHideChannel();
|
||||
} else {
|
||||
handleToggleRead();
|
||||
}
|
||||
} else if (translationX < 0 && translationX > -LONG_SWIPE) {
|
||||
// open trailing option if he swipe left
|
||||
if (I18n.isRTL) {
|
||||
toValue = -ACTION_WIDTH;
|
||||
} else {
|
||||
toValue = -2 * ACTION_WIDTH;
|
||||
}
|
||||
rowState.value = 1;
|
||||
} else if (translationX <= -LONG_SWIPE) {
|
||||
toValue = 0;
|
||||
rowState.value = 1;
|
||||
if (I18n.isRTL) {
|
||||
handleToggleRead();
|
||||
} else {
|
||||
handleHideChannel();
|
||||
}
|
||||
} else {
|
||||
toValue = 0;
|
||||
}
|
||||
} else if (rowState.value === -1) {
|
||||
// if left option is opened
|
||||
if (_value < SMALL_SWIPE) {
|
||||
toValue = 0;
|
||||
rowState.value = 0;
|
||||
} else if (_value > LONG_SWIPE) {
|
||||
toValue = 0;
|
||||
rowState.value = 0;
|
||||
if (I18n.isRTL) {
|
||||
handleHideChannel();
|
||||
} else {
|
||||
handleToggleRead();
|
||||
}
|
||||
} else if (I18n.isRTL) {
|
||||
toValue = 2 * ACTION_WIDTH;
|
||||
} else {
|
||||
toValue = ACTION_WIDTH;
|
||||
}
|
||||
} else if (rowState.value === 1) {
|
||||
// if right option is opened
|
||||
if (_value > -2 * SMALL_SWIPE) {
|
||||
toValue = 0;
|
||||
rowState.value = 0;
|
||||
} else if (_value < -LONG_SWIPE) {
|
||||
if (I18n.isRTL) {
|
||||
handleToggleRead();
|
||||
} else {
|
||||
handleHideChannel();
|
||||
}
|
||||
} else if (I18n.isRTL) {
|
||||
toValue = -ACTION_WIDTH;
|
||||
} else {
|
||||
toValue = -2 * ACTION_WIDTH;
|
||||
}
|
||||
}
|
||||
transX.value = withSpring(toValue, { overshootClamping: true });
|
||||
rowOffSet.value = toValue;
|
||||
_value = toValue;
|
||||
};
|
||||
|
||||
const onGestureEvent = useAnimatedGestureHandler({
|
||||
onActive: event => {
|
||||
transX.value = event.translationX + rowOffSet.value;
|
||||
if (transX.value > 2 * width) transX.value = 2 * width;
|
||||
},
|
||||
onEnd: event => {
|
||||
runOnJS(handleRelease)(event);
|
||||
}
|
||||
});
|
||||
|
||||
const animatedStyles = useAnimatedStyle(() => ({ transform: [{ translateX: transX.value }] }));
|
||||
|
||||
return (
|
||||
<LongPressGestureHandler onHandlerStateChange={onLongPressHandlerStateChange}>
|
||||
<Animated.View>
|
||||
<PanGestureHandler activeOffsetX={[-20, 20]} onGestureEvent={onGestureEvent} enabled={swipeEnabled}>
|
||||
<Animated.View>
|
||||
<LeftActions
|
||||
transX={transX}
|
||||
isRead={isRead}
|
||||
width={width}
|
||||
onToggleReadPress={onToggleReadPress}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<RightActions
|
||||
transX={transX}
|
||||
favorite={favorite}
|
||||
width={width}
|
||||
toggleFav={handleToggleFav}
|
||||
onHidePress={onHidePress}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<Animated.View style={animatedStyles}>
|
||||
<Touch
|
||||
onPress={handlePress}
|
||||
theme={theme}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<RightActions
|
||||
transX={this.transXReverse}
|
||||
favorite={favorite}
|
||||
width={width}
|
||||
toggleFav={this.toggleFav}
|
||||
onHidePress={this.onHidePress}
|
||||
theme={theme}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<Animated.View
|
||||
testID={testID}
|
||||
style={{
|
||||
transform: [{ translateX: this.transX }]
|
||||
backgroundColor: isFocused ? colors.chatComponentBackground : colors.backgroundColor
|
||||
}}>
|
||||
<Touch
|
||||
onPress={this.onPress}
|
||||
theme={theme}
|
||||
testID={testID}
|
||||
style={{
|
||||
backgroundColor: isFocused ? themes[theme].chatComponentBackground : themes[theme].backgroundColor
|
||||
}}>
|
||||
{children}
|
||||
</Touch>
|
||||
</Animated.View>
|
||||
{children}
|
||||
</Touch>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
</Animated.View>
|
||||
</LongPressGestureHandler>
|
||||
);
|
||||
}
|
||||
}
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
</Animated.View>
|
||||
</LongPressGestureHandler>
|
||||
);
|
||||
};
|
||||
|
||||
export default Touchable;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import React from 'react';
|
||||
import { Animated } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { TUserStatus, ILastMessage, SubscriptionType, IOmnichannelSource } from '../../definitions';
|
||||
|
||||
export interface ILeftActionsProps {
|
||||
theme: TSupportedThemes;
|
||||
transX: Animated.AnimatedAddition | Animated.AnimatedMultiplication;
|
||||
transX: Animated.SharedValue<number>;
|
||||
isRead: boolean;
|
||||
width: number;
|
||||
onToggleReadPress(): void;
|
||||
|
@ -14,8 +13,7 @@ export interface ILeftActionsProps {
|
|||
}
|
||||
|
||||
export interface IRightActionsProps {
|
||||
theme: TSupportedThemes;
|
||||
transX: Animated.AnimatedAddition | Animated.AnimatedMultiplication;
|
||||
transX: Animated.SharedValue<number>;
|
||||
favorite: boolean;
|
||||
width: number;
|
||||
toggleFav(): void;
|
||||
|
@ -159,7 +157,6 @@ export interface ITouchableProps {
|
|||
toggleFav: Function;
|
||||
toggleRead: Function;
|
||||
hideChannel: Function;
|
||||
theme: TSupportedThemes;
|
||||
isFocused: boolean;
|
||||
swipeEnabled: boolean;
|
||||
displayMode: string;
|
||||
|
|
|
@ -6,7 +6,7 @@ export const ROW_HEIGHT = 75 * PixelRatio.getFontScale();
|
|||
export const ROW_HEIGHT_CONDENSED = 60 * PixelRatio.getFontScale();
|
||||
export const ACTION_WIDTH = 80;
|
||||
export const SMALL_SWIPE = ACTION_WIDTH / 2;
|
||||
export const LONG_SWIPE = ACTION_WIDTH * 3;
|
||||
export const LONG_SWIPE = ACTION_WIDTH * 2.5;
|
||||
|
||||
export default StyleSheet.create({
|
||||
flex: {
|
||||
|
|
|
@ -5,6 +5,7 @@ import { KeyCommandsEmitter } from 'react-native-keycommands';
|
|||
import { initialWindowMetrics, SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import RNScreens from 'react-native-screens';
|
||||
import { Provider } from 'react-redux';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
|
||||
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
|
||||
import { deepLinkingOpen } from './actions/deepLinking';
|
||||
|
@ -224,14 +225,16 @@ export default class Root extends React.Component<{}, IState> {
|
|||
fontScale,
|
||||
setDimensions: this.setDimensions
|
||||
}}>
|
||||
<ActionSheetProvider>
|
||||
<AppContainer />
|
||||
<TwoFactor />
|
||||
<ScreenLockedView />
|
||||
<ChangePasscodeView />
|
||||
<InAppNotification />
|
||||
<Toast />
|
||||
</ActionSheetProvider>
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<ActionSheetProvider>
|
||||
<AppContainer />
|
||||
<TwoFactor />
|
||||
<ScreenLockedView />
|
||||
<ChangePasscodeView />
|
||||
<InAppNotification />
|
||||
<Toast />
|
||||
</ActionSheetProvider>
|
||||
</GestureHandlerRootView>
|
||||
</DimensionsContext.Provider>
|
||||
</ThemeContext.Provider>
|
||||
</Provider>
|
||||
|
|
|
@ -10,7 +10,7 @@ import { IUser } from '../../definitions';
|
|||
import sdk from '../services/sdk';
|
||||
import { compareServerVersion } from './helpers/compareServerVersion';
|
||||
|
||||
export const _activeUsersSubTimeout: { activeUsersSubTimeout: boolean | ReturnType<typeof setTimeout> } = {
|
||||
export const _activeUsersSubTimeout: { activeUsersSubTimeout: boolean | ReturnType<typeof setTimeout> | number } = {
|
||||
activeUsersSubTimeout: false
|
||||
};
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ const WINDOW_TIME = 1000;
|
|||
export default class RoomSubscription {
|
||||
private rid: string;
|
||||
private isAlive: boolean;
|
||||
private timer: null | number;
|
||||
private timer: ReturnType<typeof setTimeout> | null;
|
||||
private queue: { [key: string]: IMessage };
|
||||
private messagesBatch: {};
|
||||
private _messagesBatch: { [key: string]: TMessageModel };
|
||||
|
|
|
@ -41,7 +41,7 @@ const removeListener = (listener: { stop: () => void }) => listener.stop();
|
|||
let streamListener: Promise<any> | false;
|
||||
let subServer: string;
|
||||
let queue: { [key: string]: ISubscription | IRoom } = {};
|
||||
let subTimer: number | null | false = null;
|
||||
let subTimer: ReturnType<typeof setTimeout> | null | false = null;
|
||||
const WINDOW_TIME = 500;
|
||||
|
||||
export let roomsSubscription: { stop: () => void } | null = null;
|
||||
|
|
|
@ -12,6 +12,7 @@ export default function debounce(func: Function, wait?: number, immediate?: bool
|
|||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout!);
|
||||
// @ts-ignore
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
func.apply(context, args);
|
||||
|
|
|
@ -69,7 +69,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
|
|||
private viewabilityConfig = {
|
||||
itemVisiblePercentThreshold: 10
|
||||
};
|
||||
private highlightedMessageTimeout: number | undefined | false;
|
||||
private highlightedMessageTimeout: ReturnType<typeof setTimeout> | undefined | false;
|
||||
private thread?: TThreadModel;
|
||||
private messagesObservable?: Observable<TMessageModel[] | TThreadMessageModel[]>;
|
||||
private messagesSubscription?: Subscription;
|
||||
|
|
|
@ -197,9 +197,9 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
|||
private subSubscription?: Subscription;
|
||||
private queryUnreads?: Subscription;
|
||||
private retryInit = 0;
|
||||
private retryInitTimeout?: number;
|
||||
private retryInitTimeout?: ReturnType<typeof setTimeout>;
|
||||
private retryFindCount = 0;
|
||||
private retryFindTimeout?: number;
|
||||
private retryFindTimeout?: ReturnType<typeof setTimeout>;
|
||||
private messageErrorActions?: IMessageErrorActions | null;
|
||||
private messageActions?: IMessageActions | null;
|
||||
// Type of InteractionManager.runAfterInteractions
|
||||
|
|
|
@ -536,7 +536,7 @@ PODS:
|
|||
- RNFBApp
|
||||
- RNFileViewer (2.1.5):
|
||||
- React-Core
|
||||
- RNGestureHandler (1.10.3):
|
||||
- RNGestureHandler (2.4.2):
|
||||
- React-Core
|
||||
- RNImageCropPicker (0.36.3):
|
||||
- React-Core
|
||||
|
@ -961,7 +961,7 @@ SPEC CHECKSUMS:
|
|||
EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7
|
||||
EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5
|
||||
FBLazyVector: c9b6dfcde9b3d497793c40d4ccbfbfb05092e0df
|
||||
FBReactNativeSpec: addc4f0e6ab00dc628fe91de8bfca4601762673a
|
||||
FBReactNativeSpec: c39f7fc0cd6cc64f0a2a5beffc64b1aa5d42740e
|
||||
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
||||
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
|
||||
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
|
||||
|
@ -1046,7 +1046,7 @@ SPEC CHECKSUMS:
|
|||
RNFBApp: 6fd8a7e757135d4168bf033a8812c241af7363a0
|
||||
RNFBCrashlytics: 88de72c2476b5868a892d9523b89b86c527c540e
|
||||
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
|
||||
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
|
||||
RNGestureHandler: 61628a2c859172551aa2100d3e73d1e57878392f
|
||||
RNImageCropPicker: 97289cd94fb01ab79db4e5c92938be4d0d63415d
|
||||
RNLocalize: 82a569022724d35461e2dc5b5d015a13c3ca995b
|
||||
RNReanimated: 241c586663f44f19a53883c63375fdd041253960
|
||||
|
|
|
@ -1870,7 +1870,7 @@
|
|||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -1925,7 +1925,7 @@
|
|||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import mockClipboard from '@react-native-clipboard/clipboard/jest/clipboard-mock.js';
|
||||
|
||||
require('react-native-reanimated/lib/reanimated2/jestUtils').setUpTests();
|
||||
|
||||
jest.mock('@react-native-clipboard/clipboard', () => mockClipboard);
|
||||
|
||||
jest.mock('react-native-mmkv-storage', () => ({
|
||||
|
@ -30,4 +32,6 @@ jest.mock('react-native-file-viewer', () => ({
|
|||
open: jest.fn(() => null)
|
||||
}));
|
||||
|
||||
jest.mock('expo-haptics', () => jest.fn(() => null));
|
||||
|
||||
jest.mock('./app/lib/database', () => jest.fn(() => null));
|
||||
|
|
|
@ -90,8 +90,8 @@
|
|||
"react-native-easy-grid": "^0.2.2",
|
||||
"react-native-easy-toast": "^1.2.0",
|
||||
"react-native-fast-image": "RocketChat/react-native-fast-image.git#bump-version",
|
||||
"react-native-file-viewer": "^2.1.5",
|
||||
"react-native-gesture-handler": "^1.10.3",
|
||||
"react-native-file-viewer": "^2.1.4",
|
||||
"react-native-gesture-handler": "2.4.2",
|
||||
"react-native-image-crop-picker": "RocketChat/react-native-image-crop-picker",
|
||||
"react-native-image-progress": "^1.1.1",
|
||||
"react-native-jitsi-meet": "RocketChat/react-native-jitsi-meet",
|
||||
|
|
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||
import { Dimensions, ScrollView } from 'react-native';
|
||||
import { storiesOf } from '@storybook/react-native';
|
||||
import { Provider } from 'react-redux';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
|
||||
import RoomItemComponent from '../../app/containers/RoomItem/RoomItem';
|
||||
import { longText } from '../utils';
|
||||
|
@ -39,6 +40,7 @@ const RoomItem = props => (
|
|||
|
||||
const stories = storiesOf('Room Item', module)
|
||||
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
|
||||
.addDecorator(story => <SafeAreaProvider>{story()}</SafeAreaProvider>)
|
||||
.addDecorator(story => <ScrollView style={{ backgroundColor: themes[_theme].backgroundColor }}>{story()}</ScrollView>);
|
||||
|
||||
stories.add('Basic', () => <RoomItem />);
|
||||
|
|
File diff suppressed because one or more lines are too long
20
yarn.lock
20
yarn.lock
|
@ -4525,9 +4525,9 @@
|
|||
"@types/node" "*"
|
||||
|
||||
"@types/hammerjs@^2.0.36":
|
||||
version "2.0.36"
|
||||
resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.36.tgz#17ce0a235e9ffbcdcdf5095646b374c2bf615a4c"
|
||||
integrity sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==
|
||||
version "2.0.41"
|
||||
resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.41.tgz#f6ecf57d1b12d2befcce00e928a6a097c22980aa"
|
||||
integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==
|
||||
|
||||
"@types/history@*":
|
||||
version "4.7.6"
|
||||
|
@ -12398,7 +12398,7 @@ lodash.truncate@^4.4.2:
|
|||
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
|
||||
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
|
||||
|
||||
lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.5, lodash@^4.7.0:
|
||||
lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.7.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
@ -14963,7 +14963,7 @@ react-native-fast-image@RocketChat/react-native-fast-image.git#bump-version:
|
|||
version "8.5.12"
|
||||
resolved "https://codeload.github.com/RocketChat/react-native-fast-image/tar.gz/8bdb187a4500e23ad1c95324a669c25361bbf685"
|
||||
|
||||
react-native-file-viewer@^2.1.5:
|
||||
react-native-file-viewer@^2.1.4:
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-file-viewer/-/react-native-file-viewer-2.1.5.tgz#cd4544f573108e79002b5c7e1ebfce4371885250"
|
||||
integrity sha512-MGC6sx9jsqHdefhVQ6o0akdsPGpkXgiIbpygb2Sg4g4bh7v6K1cardLV1NwGB9A6u1yICOSDT/MOC//9Ez6EUg==
|
||||
|
@ -14973,15 +14973,15 @@ react-native-flipper@^0.34.0:
|
|||
resolved "https://registry.yarnpkg.com/react-native-flipper/-/react-native-flipper-0.34.0.tgz#7df1f38ba5d97a9321125fe0fccbe47d99e6fa1d"
|
||||
integrity sha512-48wgm29HJTOlZ0DibBsvXueEOY0EPIVL0wWKbwRfgrk86+luSEuLW3aZC50oJa95zSFb9qYShTV/6dWqh4Jamg==
|
||||
|
||||
react-native-gesture-handler@^1.10.3:
|
||||
version "1.10.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz#942bbf2963bbf49fa79593600ee9d7b5dab3cfc0"
|
||||
integrity sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==
|
||||
react-native-gesture-handler@2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.4.2.tgz#de93760b0bc251d94e8ae692f9850ec3ed2e4f27"
|
||||
integrity sha512-K3oMiQV7NOVB5RvNlxkyJxU1Gn6m1cYu53MoFA542FVDSTR491d1eQkWDdqy4lW52rfF7IK7eE1LCi+kTJx7jw==
|
||||
dependencies:
|
||||
"@egjs/hammerjs" "^2.0.17"
|
||||
fbjs "^3.0.0"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
invariant "^2.2.4"
|
||||
lodash "^4.17.21"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-native-image-crop-picker@RocketChat/react-native-image-crop-picker:
|
||||
|
|
Loading…
Reference in New Issue