Skip to content

Commit

Permalink
Fix junky animations for AnimatedStep
Browse files Browse the repository at this point in the history
  • Loading branch information
blazejkustra committed Jan 7, 2025
1 parent 38be249 commit 2984009
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/components/AnimatedStep/AnimatedStepContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type StepContext = {
currentStep: string;
previousStep: string | null;
setStep: (newStep: string, direction: AnimationDirection) => void;
renderStep: (stepName: string) => React.ReactNode;
renderStep: () => React.ReactNode;
currentScreenAnimatedStyle: StyleProp<ViewStyle>;
previousScreenAnimatedStyle: StyleProp<ViewStyle>;
};
Expand Down
22 changes: 17 additions & 5 deletions src/components/AnimatedStep/AnimatedStepProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {ReactNode} from 'react';
import React, {useCallback, useMemo, useState} from 'react';
import {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
Expand All @@ -12,10 +13,10 @@ const ANIMATED_SCREEN_TRANSITION = 400;

type AnimatedStepProviderProps = ChildrenProps & {
initialStep: string;
renderStep: (name: string) => React.ReactNode;
steps: Record<string, ReactNode>;
};

function AnimatedStepProvider({children, renderStep, initialStep}: AnimatedStepProviderProps): React.ReactNode {
function AnimatedStepProvider({children, steps, initialStep}: AnimatedStepProviderProps): React.ReactNode {
const [animationDirection, setAnimationDirection] = useState<AnimationDirection>(CONST.ANIMATION_DIRECTION.IN);
const [currentStep, setCurrentStep] = useState<string>(initialStep);
const [previousStep, setPreviousStep] = useState<string | null>(null);
Expand All @@ -30,6 +31,10 @@ function AnimatedStepProvider({children, renderStep, initialStep}: AnimatedStepP

const setStep = useCallback(
(newStep: string, direction: AnimationDirection) => {
if (currentStep === newStep || !!previousStep) {
return;
}

setAnimationDirection(direction);
setPreviousStep(currentStep);
setCurrentStep(newStep);
Expand All @@ -44,7 +49,7 @@ function AnimatedStepProvider({children, renderStep, initialStep}: AnimatedStepP
prevTranslateX.set(0);
prevTranslateX.set(withTiming(previousStepPosition, {duration: ANIMATED_SCREEN_TRANSITION, easing: Easing.inOut(Easing.cubic)}));
},
[currentStep, currentTranslateX, prevTranslateX, isSmallScreenWidth, windowWidth],
[currentStep, previousStep, isSmallScreenWidth, windowWidth, currentTranslateX, prevTranslateX],
);

const currentScreenAnimatedStyle = useAnimatedStyle(() => ({
Expand All @@ -63,9 +68,16 @@ function AnimatedStepProvider({children, renderStep, initialStep}: AnimatedStepP
setStep,
currentScreenAnimatedStyle,
previousScreenAnimatedStyle,
renderStep,
renderStep: () => {
return (
<>
{steps[currentStep]}
{previousStep && steps[previousStep]}
</>
);
},
}),
[currentStep, previousStep, setStep, currentScreenAnimatedStyle, previousScreenAnimatedStyle, renderStep],
[currentStep, previousStep, setStep, currentScreenAnimatedStyle, previousScreenAnimatedStyle, steps],
);

return <AnimatedStepContext.Provider value={contextValue}>{children}</AnimatedStepContext.Provider>;
Expand Down
5 changes: 1 addition & 4 deletions src/components/AnimatedStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ function AnimatedStep({stepName, title = '', stepCounter, onBackButtonPress, chi
}, [onEntryTransitionEnd, previousStep]);

return (
<Animated.View
style={[styles.animatedStep, stepName === previousStep ? previousScreenAnimatedStyle : currentScreenAnimatedStyle]}
key={stepName}
>
<Animated.View style={[styles.animatedStep, stepName === previousStep ? previousScreenAnimatedStyle : currentScreenAnimatedStyle]}>
<ScreenWrapper
shouldShowOfflineIndicator={false}
shouldEnableKeyboardAvoidingView={shouldEnableKeyboardAvoidingView}
Expand Down
10 changes: 9 additions & 1 deletion src/pages/settings/Security/TwoFactorAuth/Steps/GetCode.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useRef} from 'react';
import React, {useEffect, useRef} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import AnimatedStep from '@components/AnimatedStep';
Expand All @@ -23,6 +23,14 @@ function GetCode() {

const {setStep} = useTwoFactorAuthContext();

useEffect(() => {
if (account?.requiresTwoFactorAuth) {
return;
}

setStep(CONST.TWO_FACTOR_AUTH_STEPS.DISABLED);
}, [account?.requiresTwoFactorAuth, setStep]);

return (
<AnimatedStep
stepName={CONST.TWO_FACTOR_AUTH_STEPS.GETCODE}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import React, {forwardRef, useCallback, useImperativeHandle, useRef, useState} from 'react';
import type {ForwardedRef} from 'react';
import {useOnyx} from 'react-native-onyx';
import type {AutoCompleteVariant, MagicCodeInputHandle} from '@components/MagicCodeInput';
import MagicCodeInput from '@components/MagicCodeInput';
import useLocalize from '@hooks/useLocalize';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {BaseTwoFactorAuthFormRef} from './types';

Expand All @@ -27,7 +25,6 @@ function BaseTwoFactorAuthForm({autoComplete, validateInsteadOfDisable}: BaseTwo
const [twoFactorAuthCode, setTwoFactorAuthCode] = useState('');
const inputRef = useRef<MagicCodeInputHandle | null>(null);
const shouldClearData = account?.needsTwoFactorAuthSetup ?? false;
const {setStep} = useTwoFactorAuthContext();

/**
* Handle text input and clear formError upon text change
Expand All @@ -44,13 +41,6 @@ function BaseTwoFactorAuthForm({autoComplete, validateInsteadOfDisable}: BaseTwo
[account?.errors],
);

useEffect(() => {
if (!account || account?.requiresTwoFactorAuth) {
return;
}

setStep(CONST.TWO_FACTOR_AUTH_STEPS.DISABLED);
}, [account, setStep]);
/**
* Check that all the form fields are valid, then trigger the submit callback
*/
Expand Down
50 changes: 25 additions & 25 deletions src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import type {ReactNode} from 'react';
import React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import AnimatedStepProvider from '@components/AnimatedStep/AnimatedStepProvider';
import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper';
Expand Down Expand Up @@ -26,29 +27,28 @@ function TwoFactorAuthPage({route}: TwoFactorAuthPageProps) {
const backTo = route.params?.backTo ?? '';
const forwardTo = route.params?.forwardTo ?? '';

const renderStep = (name: string) => {
switch (name) {
case CONST.TWO_FACTOR_AUTH_STEPS.CODES:
return <CodesStep backTo={backTo} />;
case CONST.TWO_FACTOR_AUTH_STEPS.VERIFY:
return <VerifyStep />;
case CONST.TWO_FACTOR_AUTH_STEPS.SUCCESS:
return (
<SuccessStep
backTo={backTo}
forwardTo={forwardTo}
/>
);
case CONST.TWO_FACTOR_AUTH_STEPS.ENABLED:
return <EnabledStep />;
case CONST.TWO_FACTOR_AUTH_STEPS.DISABLED:
return <DisabledStep />;
case CONST.TWO_FACTOR_AUTH_STEPS.GETCODE:
return <GetCodeStep />;
default:
return <CodesStep backTo={backTo} />;
}
};
const steps: Record<string, ReactNode> = useMemo(
() => ({
[CONST.TWO_FACTOR_AUTH_STEPS.CODES]: (
<CodesStep
backTo={backTo}
key={CONST.TWO_FACTOR_AUTH_STEPS.CODES}
/>
),
[CONST.TWO_FACTOR_AUTH_STEPS.VERIFY]: <VerifyStep key={CONST.TWO_FACTOR_AUTH_STEPS.VERIFY} />,
[CONST.TWO_FACTOR_AUTH_STEPS.SUCCESS]: (
<SuccessStep
backTo={backTo}
forwardTo={forwardTo}
key={CONST.TWO_FACTOR_AUTH_STEPS.SUCCESS}
/>
),
[CONST.TWO_FACTOR_AUTH_STEPS.ENABLED]: <EnabledStep key={CONST.TWO_FACTOR_AUTH_STEPS.ENABLED} />,
[CONST.TWO_FACTOR_AUTH_STEPS.DISABLED]: <DisabledStep key={CONST.TWO_FACTOR_AUTH_STEPS.DISABLED} />,
[CONST.TWO_FACTOR_AUTH_STEPS.GETCODE]: <GetCodeStep key={CONST.TWO_FACTOR_AUTH_STEPS.GETCODE} />,
}),
[backTo, forwardTo],
);

if (isActingAsDelegate) {
return (
Expand All @@ -65,7 +65,7 @@ function TwoFactorAuthPage({route}: TwoFactorAuthPageProps) {
return (
<AnimatedStepProvider
initialStep={initialStep}
renderStep={renderStep}
steps={steps}
>
<TwoFactorAuthSteps />
</AnimatedStepProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {TwoFactorAuthStep} from '@src/types/onyx/Account';
import TwoFactorAuthContext from './TwoFactorAuthContext';

function TwoFactorAuthSteps() {
const {currentStep, previousStep, renderStep, setStep} = useAnimatedStepContext();
const {renderStep, setStep} = useAnimatedStepContext();

useEffect(() => () => TwoFactorAuthActions.clearTwoFactorAuthData(), []);

Expand All @@ -20,12 +20,7 @@ function TwoFactorAuthSteps() {
[setStep],
);

return (
<TwoFactorAuthContext.Provider value={contextValue}>
{renderStep(currentStep)}
{previousStep && renderStep(previousStep)}
</TwoFactorAuthContext.Provider>
);
return <TwoFactorAuthContext.Provider value={contextValue}>{renderStep()}</TwoFactorAuthContext.Provider>;
}

export default TwoFactorAuthSteps;

0 comments on commit 2984009

Please sign in to comment.