From 13e7a3b59327eb3f107541d5c622fe1239eea7f1 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Fri, 17 Apr 2020 15:51:28 +0200 Subject: [PATCH] Play tab switching animations --- src/components/Custom/AnimatedFocusView.js | 54 +++++++++++++++++++ src/components/Tabbar/CustomTabBar.js | 35 ++++++++----- src/components/Tabbar/TabIcon.js | 4 +- src/screens/HomeScreen.js | 25 ++++++--- src/screens/Planning/PlanningScreen.js | 60 ++++++++++++---------- src/screens/Proximo/ProximoMainScreen.js | 20 +++++--- src/screens/Proxiwash/ProxiwashScreen.js | 8 ++- src/screens/Websites/PlanexScreen.js | 7 ++- 8 files changed, 156 insertions(+), 57 deletions(-) create mode 100644 src/components/Custom/AnimatedFocusView.js diff --git a/src/components/Custom/AnimatedFocusView.js b/src/components/Custom/AnimatedFocusView.js new file mode 100644 index 0000000..4a52a90 --- /dev/null +++ b/src/components/Custom/AnimatedFocusView.js @@ -0,0 +1,54 @@ +// @flow + +import * as React from 'react'; +import * as Animatable from "react-native-animatable"; +import {CommonActions} from "@react-navigation/native"; + +type Props = { + navigation: Object, + route: Object, + children: React$Node +} + +export default class AnimatedFocusView extends React.Component { + + ref: Object; + + constructor() { + super(); + this.ref = React.createRef(); + } + + componentDidMount() { + this.props.navigation.addListener('focus', this.onScreenFocus); + } + + onScreenFocus = () => { + if (this.props.route.params !== undefined) { + if (this.props.route.params.animationDir && this.ref.current) { + if (this.props.route.params.animationDir === "right") + this.ref.current.fadeInRight(300); + else + this.ref.current.fadeInLeft(300); + // reset params to prevent infinite loop + this.props.navigation.dispatch(CommonActions.setParams({animationDir: null})); + } + } + + }; + + render() { + return ( + + {this.props.children} + + ); + } +} diff --git a/src/components/Tabbar/CustomTabBar.js b/src/components/Tabbar/CustomTabBar.js index c3829b4..1f5bf3a 100644 --- a/src/components/Tabbar/CustomTabBar.js +++ b/src/components/Tabbar/CustomTabBar.js @@ -18,6 +18,25 @@ const TAB_BAR_HEIGHT = 48; */ class CustomTabBar extends React.Component { + shouldComponentUpdate(nextProps: Props): boolean { + return (nextProps.theme.dark !== this.props.theme.dark) + || (nextProps.state.index !== this.props.state.index); + } + + onItemPress(route: Object, currentIndex: number, destIndex: number) { + const event = this.props.navigation.emit({ + type: 'tabPress', + target: route.key, + canPreventDefault: true, + }); + if (currentIndex !== destIndex && !event.defaultPrevented) { + this.props.navigation.navigate(route.name, { + screen: 'index', + params: {animationDir: currentIndex < destIndex ? "right" : "left"} + }); + } + } + render() { const state = this.props.state; const descriptors = this.props.descriptors; @@ -38,17 +57,7 @@ class CustomTabBar extends React.Component { const isFocused = state.index === index; - const onPress = () => { - const event = navigation.emit({ - type: 'tabPress', - target: route.key, - canPreventDefault: true, - }); - - if (!isFocused && !event.defaultPrevented) { - navigation.navigate(route.name); - } - }; + const onPress = () => this.onItemPress(route, state.index, index); const onLongPress = () => { navigation.emit({ @@ -66,7 +75,9 @@ class CustomTabBar extends React.Component { icon={options.tabBarIcon(iconData)} color={color} label={label} - focused={isFocused}/> + focused={isFocused} + extraData={state.index > index} + /> } else return { shouldComponentUpdate(nextProps: Props): boolean { return (nextProps.focused !== this.props.focused) - || (nextProps.theme.dark !== this.props.theme.dark); + || (nextProps.theme.dark !== this.props.theme.dark) + || (nextProps.extraData !== this.props.extraData); } render(): React$Node { diff --git a/src/screens/HomeScreen.js b/src/screens/HomeScreen.js index 2277908..3c89d67 100644 --- a/src/screens/HomeScreen.js +++ b/src/screens/HomeScreen.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react'; -import {Animated, FlatList, View} from 'react-native'; +import {Animated, FlatList} from 'react-native'; import i18n from "i18n-js"; import DashboardItem from "../components/Home/EventDashboardItem"; import WebSectionList from "../components/Lists/WebSectionList"; @@ -16,6 +16,7 @@ import {CommonActions} from '@react-navigation/native'; import MaterialHeaderButtons, {Item} from "../components/Custom/HeaderButton"; import {AnimatedValue} from "react-native-reanimated"; import AnimatedFAB from "../components/Custom/AnimatedFAB"; +import AnimatedFocusView from "../components/Custom/AnimatedFocusView"; // import DATA from "../dashboard_data.json"; @@ -113,12 +114,22 @@ class HomeScreen extends React.Component { ; }; - onProxiwashClick = () => this.props.navigation.navigate('proxiwash'); + onProxiwashClick = () => { + this.props.navigation.navigate("proxiwash", { + screen: 'index', + params: {animationDir: "right"} // Play tab animation + }); + }; + + onProximoClick = () => { + this.props.navigation.navigate("proximo", { + screen: 'index', + params: {animationDir: "left"} // Play tab animation + }); + }; onTutorInsaClick = () => this.props.navigation.navigate('tutorinsa'); - onProximoClick = () => this.props.navigation.navigate('proximo'); - onMenuClick = () => this.props.navigation.navigate('self-menu'); /** @@ -460,7 +471,9 @@ class HomeScreen extends React.Component { render() { const nav = this.props.navigation; return ( - + { icon="qrcode-scan" onPress={this.openScanner} /> - + ); } } diff --git a/src/screens/Planning/PlanningScreen.js b/src/screens/Planning/PlanningScreen.js index 1f3d85a..2a1d0f1 100644 --- a/src/screens/Planning/PlanningScreen.js +++ b/src/screens/Planning/PlanningScreen.js @@ -14,6 +14,7 @@ import { } from '../../utils/Planning'; import {Avatar, Divider, List} from 'react-native-paper'; import CustomAgenda from "../../components/Custom/CustomAgenda"; +import AnimatedFocusView from "../../components/Custom/AnimatedFocusView"; LocaleConfig.locales['fr'] = { monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'], @@ -26,6 +27,7 @@ LocaleConfig.locales['fr'] = { type Props = { navigation: Object, + route: Object, } type State = { @@ -229,34 +231,38 @@ class PlanningScreen extends React.Component { render() { // console.log("rendering PlanningScreen"); return ( - + > + + ); } } diff --git a/src/screens/Proximo/ProximoMainScreen.js b/src/screens/Proximo/ProximoMainScreen.js index 611aaa3..fd31628 100644 --- a/src/screens/Proximo/ProximoMainScreen.js +++ b/src/screens/Proximo/ProximoMainScreen.js @@ -6,12 +6,14 @@ import i18n from "i18n-js"; import WebSectionList from "../../components/Lists/WebSectionList"; import {List, withTheme} from 'react-native-paper'; import MaterialHeaderButtons, {Item} from "../../components/Custom/HeaderButton"; +import AnimatedFocusView from "../../components/Custom/AnimatedFocusView"; const DATA_URL = "https://etud.insa-toulouse.fr/~proximo/data/stock-v2.json"; const LIST_ITEM_HEIGHT = 84; type Props = { navigation: Object, + route: Object, } type State = { @@ -232,13 +234,17 @@ class ProximoMainScreen extends React.Component { render() { const nav = this.props.navigation; return ( - + + + ); } } diff --git a/src/screens/Proxiwash/ProxiwashScreen.js b/src/screens/Proxiwash/ProxiwashScreen.js index ee31776..5118c62 100644 --- a/src/screens/Proxiwash/ProxiwashScreen.js +++ b/src/screens/Proxiwash/ProxiwashScreen.js @@ -15,6 +15,7 @@ import AprilFoolsManager from "../../managers/AprilFoolsManager"; import MaterialHeaderButtons, {Item} from "../../components/Custom/HeaderButton"; import ProxiwashSectionHeader from "../../components/Lists/ProxiwashSectionHeader"; import {withCollapsible} from "../../utils/withCollapsible"; +import AnimatedFocusView from "../../components/Custom/AnimatedFocusView"; const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/washinsa/washinsa.json"; @@ -25,6 +26,7 @@ const LIST_ITEM_HEIGHT = 64; type Props = { navigation: Object, + route: Object, theme: Object, collapsibleStack: Object, } @@ -420,7 +422,9 @@ class ProxiwashScreen extends React.Component { const nav = this.props.navigation; const {containerPaddingTop} = this.props.collapsibleStack; return ( - + { autoRefreshTime={REFRESH_TIME} refreshOnFocus={true} updateData={this.state.machinesWatched.length}/> - + ); } } diff --git a/src/screens/Websites/PlanexScreen.js b/src/screens/Websites/PlanexScreen.js index 5ec9e7e..75e5e4a 100644 --- a/src/screens/Websites/PlanexScreen.js +++ b/src/screens/Websites/PlanexScreen.js @@ -14,6 +14,7 @@ import DateManager from "../../managers/DateManager"; import AnimatedBottomBar from "../../components/Custom/AnimatedBottomBar"; import {CommonActions} from "@react-navigation/native"; import ErrorView from "../../components/Custom/ErrorView"; +import AnimatedFocusView from "../../components/Custom/AnimatedFocusView"; type Props = { navigation: Object, @@ -299,7 +300,9 @@ class PlanexScreen extends React.Component { render() { const {containerPaddingTop} = this.props.collapsibleStack; return ( - + { onPress={this.sendMessage} seekAttention={this.state.currentGroup.id === -1} /> - + ); } }