Skip to content

Commit

Permalink
Merge pull request #33761 from shubham1206agra/revert-32577-perf/requ…
Browse files Browse the repository at this point in the history
…est-money-tabs-animation

[CP Staging] Revert "Fix RequestMoney tabs animations"
  • Loading branch information
mountiny authored Dec 29, 2023
2 parents 939632e + 3f2c9c7 commit 23f7ee8
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 69 deletions.
79 changes: 64 additions & 15 deletions src/components/TabSelector/TabSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {MaterialTopTabNavigationHelpers} from '@react-navigation/material-top-tabs/lib/typescript/src/types';
import {TabNavigationState} from '@react-navigation/native';
import React, {useMemo} from 'react';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import type {Animated} from 'react-native';
import {View} from 'react-native';
import * as Expensicons from '@components/Icon/Expensicons';
import {LocaleContextProps} from '@components/LocaleContextProvider';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import tabNavigatorAnimationEnabled from '@libs/Navigation/tabNavigatorAnimationEnabled';
import {RootStackParamList} from '@libs/Navigation/types';
import CONST from '@src/CONST';
import IconAsset from '@src/types/utils/IconAsset';
Expand All @@ -21,6 +22,9 @@ type TabSelectorProps = {

/* Callback fired when tab is pressed */
onTabPress?: (name: string) => void;

/* AnimatedValue for the position of the screen while swiping */
position: Animated.AnimatedInterpolation<number | string>;
};

type IconAndTitle = {
Expand All @@ -45,21 +49,66 @@ function getIconAndTitle(route: string, translate: LocaleContextProps['translate
}
}

function TabSelector({state, navigation, onTabPress}: TabSelectorProps) {
function getOpacity(position: Animated.AnimatedInterpolation<number>, routesLength: number, tabIndex: number, active: boolean, affectedTabs: number[]) {
const activeValue = active ? 1 : 0;
const inactiveValue = active ? 0 : 1;

if (routesLength > 1) {
const inputRange = Array.from({length: routesLength}, (v, i) => i);

return position.interpolate({
inputRange,
outputRange: inputRange.map((i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? activeValue : inactiveValue)),
});
}
return activeValue;
}

function TabSelector({state, navigation, onTabPress = () => {}, position}: TabSelectorProps) {
const {translate} = useLocalize();
const theme = useTheme();
const styles = useThemeStyles();
const defaultAffectedAnimatedTabs = useMemo(() => Array.from({length: state.routes.length}, (v, i) => i), [state.routes.length]);
const [affectedAnimatedTabs, setAffectedAnimatedTabs] = useState(defaultAffectedAnimatedTabs);

const tabs = useMemo(
() =>
state.routes.map((route, index) => {
const isFocused = index === state.index;
const getBackgroundColor = useCallback(
(routesLength: number, tabIndex: number, affectedTabs: number[]) => {
if (routesLength > 1) {
const inputRange = Array.from({length: routesLength}, (v, i) => i);

return position.interpolate({
inputRange,
outputRange: inputRange.map((i) => (affectedTabs.includes(tabIndex) && i === tabIndex ? theme.border : theme.appBG)),
});
}
return theme.border;
},
[theme, position],
);

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);
}, [defaultAffectedAnimatedTabs, state.index]);

return (
<View style={styles.tabSelector}>
{state.routes.map((route, index) => {
const activeOpacity = getOpacity(position, state.routes.length, index, true, affectedAnimatedTabs);
const inactiveOpacity = getOpacity(position, state.routes.length, index, false, affectedAnimatedTabs);
const backgroundColor = getBackgroundColor(state.routes.length, index, affectedAnimatedTabs);
const isActive = index === state.index;
const {icon, title} = getIconAndTitle(route.name, translate);

const onPress = () => {
if (isFocused) {
if (isActive) {
return;
}

setAffectedAnimatedTabs([state.index, index]);

const event = navigation.emit({
type: 'tabPress',
target: route.key,
Expand All @@ -71,7 +120,7 @@ function TabSelector({state, navigation, onTabPress}: TabSelectorProps) {
navigation.navigate({key: route.key, merge: true});
}

onTabPress?.(route.name);
onTabPress(route.name);
};

return (
Expand All @@ -80,15 +129,15 @@ function TabSelector({state, navigation, onTabPress}: TabSelectorProps) {
icon={icon}
title={title}
onPress={onPress}
isFocused={isFocused}
animationEnabled={tabNavigatorAnimationEnabled}
activeOpacity={activeOpacity}
inactiveOpacity={inactiveOpacity}
backgroundColor={backgroundColor}
isActive={isActive}
/>
);
}),
[navigation, onTabPress, state.index, state.routes, translate],
})}
</View>
);

return <View style={styles.tabSelector}>{tabs}</View>;
}

TabSelector.displayName = 'TabSelector';
Expand Down
60 changes: 17 additions & 43 deletions src/components/TabSelector/TabSelectorItem.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React, {useCallback, useEffect, useRef} from 'react';
import React from 'react';
import {Animated, StyleSheet} from 'react-native';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import IconAsset from '@src/types/utils/IconAsset';
import TabIcon from './TabIcon';
import TabLabel from './TabLabel';
Expand All @@ -18,45 +16,21 @@ type TabSelectorItemProps = {
/** Title of the tab */
title?: string;

/** Whether this tab is active */
isFocused?: boolean;

/** Whether animations should be skipped */
animationEnabled?: boolean;
};

function TabSelectorItem({icon, title = '', onPress = () => {}, isFocused = false, animationEnabled = true}: TabSelectorItemProps) {
const focusValueRef = useRef(new Animated.Value(isFocused ? 1 : 0));
const styles = useThemeStyles();
const theme = useTheme();

useEffect(() => {
const focusValue = isFocused ? 1 : 0;
/** Animated background color value for the tab button */
backgroundColor?: string | Animated.AnimatedInterpolation<string>;

if (animationEnabled) {
return Animated.timing(focusValueRef.current, {
toValue: focusValue,
duration: CONST.ANIMATED_TRANSITION,
useNativeDriver: true,
}).start();
}
/** Animated opacity value while the tab is in inactive state */
inactiveOpacity?: number | Animated.AnimatedInterpolation<number>;

focusValueRef.current.setValue(focusValue);
}, [animationEnabled, isFocused]);
/** Animated opacity value while the tab is in active state */
activeOpacity?: number | Animated.AnimatedInterpolation<number>;

const getBackgroundColorStyle = useCallback(
(hovered: boolean) => {
if (hovered && !isFocused) {
return {backgroundColor: theme.highlightBG};
}
return {backgroundColor: focusValueRef.current.interpolate({inputRange: [0, 1], outputRange: [theme.appBG, theme.border]})};
},
[theme, isFocused],
);

const activeOpacityValue = focusValueRef.current;
const inactiveOpacityValue = focusValueRef.current.interpolate({inputRange: [0, 1], outputRange: [1, 0]});
/** Whether this tab is active */
isActive?: boolean;
};

function TabSelectorItem({icon, title = '', onPress = () => {}, backgroundColor = '', activeOpacity = 0, inactiveOpacity = 1, isActive = false}: TabSelectorItemProps) {
const styles = useThemeStyles();
return (
<PressableWithFeedback
accessibilityLabel={title}
Expand All @@ -65,16 +39,16 @@ function TabSelectorItem({icon, title = '', onPress = () => {}, isFocused = fals
onPress={onPress}
>
{({hovered}) => (
<Animated.View style={[styles.tabSelectorButton, StyleSheet.absoluteFill, getBackgroundColorStyle(hovered)]}>
<Animated.View style={[styles.tabSelectorButton, StyleSheet.absoluteFill, styles.tabBackground(hovered, isActive, backgroundColor)]}>
<TabIcon
icon={icon}
activeOpacity={activeOpacityValue}
inactiveOpacity={inactiveOpacityValue}
activeOpacity={styles.tabOpacity(hovered, isActive, activeOpacity, inactiveOpacity).opacity}
inactiveOpacity={styles.tabOpacity(hovered, isActive, inactiveOpacity, activeOpacity).opacity}
/>
<TabLabel
title={title}
activeOpacity={activeOpacityValue}
inactiveOpacity={inactiveOpacityValue}
activeOpacity={styles.tabOpacity(hovered, isActive, activeOpacity, inactiveOpacity).opacity}
inactiveOpacity={styles.tabOpacity(hovered, isActive, inactiveOpacity, activeOpacity).opacity}
/>
</Animated.View>
)}
Expand Down
4 changes: 0 additions & 4 deletions src/libs/Navigation/OnyxTabNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import {OnyxEntry} from 'react-native-onyx/lib/types';
import Tab from '@userActions/Tab';
import ONYXKEYS from '@src/ONYXKEYS';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import tabNavigatorAnimationEnabled from './tabNavigatorAnimationEnabled';

const screenOptions = {animationEnabled: tabNavigatorAnimationEnabled};

type OnyxTabNavigatorOnyxProps = {
selectedTab: OnyxEntry<string>;
Expand Down Expand Up @@ -40,7 +37,6 @@ function OnyxTabNavigator({id, selectedTab = '', children, onTabSelected = () =>
{...rest}
id={id}
initialRouteName={selectedTab}
screenOptions={screenOptions}
backBehavior="initialRoute"
keyboardDismissMode="none"
screenListeners={{
Expand Down

This file was deleted.

3 changes: 0 additions & 3 deletions src/libs/Navigation/tabNavigatorAnimationEnabled/index.ts

This file was deleted.

8 changes: 7 additions & 1 deletion src/pages/iou/MoneyRequestSelectorPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,13 @@ function MoneyRequestSelectorPage(props) {
<OnyxTabNavigator
id={CONST.TAB.RECEIPT_TAB_ID}
selectedTab={props.selectedTab}
tabBar={TabSelector}
tabBar={({state, navigation, position}) => (
<TabSelector
state={state}
navigation={navigation}
position={position}
/>
)}
>
<TopTab.Screen
name={CONST.TAB_REQUEST.MANUAL}
Expand Down
14 changes: 14 additions & 0 deletions src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3705,6 +3705,20 @@ const styles = (theme: ThemeColors) =>
color: isSelected ? theme.text : theme.textSupporting,
lineHeight: 14,
} satisfies TextStyle),

tabBackground: (hovered: boolean, isFocused: boolean, background: string | Animated.AnimatedInterpolation<string>) => ({
backgroundColor: hovered && !isFocused ? theme.highlightBG : background,
}),

tabOpacity: (
hovered: boolean,
isFocused: boolean,
activeOpacityValue: number | Animated.AnimatedInterpolation<number>,
inactiveOpacityValue: number | Animated.AnimatedInterpolation<number>,
) => ({
opacity: hovered && !isFocused ? inactiveOpacityValue : activeOpacityValue,
}),

overscrollSpacer: (backgroundColor: string, height: number) =>
({
backgroundColor,
Expand Down

0 comments on commit 23f7ee8

Please sign in to comment.