From 362a17f5bd2f80c784ed114b4803ea0547412d38 Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Thu, 5 Oct 2023 10:54:27 +0200 Subject: [PATCH 1/8] fix tabs animation --- src/components/TabSelector/TabSelector.js | 31 +++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js index 79cd1a6dd17d..1c14701b6e79 100644 --- a/src/components/TabSelector/TabSelector.js +++ b/src/components/TabSelector/TabSelector.js @@ -1,5 +1,5 @@ import {View} from 'react-native'; -import React from 'react'; +import React, {useReducer, useRef} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; import * as Expensicons from '../Icon/Expensicons'; @@ -53,7 +53,7 @@ const getIconAndTitle = (route, translate) => { } }; -const getOpacity = (position, routesLength, tabIndex, active) => { +const getOpacity = (position, routesLength, tabIndex, active, affectedTabs) => { const activeValue = active ? 1 : 0; const inactiveValue = active ? 0 : 1; @@ -62,19 +62,19 @@ const getOpacity = (position, routesLength, tabIndex, active) => { return position.interpolate({ inputRange, - outputRange: _.map(inputRange, (i) => (i === tabIndex ? activeValue : inactiveValue)), + outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? activeValue : inactiveValue)) }); } return activeValue; }; -const getBackgroundColor = (position, routesLength, tabIndex) => { +const getBackgroundColor = (position, routesLength, tabIndex, affectedTabs) => { if (routesLength > 1) { const inputRange = Array.from({length: routesLength}, (v, i) => i); return position.interpolate({ inputRange, - outputRange: _.map(inputRange, (i) => (i === tabIndex ? themeColors.border : themeColors.appBG)), + outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? themeColors.border : themeColors.appBG)) }); } return themeColors.border; @@ -82,13 +82,27 @@ const getBackgroundColor = (position, routesLength, tabIndex) => { function TabSelector({state, navigation, onTabPress, position}) { const {translate} = useLocalize(); + const [, reRender] = useReducer(x => !x, false); + + const defaultAffectedAnimatedTabs = Array.from({length: state.routes.length}, (v, i) => i); + const affectedAnimatedTabs = useRef(defaultAffectedAnimatedTabs); + + + React.useEffect(() => { + setTimeout(() => { + affectedAnimatedTabs.current = defaultAffectedAnimatedTabs; + // re-render is important to re-define opacity and background base on new affected tabs + reRender(); + }, CONST.ANIMATED_TRANSITION) + }, [state.index]); + return ( {_.map(state.routes, (route, index) => { - const activeOpacity = getOpacity(position, state.routes.length, index, true); - const inactiveOpacity = getOpacity(position, state.routes.length, index, false); - const backgroundColor = getBackgroundColor(position, state.routes.length, index); + const activeOpacity = getOpacity(position, state.routes.length, index, true, affectedAnimatedTabs.current); + const inactiveOpacity = getOpacity(position, state.routes.length, index, false, affectedAnimatedTabs.current); + const backgroundColor = getBackgroundColor(position, state.routes.length, index, affectedAnimatedTabs.current); const isFocused = index === state.index; const {icon, title} = getIconAndTitle(route.name, translate); @@ -97,6 +111,7 @@ function TabSelector({state, navigation, onTabPress, position}) { return; } + affectedAnimatedTabs.current = [state.index, index]; const event = navigation.emit({ type: 'tabPress', target: route.key, From f3d797c71392c96937afb88d074109cf9e9f23b0 Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Tue, 10 Oct 2023 21:25:37 +0200 Subject: [PATCH 2/8] fix money request tabs animation --- src/components/TabSelector/TabSelector.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js index 1c14701b6e79..55d4030d3a45 100644 --- a/src/components/TabSelector/TabSelector.js +++ b/src/components/TabSelector/TabSelector.js @@ -62,7 +62,7 @@ const getOpacity = (position, routesLength, tabIndex, active, affectedTabs) => { return position.interpolate({ inputRange, - outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? activeValue : inactiveValue)) + outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? activeValue : inactiveValue)), }); } return activeValue; @@ -74,7 +74,7 @@ const getBackgroundColor = (position, routesLength, tabIndex, affectedTabs) => { return position.interpolate({ inputRange, - outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? themeColors.border : themeColors.appBG)) + outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? themeColors.border : themeColors.appBG)), }); } return themeColors.border; @@ -82,21 +82,19 @@ const getBackgroundColor = (position, routesLength, tabIndex, affectedTabs) => { function TabSelector({state, navigation, onTabPress, position}) { const {translate} = useLocalize(); - const [, reRender] = useReducer(x => !x, false); + const [, reRender] = useReducer((x) => !x, false); const defaultAffectedAnimatedTabs = Array.from({length: state.routes.length}, (v, i) => i); const affectedAnimatedTabs = useRef(defaultAffectedAnimatedTabs); - React.useEffect(() => { setTimeout(() => { affectedAnimatedTabs.current = defaultAffectedAnimatedTabs; - // re-render is important to re-define opacity and background base on new affected tabs + // re-render is important to re-defining opacity and background base on new affected tabs, to be ready for user swiping. reRender(); - }, CONST.ANIMATED_TRANSITION) + }, CONST.ANIMATED_TRANSITION); }, [state.index]); - return ( {_.map(state.routes, (route, index) => { From 0c1f2e1fa4a81120882082bf3bfaa66c5a0779b1 Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Tue, 10 Oct 2023 21:29:29 +0200 Subject: [PATCH 3/8] fix money request tabs animation --- src/components/TabSelector/TabSelector.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js index 55d4030d3a45..3fd7476dd207 100644 --- a/src/components/TabSelector/TabSelector.js +++ b/src/components/TabSelector/TabSelector.js @@ -93,6 +93,7 @@ function TabSelector({state, navigation, onTabPress, position}) { // re-render is important to re-defining opacity and background base on new affected tabs, to be ready for user swiping. reRender(); }, CONST.ANIMATED_TRANSITION); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.index]); return ( From b5ec1f263fc62447687410d6d3346b9aad02c8ba Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Thu, 12 Oct 2023 19:12:28 +0200 Subject: [PATCH 4/8] fix money request tabs animation --- src/components/TabSelector/TabSelector.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js index 50ffcbc43b69..b535d2ad6075 100644 --- a/src/components/TabSelector/TabSelector.js +++ b/src/components/TabSelector/TabSelector.js @@ -1,5 +1,5 @@ import {View} from 'react-native'; -import React, {useReducer, useRef} from 'react'; +import React, {useState} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; import * as Expensicons from '../Icon/Expensicons'; @@ -82,16 +82,13 @@ const getBackgroundColor = (position, routesLength, tabIndex, affectedTabs) => { function TabSelector({state, navigation, onTabPress, position}) { const {translate} = useLocalize(); - const [, reRender] = useReducer((x) => !x, false); const defaultAffectedAnimatedTabs = Array.from({length: state.routes.length}, (v, i) => i); - const affectedAnimatedTabs = useRef(defaultAffectedAnimatedTabs); + const [affectedAnimatedTabs, setAffectedAnimatedTabs] = useState(defaultAffectedAnimatedTabs); React.useEffect(() => { setTimeout(() => { - affectedAnimatedTabs.current = defaultAffectedAnimatedTabs; - // re-render is important to re-defining opacity and background base on new affected tabs, to be ready for user swiping. - reRender(); + setAffectedAnimatedTabs(defaultAffectedAnimatedTabs); }, CONST.ANIMATED_TRANSITION); // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.index]); @@ -99,9 +96,9 @@ function TabSelector({state, navigation, onTabPress, position}) { return ( {_.map(state.routes, (route, index) => { - const activeOpacity = getOpacity(position, state.routes.length, index, true, affectedAnimatedTabs.current); - const inactiveOpacity = getOpacity(position, state.routes.length, index, false, affectedAnimatedTabs.current); - const backgroundColor = getBackgroundColor(position, state.routes.length, index, affectedAnimatedTabs.current); + const activeOpacity = getOpacity(position, state.routes.length, index, true, affectedAnimatedTabs); + const inactiveOpacity = getOpacity(position, state.routes.length, index, false, affectedAnimatedTabs); + const backgroundColor = getBackgroundColor(position, state.routes.length, index, affectedAnimatedTabs); const isFocused = index === state.index; const {icon, title} = getIconAndTitle(route.name, translate); @@ -110,7 +107,8 @@ function TabSelector({state, navigation, onTabPress, position}) { return; } - affectedAnimatedTabs.current = [state.index, index]; + setAffectedAnimatedTabs([state.index, index]); + const event = navigation.emit({ type: 'tabPress', target: route.key, From 878a8d31f5ae73f3557cfceeb163044e3b6c82ca Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Fri, 13 Oct 2023 17:38:10 +0200 Subject: [PATCH 5/8] fix money request tabs animation --- src/components/TabSelector/TabSelector.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js index b535d2ad6075..7823a8789e7a 100644 --- a/src/components/TabSelector/TabSelector.js +++ b/src/components/TabSelector/TabSelector.js @@ -1,5 +1,5 @@ import {View} from 'react-native'; -import React, {useState} from 'react'; +import React, {useMemo, useState} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; import * as Expensicons from '../Icon/Expensicons'; @@ -83,15 +83,14 @@ const getBackgroundColor = (position, routesLength, tabIndex, affectedTabs) => { function TabSelector({state, navigation, onTabPress, position}) { const {translate} = useLocalize(); - const defaultAffectedAnimatedTabs = Array.from({length: state.routes.length}, (v, i) => i); + const defaultAffectedAnimatedTabs = useMemo(() => Array.from({length: state.routes.length}, (v, i) => i), [state.routes.length]); const [affectedAnimatedTabs, setAffectedAnimatedTabs] = useState(defaultAffectedAnimatedTabs); React.useEffect(() => { setTimeout(() => { setAffectedAnimatedTabs(defaultAffectedAnimatedTabs); }, CONST.ANIMATED_TRANSITION); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.index]); + }, [defaultAffectedAnimatedTabs, state.index]); return ( From 4863dd51ae0cd255d428014967c2600de9b09a29 Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Fri, 13 Oct 2023 17:51:39 +0200 Subject: [PATCH 6/8] fix money request tabs animation --- src/components/TabSelector/TabSelector.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js index 7823a8789e7a..b2d073fcab82 100644 --- a/src/components/TabSelector/TabSelector.js +++ b/src/components/TabSelector/TabSelector.js @@ -62,7 +62,7 @@ const getOpacity = (position, routesLength, tabIndex, active, affectedTabs) => { return position.interpolate({ inputRange, - outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? activeValue : inactiveValue)), + outputRange: _.map(inputRange, (i) => ((affectedTabs.includes(tabIndex) && i === tabIndex) ? activeValue : inactiveValue)), }); } return activeValue; @@ -74,7 +74,7 @@ const getBackgroundColor = (position, routesLength, tabIndex, affectedTabs) => { return position.interpolate({ inputRange, - outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? themeColors.border : themeColors.appBG)), + outputRange: _.map(inputRange, (i) => ((affectedTabs.includes(tabIndex) && i === tabIndex) ? themeColors.border : themeColors.appBG)), }); } return themeColors.border; From 3d8b9a202d78cf42134f41ad349b6f337297e9b2 Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Fri, 13 Oct 2023 17:57:50 +0200 Subject: [PATCH 7/8] prettier --- src/components/TabSelector/TabSelector.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js index b2d073fcab82..7823a8789e7a 100644 --- a/src/components/TabSelector/TabSelector.js +++ b/src/components/TabSelector/TabSelector.js @@ -62,7 +62,7 @@ const getOpacity = (position, routesLength, tabIndex, active, affectedTabs) => { return position.interpolate({ inputRange, - outputRange: _.map(inputRange, (i) => ((affectedTabs.includes(tabIndex) && i === tabIndex) ? activeValue : inactiveValue)), + outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? activeValue : inactiveValue)), }); } return activeValue; @@ -74,7 +74,7 @@ const getBackgroundColor = (position, routesLength, tabIndex, affectedTabs) => { return position.interpolate({ inputRange, - outputRange: _.map(inputRange, (i) => ((affectedTabs.includes(tabIndex) && i === tabIndex) ? themeColors.border : themeColors.appBG)), + outputRange: _.map(inputRange, (i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? themeColors.border : themeColors.appBG)), }); } return themeColors.border; From 5504141a4fd65a51903da4dafc1535d538fba1e1 Mon Sep 17 00:00:00 2001 From: ahmedGaber93 Date: Wed, 18 Oct 2023 13:23:25 +0200 Subject: [PATCH 8/8] add comments --- src/components/TabSelector/TabSelector.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js index 7823a8789e7a..3483ec10f804 100644 --- a/src/components/TabSelector/TabSelector.js +++ b/src/components/TabSelector/TabSelector.js @@ -87,6 +87,7 @@ function TabSelector({state, navigation, onTabPress, position}) { const [affectedAnimatedTabs, setAffectedAnimatedTabs] = useState(defaultAffectedAnimatedTabs); React.useEffect(() => { + // It is required to wait transition end to reset affectedAnimatedTabs because tabs style is still animating during transition. setTimeout(() => { setAffectedAnimatedTabs(defaultAffectedAnimatedTabs); }, CONST.ANIMATED_TRANSITION);