From 9d51886e438b1676706009d91bdcf20e8df5dd58 Mon Sep 17 00:00:00 2001 From: Ansh Date: Thu, 7 Dec 2023 16:53:50 -0800 Subject: [PATCH] Fixes issue with (#2119) * Allow going directly to password input screen when switching accounts and password is required * Revise state handling * Handle logged out states, enable clearing requestedAccount --------- Co-authored-by: Eric Bailey --- src/lib/hooks/useAccountSwitcher.ts | 6 +-- src/state/shell/logged-out.tsx | 72 +++++++++++++++++++++++++---- src/view/com/auth/LoggedOut.tsx | 24 ++++++++-- src/view/com/auth/login/Login.tsx | 28 ++++++++--- 4 files changed, 108 insertions(+), 22 deletions(-) diff --git a/src/lib/hooks/useAccountSwitcher.ts b/src/lib/hooks/useAccountSwitcher.ts index 2f2c110d..74b5674d 100644 --- a/src/lib/hooks/useAccountSwitcher.ts +++ b/src/lib/hooks/useAccountSwitcher.ts @@ -11,7 +11,7 @@ export function useAccountSwitcher() { const {track} = useAnalytics() const {selectAccount, clearCurrentAccount} = useSessionApi() const closeAllActiveElements = useCloseAllActiveElements() - const {setShowLoggedOut} = useLoggedOutViewControls() + const {requestSwitchToAccount} = useLoggedOutViewControls() const onPressSwitchAccount = useCallback( async (account: SessionAccount) => { @@ -34,7 +34,7 @@ export function useAccountSwitcher() { }, 100) } else { closeAllActiveElements() - setShowLoggedOut(true) + requestSwitchToAccount({requestedAccount: account.did}) Toast.show( `Please sign in as @${account.handle}`, 'circle-exclamation', @@ -50,7 +50,7 @@ export function useAccountSwitcher() { clearCurrentAccount, selectAccount, closeAllActiveElements, - setShowLoggedOut, + requestSwitchToAccount, ], ) diff --git a/src/state/shell/logged-out.tsx b/src/state/shell/logged-out.tsx index 19eaee76..a06a6d96 100644 --- a/src/state/shell/logged-out.tsx +++ b/src/state/shell/logged-out.tsx @@ -1,23 +1,77 @@ import React from 'react' -type StateContext = { +type State = { showLoggedOut: boolean + /** + * Account did used to populate the login form when the logged out view is + * shown. + */ + requestedAccountSwitchTo?: string } -const StateContext = React.createContext({ - showLoggedOut: false, -}) -const ControlsContext = React.createContext<{ +type Controls = { + /** + * Show or hide the logged out view. + */ setShowLoggedOut: (show: boolean) => void -}>({ + /** + * Shows the logged out view and drops the user into the login form using the + * requested account. + */ + requestSwitchToAccount: (props: { + /** + * The did of the account to populate the login form with. + */ + requestedAccount?: string + }) => void + /** + * Clears the requested account so that next time the logged out view is + * show, no account is pre-populated. + */ + clearRequestedAccount: () => void +} + +const StateContext = React.createContext({ + showLoggedOut: false, + requestedAccountSwitchTo: undefined, +}) + +const ControlsContext = React.createContext({ setShowLoggedOut: () => {}, + requestSwitchToAccount: () => {}, + clearRequestedAccount: () => {}, }) export function Provider({children}: React.PropsWithChildren<{}>) { - const [showLoggedOut, setShowLoggedOut] = React.useState(false) + const [state, setState] = React.useState({ + showLoggedOut: false, + requestedAccountSwitchTo: undefined, + }) - const state = React.useMemo(() => ({showLoggedOut}), [showLoggedOut]) - const controls = React.useMemo(() => ({setShowLoggedOut}), [setShowLoggedOut]) + const controls = React.useMemo( + () => ({ + setShowLoggedOut(show) { + setState(s => ({ + ...s, + showLoggedOut: show, + })) + }, + requestSwitchToAccount({requestedAccount}) { + setState(s => ({ + ...s, + showLoggedOut: true, + requestedAccountSwitchTo: requestedAccount, + })) + }, + clearRequestedAccount() { + setState(s => ({ + ...s, + requestedAccountSwitchTo: undefined, + })) + }, + }), + [setState], + ) return ( diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx index 030ae68b..fcff4f78 100644 --- a/src/view/com/auth/LoggedOut.tsx +++ b/src/view/com/auth/LoggedOut.tsx @@ -14,6 +14,10 @@ import {useAnalytics} from 'lib/analytics/analytics' import {SplashScreen} from './SplashScreen' import {useSetMinimalShellMode} from '#/state/shell/minimal-mode' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import { + useLoggedOutView, + useLoggedOutViewControls, +} from '#/state/shell/logged-out' enum ScreenState { S_LoginOrCreateAccount, @@ -26,16 +30,27 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) { const pal = usePalette('default') const setMinimalShellMode = useSetMinimalShellMode() const {screen} = useAnalytics() + const {requestedAccountSwitchTo} = useLoggedOutView() const [screenState, setScreenState] = React.useState( - ScreenState.S_LoginOrCreateAccount, + requestedAccountSwitchTo + ? ScreenState.S_Login + : ScreenState.S_LoginOrCreateAccount, ) const {isMobile} = useWebMediaQueries() + const {clearRequestedAccount} = useLoggedOutViewControls() React.useEffect(() => { screen('Login') setMinimalShellMode(true) }, [screen, setMinimalShellMode]) + const onPressDismiss = React.useCallback(() => { + if (onDismiss) { + onDismiss() + } + clearRequestedAccount() + }, [clearRequestedAccount, onDismiss]) + return ( void}) { backgroundColor: pal.text.color, borderRadius: 100, }} - onPress={onDismiss}> + onPress={onPressDismiss}> void}) { ) : undefined} {screenState === ScreenState.S_Login ? ( + onPressBack={() => { setScreenState(ScreenState.S_LoginOrCreateAccount) - } + clearRequestedAccount() + }} /> ) : undefined} {screenState === ScreenState.S_CreateAccount ? ( diff --git a/src/view/com/auth/login/Login.tsx b/src/view/com/auth/login/Login.tsx index 67d0afdf..bc931ac0 100644 --- a/src/view/com/auth/login/Login.tsx +++ b/src/view/com/auth/login/Login.tsx @@ -14,6 +14,7 @@ import {useLingui} from '@lingui/react' import {msg} from '@lingui/macro' import {useSession, SessionAccount} from '#/state/session' import {useServiceQuery} from '#/state/queries/service' +import {useLoggedOutView} from '#/state/shell/logged-out' enum Forms { Login, @@ -24,16 +25,31 @@ enum Forms { } export const Login = ({onPressBack}: {onPressBack: () => void}) => { + const {_} = useLingui() const pal = usePalette('default') + const {accounts} = useSession() const {track} = useAnalytics() - const {_} = useLingui() - const [error, setError] = useState('') - const [serviceUrl, setServiceUrl] = useState(DEFAULT_SERVICE) - const [initialHandle, setInitialHandle] = useState('') - const [currentForm, setCurrentForm] = useState( - accounts.length ? Forms.ChooseAccount : Forms.Login, + const {requestedAccountSwitchTo} = useLoggedOutView() + const requestedAccount = accounts.find( + a => a.did === requestedAccountSwitchTo, ) + + const [error, setError] = useState('') + const [serviceUrl, setServiceUrl] = useState( + requestedAccount?.service || DEFAULT_SERVICE, + ) + const [initialHandle, setInitialHandle] = useState( + requestedAccount?.handle || '', + ) + const [currentForm, setCurrentForm] = useState( + requestedAccount + ? Forms.Login + : accounts.length + ? Forms.ChooseAccount + : Forms.Login, + ) + const { data: serviceDescription, error: serviceError,