diff --git a/app/animations/collapse.js b/app/animations/collapse.js deleted file mode 100644 index e04895c97..000000000 --- a/app/animations/collapse.js +++ /dev/null @@ -1,77 +0,0 @@ -import { View, Animated } from 'react-native'; - -import PropTypes from 'prop-types'; -import React from 'react'; - -export default class Panel extends React.Component { - static propTypes = { - open: PropTypes.bool.isRequired, - children: PropTypes.node.isRequired, - style: PropTypes.object - } - - constructor(props) { - super(props); - this.state = { - animation: new Animated.Value() - }; - this.first = true; - this.open = false; - this.opacity = 0; - } - - componentDidMount() { - const { animation } = this.state; - const { open } = this.props; - const initialValue = !open ? this.height : 0; - animation.setValue(initialValue); - } - - componentWillReceiveProps(nextProps) { - const { animation } = this.state; - const { open } = this.props; - - if (this.first) { - this.first = false; - if (!open) { - animation.setValue(0); - return; - } - } - if (this.open === nextProps.open) { - return; - } - this.open = nextProps.open; - const initialValue = !nextProps.open ? this.height : 0; - const finalValue = !nextProps.open ? 0 : this.height; - - animation.setValue(initialValue); - Animated.timing( - animation, - { - toValue: finalValue, - duration: 150, - useNativeDriver: true - } - ).start(); - } - - set _height(h) { - this.height = h || this.height; - } - - render() { - const { animation } = this.state; - const { style, children } = this.props; - - return ( - - this._height = nativeEvent.layout.height} style={{ position: !this.first ? 'relative' : 'absolute' }}> - {children} - - - ); - } -} diff --git a/app/animations/fade.js b/app/animations/fade.js deleted file mode 100644 index 513625816..000000000 --- a/app/animations/fade.js +++ /dev/null @@ -1,63 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { Animated, Text } from 'react-native'; - -export default class Fade extends React.Component { - static propTypes = { - visible: PropTypes.bool.isRequired, - style: Animated.View.propTypes.style, - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node - ]) - } - - constructor(props) { - super(props); - const { visible } = this.props; - this.state = { - visible - }; - this._visibility = new Animated.Value(visible ? 1 : 0); - } - - componentWillReceiveProps(nextProps) { - if (nextProps.visible) { - this.setState({ visible: true }); - } - Animated.timing(this._visibility, { - toValue: nextProps.visible ? 1 : 0, - duration: 300, - useNativeDriver: true - }).start(() => { - this.setState({ visible: nextProps.visible }); - }); - } - - render() { - const { visible } = this.state; - const { style, children, ...rest } = this.props; - - const containerStyle = { - opacity: this._visibility.interpolate({ - inputRange: [0, 1], - outputRange: [0, 1] - }), - transform: [ - { - scale: this._visibility.interpolate({ - inputRange: [0, 1], - outputRange: [1.1, 1] - }) - } - ] - }; - - const combinedStyle = [containerStyle, style]; - return ( - - {visible ? children : null} - - ); - } -} diff --git a/app/index.js b/app/index.js index 96da474c0..bdc378aec 100644 --- a/app/index.js +++ b/app/index.js @@ -19,6 +19,7 @@ import { defaultHeader, onNavigationStateChange } from './utils/navigation'; import { loggerConfig, analytics } from './utils/log'; import Toast from './containers/Toast'; import RocketChat from './lib/rocketchat'; +import LayoutAnimation from './utils/layoutAnimation'; useScreens(); @@ -308,12 +309,14 @@ export default class Root extends React.Component { render() { return ( - { - Navigation.setTopLevelNavigator(navigatorRef); - }} - onNavigationStateChange={onNavigationStateChange} - /> + + { + Navigation.setTopLevelNavigator(navigatorRef); + }} + onNavigationStateChange={onNavigationStateChange} + /> + ); } diff --git a/app/share.js b/app/share.js index 2374afe2a..73d6f06b4 100644 --- a/app/share.js +++ b/app/share.js @@ -11,6 +11,7 @@ import sharedStyles from './views/Styles'; import { isNotch, isIOS } from './utils/deviceInfo'; import { defaultHeader, onNavigationStateChange } from './utils/navigation'; import RocketChat from './lib/rocketchat'; +import LayoutAnimation from './utils/layoutAnimation'; const InsideNavigator = createStackNavigator({ ShareListView: { @@ -84,12 +85,14 @@ class Root extends React.Component { onLayout={this.handleLayout} > - { - Navigation.setTopLevelNavigator(navigatorRef); - }} - onNavigationStateChange={onNavigationStateChange} - /> + + { + Navigation.setTopLevelNavigator(navigatorRef); + }} + onNavigationStateChange={onNavigationStateChange} + /> + ); diff --git a/app/utils/layoutAnimation.js b/app/utils/layoutAnimation.js new file mode 100644 index 000000000..a558404cd --- /dev/null +++ b/app/utils/layoutAnimation.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { Transition, Transitioning } from 'react-native-reanimated'; +import PropTypes from 'prop-types'; + +import debounce from './debounce'; +import { isIOS } from './deviceInfo'; +import sharedStyles from '../views/Styles'; + +const transition = ( + + + + + +); + +const TRANSITION_REF = React.createRef(); + +export const animateNextTransition = debounce(() => { + if (isIOS) { + TRANSITION_REF.current.animateNextTransition(); + } +}, 200, true); + +const LayoutAnimation = ({ children }) => { + if (isIOS) { + return ( + + {children} + + ); + } + return children; +}; + +LayoutAnimation.propTypes = { + children: PropTypes.node +}; + +export default LayoutAnimation; diff --git a/app/views/LoginView.js b/app/views/LoginView.js index 435d173ff..87c9cf34f 100644 --- a/app/views/LoginView.js +++ b/app/views/LoginView.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - Keyboard, Text, ScrollView, View, StyleSheet, Alert, LayoutAnimation + Keyboard, Text, ScrollView, View, StyleSheet, Alert } from 'react-native'; import { connect } from 'react-redux'; import { SafeAreaView } from 'react-navigation'; @@ -18,6 +18,7 @@ import { loginRequest as loginRequestAction } from '../actions/login'; import { LegalButton } from '../containers/HeaderButton'; import StatusBar from '../containers/StatusBar'; import { COLOR_PRIMARY } from '../constants/colors'; +import { animateNextTransition } from '../utils/layoutAnimation'; const styles = StyleSheet.create({ bottomContainer: { @@ -84,7 +85,7 @@ class LoginView extends React.Component { this.setTitle(nextProps.Site_Name); } else if (nextProps.failure && !equal(error, nextProps.error)) { if (nextProps.error && nextProps.error.error === 'totp-required') { - LayoutAnimation.easeInEaseOut(); + animateNextTransition(); this.setState({ showTOTP: true }); return; } diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js index 58755a65e..92759700c 100644 --- a/app/views/NewServerView.js +++ b/app/views/NewServerView.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - Text, ScrollView, Keyboard, Image, StyleSheet, TouchableOpacity, View, Alert, LayoutAnimation + Text, ScrollView, Keyboard, Image, StyleSheet, TouchableOpacity, View, Alert } from 'react-native'; import { connect } from 'react-redux'; import { SafeAreaView } from 'react-navigation'; @@ -23,6 +23,7 @@ import { CustomIcon } from '../lib/Icons'; import StatusBar from '../containers/StatusBar'; import { COLOR_PRIMARY } from '../constants/colors'; import log from '../utils/log'; +import { animateNextTransition } from '../utils/layoutAnimation'; const styles = StyleSheet.create({ image: { @@ -195,7 +196,7 @@ class NewServerView extends React.Component { uriToPath = uri => uri.replace('file://', ''); saveCertificate = (certificate) => { - LayoutAnimation.easeInEaseOut(); + animateNextTransition(); this.setState({ certificate }); } diff --git a/app/views/RoomView/List.js b/app/views/RoomView/List.js index 3ed35e2a0..4bae8096a 100644 --- a/app/views/RoomView/List.js +++ b/app/views/RoomView/List.js @@ -1,6 +1,6 @@ import React from 'react'; import { - ActivityIndicator, FlatList, InteractionManager, LayoutAnimation + ActivityIndicator, FlatList, InteractionManager } from 'react-native'; import PropTypes from 'prop-types'; import debounce from 'lodash/debounce'; @@ -15,6 +15,7 @@ import RocketChat from '../../lib/rocketchat'; import log from '../../utils/log'; import EmptyRoom from './EmptyRoom'; import { isIOS } from '../../utils/deviceInfo'; +import { animateNextTransition } from '../../utils/layoutAnimation'; export class List extends React.Component { static propTypes = { @@ -83,7 +84,7 @@ export class List extends React.Component { } const messages = orderBy(data, ['ts'], ['desc']); if (this.mounted) { - LayoutAnimation.easeInEaseOut(); + animateNextTransition(); this.setState({ messages }); } else { this.state.messages = messages; @@ -186,11 +187,11 @@ export class List extends React.Component { style={styles.list} inverted removeClippedSubviews={isIOS} - initialNumToRender={7} + // initialNumToRender={7} onEndReached={this.onEndReached} - onEndReachedThreshold={5} - maxToRenderPerBatch={5} - windowSize={10} + // onEndReachedThreshold={5} + // maxToRenderPerBatch={5} + // windowSize={10} ListFooterComponent={this.renderFooter} {...scrollPersistTaps} /> diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 218a7e50f..05dc9677a 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -1,8 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - Text, View, InteractionManager, LayoutAnimation -} from 'react-native'; +import { Text, View, InteractionManager } from 'react-native'; import { connect } from 'react-redux'; import { RectButton } from 'react-native-gesture-handler'; import { SafeAreaView } from 'react-navigation'; @@ -484,15 +482,11 @@ class RoomView extends React.Component { if (!this.mounted) { return; } - if (isIOS && this.beginAnimating) { - LayoutAnimation.easeInEaseOut(); - } this.setState(...args); } sendMessage = (message, tmid) => { const { user } = this.props; - LayoutAnimation.easeInEaseOut(); RocketChat.sendMessage(this.rid, message, this.tmid || tmid, user).then(() => { this.setLastOpen(null); }); diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 1cda64fe6..94783eeea 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -8,7 +8,6 @@ import { Text, ScrollView, Keyboard, - LayoutAnimation, Dimensions } from 'react-native'; import { connect } from 'react-redux'; @@ -43,6 +42,7 @@ import { import StatusBar from '../../containers/StatusBar'; import ListHeader from './ListHeader'; import { selectServerRequest as selectServerRequestAction } from '../../actions/server'; +import { animateNextTransition } from '../../utils/layoutAnimation'; const SCROLL_OFFSET = 56; @@ -290,8 +290,8 @@ class RoomsListView extends React.Component { // eslint-disable-next-line react/sort-comp internalSetState = (...args) => { const { navigation } = this.props; - if (isIOS && navigation.isFocused()) { - LayoutAnimation.easeInEaseOut(); + if (navigation.isFocused()) { + animateNextTransition(); } this.setState(...args); }; diff --git a/app/views/SelectedUsersView.js b/app/views/SelectedUsersView.js index 3b39c13d5..b9209f7d7 100644 --- a/app/views/SelectedUsersView.js +++ b/app/views/SelectedUsersView.js @@ -1,8 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - View, StyleSheet, FlatList, LayoutAnimation -} from 'react-native'; +import { View, StyleSheet, FlatList } from 'react-native'; import { connect } from 'react-redux'; import { SafeAreaView } from 'react-navigation'; import equal from 'deep-equal'; @@ -25,6 +23,7 @@ import sharedStyles from './Styles'; import { Item, CustomHeaderButtons } from '../containers/HeaderButton'; import StatusBar from '../containers/StatusBar'; import { COLOR_WHITE } from '../constants/colors'; +import { animateNextTransition } from '../utils/layoutAnimation'; const styles = StyleSheet.create({ safeAreaView: { @@ -169,7 +168,7 @@ class SelectedUsersView extends React.Component { toggleUser = (user) => { const { addUser, removeUser } = this.props; - LayoutAnimation.easeInEaseOut(); + animateNextTransition(); if (!this.isChecked(user.name)) { addUser(user); } else { diff --git a/app/views/ShareListView/Header/Header.ios.js b/app/views/ShareListView/Header/Header.ios.js index fabe3c499..d67348818 100644 --- a/app/views/ShareListView/Header/Header.ios.js +++ b/app/views/ShareListView/Header/Header.ios.js @@ -1,8 +1,6 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import { - Keyboard, LayoutAnimation, View, StyleSheet -} from 'react-native'; +import { Keyboard, View, StyleSheet } from 'react-native'; import ShareExtension from 'rn-extensions-share'; import SearchBox from '../../../containers/SearchBox'; @@ -10,6 +8,7 @@ import { CloseShareExtensionButton } from '../../../containers/HeaderButton'; import { HEADER_BACKGROUND } from '../../../constants/colors'; import sharedStyles from '../../Styles'; +import { animateNextTransition } from '../../../utils/layoutAnimation'; const styles = StyleSheet.create({ container: { @@ -33,12 +32,12 @@ const Header = React.memo(({ Keyboard.dismiss(); onChangeText(''); cancelSearch(); - LayoutAnimation.easeInEaseOut(); + animateNextTransition(); }; const onFocus = () => { initSearch(); - LayoutAnimation.easeInEaseOut(); + animateNextTransition(); }; return ( diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js index b6ce99b3f..446da3580 100644 --- a/app/views/ShareListView/index.js +++ b/app/views/ShareListView/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - View, Text, LayoutAnimation, FlatList, ActivityIndicator, Keyboard, BackHandler + View, Text, FlatList, ActivityIndicator, Keyboard, BackHandler } from 'react-native'; import { SafeAreaView } from 'react-navigation'; import ShareExtension from 'rn-extensions-share'; @@ -25,6 +25,7 @@ import ShareListHeader from './Header'; import styles from './styles'; import StatusBar from '../../containers/StatusBar'; +import { animateNextTransition } from '../../utils/layoutAnimation'; const LIMIT = 50; const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); @@ -174,8 +175,8 @@ class ShareListView extends React.Component { // eslint-disable-next-line react/sort-comp internalSetState = (...args) => { const { navigation } = this.props; - if (isIOS && navigation.isFocused()) { - LayoutAnimation.easeInEaseOut(); + if (navigation.isFocused()) { + animateNextTransition(); } this.setState(...args); } diff --git a/app/views/SidebarView/index.js b/app/views/SidebarView/index.js index 099702f2a..1341e284f 100644 --- a/app/views/SidebarView/index.js +++ b/app/views/SidebarView/index.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { - ScrollView, Text, View, FlatList, LayoutAnimation, SafeAreaView + ScrollView, Text, View, FlatList, SafeAreaView } from 'react-native'; import { connect } from 'react-redux'; import equal from 'deep-equal'; @@ -20,6 +20,7 @@ import styles from './styles'; import SidebarItem from './SidebarItem'; import { COLOR_TEXT } from '../../constants/colors'; import database from '../../lib/database'; +import { animateNextTransition } from '../../utils/layoutAnimation'; const keyExtractor = item => item.id; @@ -147,7 +148,7 @@ class Sidebar extends Component { } toggleStatus = () => { - LayoutAnimation.easeInEaseOut(); + animateNextTransition(); this.setState(prevState => ({ showStatus: !prevState.showStatus })); } diff --git a/app/views/Styles.js b/app/views/Styles.js index 041f9ea05..4861380af 100644 --- a/app/views/Styles.js +++ b/app/views/Styles.js @@ -5,6 +5,9 @@ import { } from '../constants/colors'; export default StyleSheet.create({ + root: { + flex: 1 + }, container: { backgroundColor: 'white', flex: 1