diff --git a/native/account/fullscreen-siwe-panel.react.js b/native/account/fullscreen-siwe-panel.react.js index 348208cf4..f1fce696f 100644 --- a/native/account/fullscreen-siwe-panel.react.js +++ b/native/account/fullscreen-siwe-panel.react.js @@ -1,124 +1,124 @@ // @flow import { useNavigation } from '@react-navigation/native'; import invariant from 'invariant'; import * as React from 'react'; import { ActivityIndicator, View } from 'react-native'; import { setDataLoadedActionType } from 'lib/actions/client-db-store-actions.js'; import type { SIWEResult } from 'lib/types/siwe-types.js'; import { ServerError } from 'lib/utils/errors.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; import { useGetEthereumAccountFromSIWEResult } from './registration/ethereum-utils.js'; import { RegistrationContext } from './registration/registration-context.js'; import { enableNewRegistrationMode } from './registration/registration-types.js'; -import { useSIWEServerCall } from './siwe-hooks.js'; +import { useLegacySIWEServerCall } from './siwe-hooks.js'; import SIWEPanel from './siwe-panel.react.js'; import { AccountDoesNotExistRouteName, RegistrationRouteName, } from '../navigation/route-names.js'; import { UnknownErrorAlertDetails } from '../utils/alert-messages.js'; import Alert from '../utils/alert.js'; type Props = { +goBackToPrompt: () => mixed, +closing: boolean, }; function FullscreenSIWEPanel(props: Props): React.Node { const [loading, setLoading] = React.useState(true); const activity = loading ? : null; const activityContainer = React.useMemo( () => ({ flex: 1, }), [], ); const registrationContext = React.useContext(RegistrationContext); invariant(registrationContext, 'registrationContext should be set'); const { setSkipEthereumLoginOnce } = registrationContext; const getEthereumAccountFromSIWEResult = useGetEthereumAccountFromSIWEResult(); const { navigate } = useNavigation(); const { goBackToPrompt } = props; const onAccountDoesNotExist = React.useCallback( async (result: SIWEResult) => { await getEthereumAccountFromSIWEResult(result); setSkipEthereumLoginOnce(true); goBackToPrompt(); navigate<'Registration'>(RegistrationRouteName, { screen: AccountDoesNotExistRouteName, }); }, [ getEthereumAccountFromSIWEResult, navigate, goBackToPrompt, setSkipEthereumLoginOnce, ], ); - const siweServerCall = useSIWEServerCall(); + const legacySiweServerCall = useLegacySIWEServerCall(); const successRef = React.useRef(false); const dispatch = useDispatch(); const onSuccess = React.useCallback( async (result: SIWEResult) => { successRef.current = true; try { - await siweServerCall({ + await legacySiweServerCall({ ...result, doNotRegister: enableNewRegistrationMode, }); } catch (e) { if ( e instanceof ServerError && e.message === 'account_does_not_exist' ) { await onAccountDoesNotExist(result); return; } Alert.alert( UnknownErrorAlertDetails.title, UnknownErrorAlertDetails.message, [{ text: 'OK', onPress: goBackToPrompt }], { cancelable: false }, ); throw e; } dispatch({ type: setDataLoadedActionType, payload: { dataLoaded: true, }, }); }, - [siweServerCall, dispatch, goBackToPrompt, onAccountDoesNotExist], + [legacySiweServerCall, dispatch, goBackToPrompt, onAccountDoesNotExist], ); const ifBeforeSuccessGoBackToPrompt = React.useCallback(() => { if (!successRef.current) { goBackToPrompt(); } }, [goBackToPrompt]); const { closing } = props; return ( <> {activity} ); } export default FullscreenSIWEPanel; diff --git a/native/account/registration/existing-ethereum-account.react.js b/native/account/registration/existing-ethereum-account.react.js index 63dc2e83b..2050b52d8 100644 --- a/native/account/registration/existing-ethereum-account.react.js +++ b/native/account/registration/existing-ethereum-account.react.js @@ -1,145 +1,145 @@ // @flow import * as React from 'react'; import { Text, View } from 'react-native'; import { setDataLoadedActionType } from 'lib/actions/client-db-store-actions.js'; import { siweAuthActionTypes } from 'lib/actions/siwe-actions.js'; import { useENSName } from 'lib/hooks/ens-cache.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import type { SIWEResult } from 'lib/types/siwe-types.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; import RegistrationButtonContainer from './registration-button-container.react.js'; import RegistrationButton from './registration-button.react.js'; import RegistrationContainer from './registration-container.react.js'; import RegistrationContentContainer from './registration-content-container.react.js'; import type { RegistrationNavigationProp } from './registration-navigator.react.js'; import type { NavigationRoute } from '../../navigation/route-names.js'; import { useSelector } from '../../redux/redux-utils.js'; import { useStyles } from '../../themes/colors.js'; import { UnknownErrorAlertDetails } from '../../utils/alert-messages.js'; import Alert from '../../utils/alert.js'; -import { useSIWEServerCall } from '../siwe-hooks.js'; +import { useLegacySIWEServerCall } from '../siwe-hooks.js'; const siweAuthLoadingStatusSelector = createLoadingStatusSelector(siweAuthActionTypes); export type ExistingEthereumAccountParams = SIWEResult; type Props = { +navigation: RegistrationNavigationProp<'ExistingEthereumAccount'>, +route: NavigationRoute<'ExistingEthereumAccount'>, }; function ExistingEthereumAccount(props: Props): React.Node { - const siweServerCall = useSIWEServerCall(); + const legacySiweServerCall = useLegacySIWEServerCall(); const { params } = props.route; const dispatch = useDispatch(); const onProceedToLogIn = React.useCallback(async () => { try { - await siweServerCall({ ...params, doNotRegister: true }); + await legacySiweServerCall({ ...params, doNotRegister: true }); } catch (e) { Alert.alert( UnknownErrorAlertDetails.title, UnknownErrorAlertDetails.message, [{ text: 'OK' }], { cancelable: false, }, ); throw e; } dispatch({ type: setDataLoadedActionType, payload: { dataLoaded: true, }, }); - }, [siweServerCall, params, dispatch]); + }, [legacySiweServerCall, params, dispatch]); const siweAuthCallLoading = useSelector( state => siweAuthLoadingStatusSelector(state) === 'loading', ); const { address } = params; const walletIdentifier = useENSName(address); const walletIdentifierTitle = walletIdentifier === address ? 'Ethereum wallet' : 'ENS name'; const { goBack } = props.navigation; const styles = useStyles(unboundStyles); return ( Account already exists for wallet You can proceed to log in with this wallet, or go back and use a different wallet. {walletIdentifierTitle} {walletIdentifier} ); } const unboundStyles = { header: { fontSize: 24, color: 'panelForegroundLabel', paddingBottom: 16, }, body: { fontFamily: 'Arial', fontSize: 15, lineHeight: 20, color: 'panelForegroundSecondaryLabel', paddingBottom: 40, }, walletTile: { backgroundColor: 'panelForeground', borderRadius: 8, padding: 24, alignItems: 'center', }, walletIdentifierTitleText: { fontSize: 17, color: 'panelForegroundLabel', textAlign: 'center', }, walletIdentifier: { backgroundColor: 'panelSecondaryForeground', paddingVertical: 8, paddingHorizontal: 24, borderRadius: 56, marginTop: 8, alignItems: 'center', }, walletIdentifierText: { fontSize: 15, color: 'panelForegroundLabel', }, }; export default ExistingEthereumAccount; diff --git a/native/account/registration/registration-server-call.js b/native/account/registration/registration-server-call.js index afc043764..bedfd42da 100644 --- a/native/account/registration/registration-server-call.js +++ b/native/account/registration/registration-server-call.js @@ -1,297 +1,297 @@ // @flow import * as React from 'react'; import { setDataLoadedActionType } from 'lib/actions/client-db-store-actions.js'; import { keyserverRegisterActionTypes, keyserverRegister, useIdentityRegister, identityRegisterActionTypes, } from 'lib/actions/user-actions.js'; import type { LogInStartingPayload } from 'lib/types/account-types.js'; import { useLegacyAshoatKeyserverCall } from 'lib/utils/action-utils.js'; import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; import { usingCommServicesAccessToken } from 'lib/utils/services-utils.js'; import { setURLPrefix } from 'lib/utils/url-utils.js'; import type { RegistrationServerCallInput, UsernameAccountSelection, AvatarData, } from './registration-types.js'; import { useNativeSetUserAvatar, useUploadSelectedMedia, } from '../../avatars/avatar-hooks.js'; import { useSelector } from '../../redux/redux-utils.js'; import { nativeLogInExtraInfoSelector } from '../../selectors/account-selectors.js'; import { AppOutOfDateAlertDetails, UsernameReservedAlertDetails, UsernameTakenAlertDetails, UnknownErrorAlertDetails, } from '../../utils/alert-messages.js'; import Alert from '../../utils/alert.js'; import { setNativeCredentials } from '../native-credentials.js'; -import { useSIWEServerCall } from '../siwe-hooks.js'; +import { useLegacySIWEServerCall } from '../siwe-hooks.js'; // We can't just do everything in one async callback, since the server calls // would get bound to Redux state from before the registration. The registration // flow has multiple steps where critical Redux state is changed, where // subsequent steps depend on accessing the updated Redux state. // To address this, we break the registration process up into multiple steps. // When each step completes we update the currentStep state, and we have Redux // selectors that trigger useEffects for subsequent steps when relevant data // starts to appear in Redux. type CurrentStep = | { +step: 'inactive' } | { +step: 'waiting_for_registration_call', +avatarData: ?AvatarData, +resolve: () => void, +reject: Error => void, }; const inactiveStep = { step: 'inactive' }; function useRegistrationServerCall(): RegistrationServerCallInput => Promise { const [currentStep, setCurrentStep] = React.useState(inactiveStep); // STEP 1: ACCOUNT REGISTRATION const logInExtraInfo = useSelector(nativeLogInExtraInfoSelector); const dispatchActionPromise = useDispatchActionPromise(); const callKeyserverRegister = useLegacyAshoatKeyserverCall(keyserverRegister); const callIdentityRegister = useIdentityRegister(); const identityRegisterUsernameAccount = React.useCallback( async (accountSelection: UsernameAccountSelection) => { const identityRegisterPromise = (async () => { try { const result = await callIdentityRegister( accountSelection.username, accountSelection.password, ); await setNativeCredentials({ username: accountSelection.username, password: accountSelection.password, }); return result; } catch (e) { if (e.message === 'username reserved') { Alert.alert( UsernameReservedAlertDetails.title, UsernameReservedAlertDetails.message, ); } else if (e.message === 'username already exists') { Alert.alert( UsernameTakenAlertDetails.title, UsernameTakenAlertDetails.message, ); } else if (e.message === 'Unsupported version') { Alert.alert( AppOutOfDateAlertDetails.title, AppOutOfDateAlertDetails.message, ); } else { Alert.alert( UnknownErrorAlertDetails.title, UnknownErrorAlertDetails.message, ); } throw e; } })(); void dispatchActionPromise( identityRegisterActionTypes, identityRegisterPromise, ); await identityRegisterPromise; }, [callIdentityRegister, dispatchActionPromise], ); const keyserverRegisterUsernameAccount = React.useCallback( async ( accountSelection: UsernameAccountSelection, keyserverURL: string, ) => { const extraInfo = await logInExtraInfo(); const keyserverRegisterPromise = (async () => { try { const result = await callKeyserverRegister( { ...extraInfo, username: accountSelection.username, password: accountSelection.password, }, { urlPrefixOverride: keyserverURL, }, ); await setNativeCredentials({ username: result.currentUserInfo.username, password: accountSelection.password, }); return result; } catch (e) { if (e.message === 'username_reserved') { Alert.alert( UsernameReservedAlertDetails.title, UsernameReservedAlertDetails.message, ); } else if (e.message === 'username_taken') { Alert.alert( UsernameTakenAlertDetails.title, UsernameTakenAlertDetails.message, ); } else if (e.message === 'client_version_unsupported') { Alert.alert( AppOutOfDateAlertDetails.title, AppOutOfDateAlertDetails.message, ); } else { Alert.alert( UnknownErrorAlertDetails.title, UnknownErrorAlertDetails.message, ); } throw e; } })(); void dispatchActionPromise( keyserverRegisterActionTypes, keyserverRegisterPromise, undefined, ({ calendarQuery: extraInfo.calendarQuery }: LogInStartingPayload), ); await keyserverRegisterPromise; }, [logInExtraInfo, callKeyserverRegister, dispatchActionPromise], ); - const siweServerCall = useSIWEServerCall(); + const legacySiweServerCall = useLegacySIWEServerCall(); const dispatch = useDispatch(); const returnedFunc = React.useCallback( (input: RegistrationServerCallInput) => new Promise( // eslint-disable-next-line no-async-promise-executor async (resolve, reject) => { try { if (currentStep.step !== 'inactive') { return; } const { accountSelection, avatarData, keyserverURL } = input; if ( accountSelection.accountType === 'username' && !usingCommServicesAccessToken ) { await keyserverRegisterUsernameAccount( accountSelection, keyserverURL, ); } else if (accountSelection.accountType === 'username') { await identityRegisterUsernameAccount(accountSelection); } else { try { - await siweServerCall(accountSelection, { + await legacySiweServerCall(accountSelection, { urlPrefixOverride: keyserverURL, }); } catch (e) { Alert.alert( UnknownErrorAlertDetails.title, UnknownErrorAlertDetails.message, ); throw e; } } dispatch({ type: setURLPrefix, payload: keyserverURL, }); setCurrentStep({ step: 'waiting_for_registration_call', avatarData, resolve, reject, }); } catch (e) { reject(e); } }, ), [ currentStep, keyserverRegisterUsernameAccount, identityRegisterUsernameAccount, - siweServerCall, + legacySiweServerCall, dispatch, ], ); // STEP 2: SETTING AVATAR const uploadSelectedMedia = useUploadSelectedMedia(); const nativeSetUserAvatar = useNativeSetUserAvatar(); const hasCurrentUserInfo = useSelector( state => !!state.currentUserInfo && !state.currentUserInfo.anonymous, ); const avatarBeingSetRef = React.useRef(false); React.useEffect(() => { if ( !hasCurrentUserInfo || currentStep.step !== 'waiting_for_registration_call' || avatarBeingSetRef.current ) { return; } avatarBeingSetRef.current = true; const { avatarData, resolve } = currentStep; void (async () => { try { if (!avatarData) { return; } let updateUserAvatarRequest; if (!avatarData.needsUpload) { ({ updateUserAvatarRequest } = avatarData); } else { const { mediaSelection } = avatarData; updateUserAvatarRequest = await uploadSelectedMedia(mediaSelection); if (!updateUserAvatarRequest) { return; } } await nativeSetUserAvatar(updateUserAvatarRequest); } finally { dispatch({ type: setDataLoadedActionType, payload: { dataLoaded: true, }, }); setCurrentStep(inactiveStep); avatarBeingSetRef.current = false; resolve(); } })(); }, [ currentStep, hasCurrentUserInfo, uploadSelectedMedia, nativeSetUserAvatar, dispatch, ]); return returnedFunc; } export { useRegistrationServerCall }; diff --git a/native/account/siwe-hooks.js b/native/account/siwe-hooks.js index f39013361..506d676b3 100644 --- a/native/account/siwe-hooks.js +++ b/native/account/siwe-hooks.js @@ -1,96 +1,96 @@ // @flow import * as React from 'react'; import { siweAuth, siweAuthActionTypes } from 'lib/actions/siwe-actions.js'; import { useInitialNotificationsEncryptedMessage } from 'lib/shared/crypto-utils.js'; import type { LogInStartingPayload, LogInExtraInfo, } from 'lib/types/account-types.js'; import { useLegacyAshoatKeyserverCall } from 'lib/utils/action-utils.js'; import type { CallSingleKeyserverEndpointOptions } from 'lib/utils/call-single-keyserver-endpoint.js'; import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import { ashoatKeyserverID } from 'lib/utils/validation-utils.js'; import { useSelector } from '../redux/redux-utils.js'; import { nativeLogInExtraInfoSelector } from '../selectors/account-selectors.js'; import { nativeNotificationsSessionCreator } from '../utils/crypto-utils.js'; type SIWEServerCallParams = { +message: string, +signature: string, +doNotRegister?: boolean, ... }; -function useSIWEServerCall(): ( +function useLegacySIWEServerCall(): ( SIWEServerCallParams, ?CallSingleKeyserverEndpointOptions, ) => Promise { const siweAuthCall = useLegacyAshoatKeyserverCall(siweAuth); const callSIWE = React.useCallback( ( message: string, signature: string, extraInfo: $ReadOnly<{ ...LogInExtraInfo, +doNotRegister?: boolean }>, callSingleKeyserverEndpointOptions: ?CallSingleKeyserverEndpointOptions, ) => siweAuthCall( { message, signature, ...extraInfo, }, callSingleKeyserverEndpointOptions, ), [siweAuthCall], ); const logInExtraInfo = useSelector(nativeLogInExtraInfoSelector); const getInitialNotificationsEncryptedMessage = useInitialNotificationsEncryptedMessage(nativeNotificationsSessionCreator); const dispatchActionPromise = useDispatchActionPromise(); return React.useCallback( async ( { message, signature, doNotRegister }, callSingleKeyserverEndpointOptions, ) => { const extraInfo = await logInExtraInfo(); const initialNotificationsEncryptedMessage = await getInitialNotificationsEncryptedMessage(ashoatKeyserverID, { callSingleKeyserverEndpointOptions, }); const siwePromise = callSIWE( message, signature, { ...extraInfo, initialNotificationsEncryptedMessage, doNotRegister, }, callSingleKeyserverEndpointOptions, ); void dispatchActionPromise( siweAuthActionTypes, siwePromise, undefined, ({ calendarQuery: extraInfo.calendarQuery }: LogInStartingPayload), ); await siwePromise; }, [ logInExtraInfo, dispatchActionPromise, callSIWE, getInitialNotificationsEncryptedMessage, ], ); } -export { useSIWEServerCall }; +export { useLegacySIWEServerCall };