diff --git a/src/components/StarterPack/Wizard/ScreenTransition.tsx b/src/components/ScreenTransition.tsx similarity index 51% rename from src/components/StarterPack/Wizard/ScreenTransition.tsx rename to src/components/ScreenTransition.tsx index c02888e1d1..8c4e7e01f5 100644 --- a/src/components/StarterPack/Wizard/ScreenTransition.tsx +++ b/src/components/ScreenTransition.tsx @@ -1,5 +1,6 @@ import {type StyleProp, type ViewStyle} from 'react-native' import Animated, { + Easing, FadeIn, FadeOut, SlideInLeft, @@ -13,17 +14,25 @@ export function ScreenTransition({ direction, style, children, + enabledWeb, }: { direction: 'Backward' | 'Forward' style?: StyleProp children: React.ReactNode + enabledWeb?: boolean }) { - const entering = direction === 'Forward' ? SlideInRight : SlideInLeft + const entering = + direction === 'Forward' + ? SlideInRight.easing(Easing.out(Easing.exp)) + : SlideInLeft.easing(Easing.out(Easing.exp)) + const webEntering = enabledWeb ? FadeIn.duration(90) : undefined + const exiting = FadeOut.duration(90) // Totally vibes based + const webExiting = enabledWeb ? FadeOut.duration(90) : undefined return ( {children} diff --git a/src/screens/Login/ScreenTransition.tsx b/src/screens/Login/ScreenTransition.tsx deleted file mode 100644 index b9e4b2d556..0000000000 --- a/src/screens/Login/ScreenTransition.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import {type StyleProp, type ViewStyle} from 'react-native' -import Animated, {FadeInRight, FadeOutLeft} from 'react-native-reanimated' -import type React from 'react' - -export function ScreenTransition({ - style, - children, -}: { - style?: StyleProp - children: React.ReactNode -}) { - return ( - - {children} - - ) -} diff --git a/src/screens/Login/ScreenTransition.web.tsx b/src/screens/Login/ScreenTransition.web.tsx deleted file mode 100644 index 4583720aa8..0000000000 --- a/src/screens/Login/ScreenTransition.web.tsx +++ /dev/null @@ -1 +0,0 @@ -export {Fragment as ScreenTransition} from 'react' diff --git a/src/screens/Login/index.tsx b/src/screens/Login/index.tsx index 601d766061..9cbbd51216 100644 --- a/src/screens/Login/index.tsx +++ b/src/screens/Login/index.tsx @@ -1,6 +1,6 @@ -import React, {useRef} from 'react' +import {useEffect, useRef, useState} from 'react' import {KeyboardAvoidingView} from 'react-native' -import {LayoutAnimationConfig} from 'react-native-reanimated' +import Animated, {FadeIn, LayoutAnimationConfig} from 'react-native-reanimated' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -15,9 +15,9 @@ import {ForgotPasswordForm} from '#/screens/Login/ForgotPasswordForm' import {LoginForm} from '#/screens/Login/LoginForm' import {PasswordUpdatedForm} from '#/screens/Login/PasswordUpdatedForm' import {SetNewPasswordForm} from '#/screens/Login/SetNewPasswordForm' -import {atoms as a} from '#/alf' +import {atoms as a, native} from '#/alf' +import {ScreenTransition} from '#/components/ScreenTransition' import {ChooseAccountForm} from './ChooseAccountForm' -import {ScreenTransition} from './ScreenTransition' enum Forms { Login, @@ -27,6 +27,14 @@ enum Forms { PasswordUpdated, } +const OrderedForms = [ + Forms.ChooseAccount, + Forms.Login, + Forms.ForgotPassword, + Forms.SetNewPassword, + Forms.PasswordUpdated, +] as const + export const Login = ({onPressBack}: {onPressBack: () => void}) => { const {_} = useLingui() const failedAttemptCountRef = useRef(0) @@ -38,20 +46,23 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { acc => acc.did === requestedAccountSwitchTo, ) - const [error, setError] = React.useState('') - const [serviceUrl, setServiceUrl] = React.useState( + const [error, setError] = useState('') + const [serviceUrl, setServiceUrl] = useState( requestedAccount?.service || DEFAULT_SERVICE, ) - const [initialHandle, setInitialHandle] = React.useState( + const [initialHandle, setInitialHandle] = useState( requestedAccount?.handle || '', ) - const [currentForm, setCurrentForm] = React.useState( + const [currentForm, setCurrentForm] = useState( requestedAccount ? Forms.Login : accounts.length ? Forms.ChooseAccount : Forms.Login, ) + const [screenTransitionDirection, setScreenTransitionDirection] = useState< + 'Forward' | 'Backward' + >('Forward') const { data: serviceDescription, @@ -64,15 +75,18 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { setServiceUrl(account.service) } setInitialHandle(account?.handle || '') - setCurrentForm(Forms.Login) + gotoForm(Forms.Login) } const gotoForm = (form: Forms) => { setError('') + const index = OrderedForms.indexOf(currentForm) + const nextIndex = OrderedForms.indexOf(form) + setScreenTransitionDirection(index < nextIndex ? 'Forward' : 'Backward') setCurrentForm(form) } - React.useEffect(() => { + useEffect(() => { if (serviceError) { setError( _( @@ -89,12 +103,13 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { }, [serviceError, serviceUrl, _]) const onPressForgotPassword = () => { - setCurrentForm(Forms.ForgotPassword) + gotoForm(Forms.ForgotPassword) logEvent('signin:forgotPasswordPressed', {}) } const handlePressBack = () => { onPressBack() + setScreenTransitionDirection('Backward') logEvent('signin:backPressed', { failedAttemptsCount: failedAttemptCountRef.current, }) @@ -106,7 +121,6 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { timeTakenSeconds: Math.round((Date.now() - startTimeRef.current) / 1000), failedAttemptsCount: failedAttemptCountRef.current, }) - setCurrentForm(Forms.Login) } const onAttemptFailed = () => { @@ -187,16 +201,22 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { } return ( - - - - {content} - - - + + + + + + {content} + + + + + ) } diff --git a/src/screens/Signup/StepCaptcha/index.tsx b/src/screens/Signup/StepCaptcha/index.tsx index 8ea893c4aa..e1d7afd01b 100644 --- a/src/screens/Signup/StepCaptcha/index.tsx +++ b/src/screens/Signup/StepCaptcha/index.tsx @@ -8,7 +8,6 @@ import {nanoid} from 'nanoid/non-secure' import {createFullHandle} from '#/lib/strings/handles' import {logger} from '#/logger' import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection' -import {ScreenTransition} from '#/screens/Login/ScreenTransition' import {useSignupContext} from '#/screens/Signup/state' import {CaptchaWebView} from '#/screens/Signup/StepCaptcha/CaptchaWebView' import {atoms as a, useTheme} from '#/alf' @@ -143,7 +142,7 @@ function StepCaptchaInner({ }, [dispatch, state.handle]) return ( - + <> - + ) } diff --git a/src/screens/Signup/StepHandle/index.tsx b/src/screens/Signup/StepHandle/index.tsx index 64333933cc..696c4d468b 100644 --- a/src/screens/Signup/StepHandle/index.tsx +++ b/src/screens/Signup/StepHandle/index.tsx @@ -19,7 +19,6 @@ import { checkHandleAvailability, useHandleAvailabilityQuery, } from '#/state/queries/handle-availability' -import {ScreenTransition} from '#/screens/Login/ScreenTransition' import {useSignupContext} from '#/screens/Signup/state' import {atoms as a, native, useTheme} from '#/alf' import * as TextField from '#/components/forms/TextField' @@ -141,7 +140,7 @@ export function StepHandle() { !validCheck.totalLength return ( - + <> @@ -252,7 +251,7 @@ export function StepHandle() { onNextPress={onNextPress} /> - + ) } diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx index 842ddadc52..e3664c0194 100644 --- a/src/screens/Signup/StepInfo/index.tsx +++ b/src/screens/Signup/StepInfo/index.tsx @@ -7,7 +7,6 @@ import type tldts from 'tldts' import {isEmailMaybeInvalid} from '#/lib/strings/email' import {logger} from '#/logger' -import {ScreenTransition} from '#/screens/Login/ScreenTransition' import {is13, is18, useSignupContext} from '#/screens/Signup/state' import {Policies} from '#/screens/Signup/StepInfo/Policies' import {atoms as a, native} from '#/alf' @@ -147,7 +146,7 @@ export function StepInfo({ } return ( - + <> - + ) } diff --git a/src/screens/Signup/index.tsx b/src/screens/Signup/index.tsx index 807bbff4f9..21f9547d0c 100644 --- a/src/screens/Signup/index.tsx +++ b/src/screens/Signup/index.tsx @@ -23,11 +23,12 @@ import { import {StepCaptcha} from '#/screens/Signup/StepCaptcha' import {StepHandle} from '#/screens/Signup/StepHandle' import {StepInfo} from '#/screens/Signup/StepInfo' -import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {atoms as a, native, useBreakpoints, useTheme} from '#/alf' import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' import {Divider} from '#/components/Divider' import {LinearGradientBackground} from '#/components/LinearGradientBackground' import {InlineLinkText} from '#/components/Link' +import {ScreenTransition} from '#/components/ScreenTransition' import {Text} from '#/components/Typography' import {GCP_PROJECT_ID} from '#/env' import * as bsky from '#/types/bsky' @@ -116,109 +117,120 @@ export function Signup({onPressBack}: {onPressBack: () => void}) { }, []) return ( - - - - {showStarterPackCard && - bsky.dangerousIsType( - starterPack.record, - AppBskyGraphStarterpack.isRecord, - ) ? ( - - - - {starterPack.record.name} - - - {starterPack.feeds?.length ? ( - - You'll follow the suggested users and feeds once you - finish creating your account! - + + + + + {showStarterPackCard && + bsky.dangerousIsType( + starterPack.record, + AppBskyGraphStarterpack.isRecord, + ) ? ( + + + + {starterPack.record.name} + + + {starterPack.feeds?.length ? ( + + You'll follow the suggested users and feeds once you + finish creating your account! + + ) : ( + + You'll follow the suggested users once you finish + creating your account! + + )} + + + + ) : null} + + + + + + + Step {state.activeStep + 1} of{' '} + {state.serviceDescription && + !state.serviceDescription.phoneVerificationRequired + ? '2' + : '3'} + + + + {state.activeStep === SignupStep.INFO ? ( + Your account + ) : state.activeStep === SignupStep.HANDLE ? ( + Choose your username + ) : ( + Complete the challenge + )} + + + + {state.activeStep === SignupStep.INFO ? ( + + ) : state.activeStep === SignupStep.HANDLE ? ( + ) : ( - - You'll follow the suggested users once you finish creating - your account! - + )} - - - - ) : null} - - - - - Step {state.activeStep + 1} of{' '} - {state.serviceDescription && - !state.serviceDescription.phoneVerificationRequired - ? '2' - : '3'} - - - - {state.activeStep === SignupStep.INFO ? ( - Your account - ) : state.activeStep === SignupStep.HANDLE ? ( - Choose your username - ) : ( - Complete the challenge - )} - - - - {state.activeStep === SignupStep.INFO ? ( - - ) : state.activeStep === SignupStep.HANDLE ? ( - - ) : ( - - )} + + + + + + Having trouble?{' '} + + Contact support + + + + + - - - - - - - Having trouble?{' '} - - Contact support - - - - - - + + + ) } diff --git a/src/screens/Signup/state.ts b/src/screens/Signup/state.ts index 29b27e7a24..5bf0466d75 100644 --- a/src/screens/Signup/state.ts +++ b/src/screens/Signup/state.ts @@ -41,6 +41,7 @@ type ErrorField = export type SignupState = { hasPrev: boolean activeStep: SignupStep + screenTransitionDirection: 'Forward' | 'Backward' serviceUrl: string serviceDescription?: ServiceDescription @@ -84,6 +85,7 @@ export type SignupAction = export const initialState: SignupState = { hasPrev: false, activeStep: SignupStep.INFO, + screenTransitionDirection: 'Forward', serviceUrl: DEFAULT_SERVICE, serviceDescription: undefined, @@ -126,7 +128,7 @@ export function reducer(s: SignupState, a: SignupAction): SignupState { switch (a.type) { case 'prev': { if (s.activeStep !== SignupStep.INFO) { - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) + next.screenTransitionDirection = 'Backward' next.activeStep-- next.error = '' next.errorField = undefined @@ -135,7 +137,7 @@ export function reducer(s: SignupState, a: SignupAction): SignupState { } case 'next': { if (s.activeStep !== SignupStep.CAPTCHA) { - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) + next.screenTransitionDirection = 'Forward' next.activeStep++ next.error = '' next.errorField = undefined diff --git a/src/screens/StarterPack/Wizard/StepDetails.tsx b/src/screens/StarterPack/Wizard/StepDetails.tsx index ba18a4b023..00fff95c4e 100644 --- a/src/screens/StarterPack/Wizard/StepDetails.tsx +++ b/src/screens/StarterPack/Wizard/StepDetails.tsx @@ -8,7 +8,7 @@ import {useWizardState} from '#/screens/StarterPack/Wizard/State' import {atoms as a, useTheme} from '#/alf' import * as TextField from '#/components/forms/TextField' import {StarterPack} from '#/components/icons/StarterPack' -import {ScreenTransition} from '#/components/StarterPack/Wizard/ScreenTransition' +import {ScreenTransition} from '#/components/ScreenTransition' import {Text} from '#/components/Typography' export function StepDetails() { @@ -23,7 +23,7 @@ export function StepDetails() { }) return ( - + diff --git a/src/screens/StarterPack/Wizard/StepFeeds.tsx b/src/screens/StarterPack/Wizard/StepFeeds.tsx index c87408e23d..0f16874642 100644 --- a/src/screens/StarterPack/Wizard/StepFeeds.tsx +++ b/src/screens/StarterPack/Wizard/StepFeeds.tsx @@ -17,7 +17,7 @@ import {atoms as a, useTheme} from '#/alf' import {SearchInput} from '#/components/forms/SearchInput' import {useThrottledValue} from '#/components/hooks/useThrottledValue' import {Loader} from '#/components/Loader' -import {ScreenTransition} from '#/components/StarterPack/Wizard/ScreenTransition' +import {ScreenTransition} from '#/components/ScreenTransition' import {WizardFeedCard} from '#/components/StarterPack/Wizard/WizardListCard' import {Text} from '#/components/Typography' @@ -79,7 +79,10 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) { } return ( - + + - - - + + + + - - + + + + + + What's up? + - - What's up? - - - - - - - - - + + + - - - + + + + + + + + ) }