[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:
Danish Ahmed Mirza 2022-06-06 18:53:49 +05:30 committed by GitHub
parent c76a1e6a4c
commit 02c1bc50b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 360 additions and 360 deletions

View File

@ -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) {

View File

@ -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);
}
};
}
}

View File

@ -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>
);
});

View File

@ -66,7 +66,6 @@ const RoomItem = ({
hideChannel={hideChannel}
testID={testID}
type={type}
theme={theme}
isFocused={isFocused}
swipeEnabled={swipeEnabled}
displayMode={displayMode}>

View File

@ -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;

View File

@ -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;

View File

@ -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: {

View File

@ -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>

View File

@ -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
};

View File

@ -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 };

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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));

View File

@ -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",

View File

@ -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

View File

@ -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: