diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js --- a/lib/actions/user-actions.js +++ b/lib/actions/user-actions.js @@ -25,6 +25,7 @@ KeyserverAuthRequest, ClientLogInResponse, KeyserverLogOutResult, + LogOutResult, } from '../types/account-types.js'; import type { UpdateUserAvatarRequest, @@ -66,6 +67,7 @@ import { getConfig } from '../utils/config.js'; import { useKeyserverCall } from '../utils/keyserver-call.js'; import { useSelector } from '../utils/redux-utils.js'; +import { usingCommServicesAccessToken } from '../utils/services-utils.js'; import sleep from '../utils/sleep.js'; import { ashoatKeyserverID } from '../utils/validation-utils.js'; @@ -188,21 +190,36 @@ ); } -const deleteIdentityAccountActionTypes = Object.freeze({ - started: 'DELETE_IDENTITY_ACCOUNT_STARTED', - success: 'DELETE_IDENTITY_ACCOUNT_SUCCESS', - failed: 'DELETE_IDENTITY_ACCOUNT_FAILED', +const deleteAccountActionTypes = Object.freeze({ + started: 'DELETE_ACCOUNT_STARTED', + success: 'DELETE_ACCOUNT_SUCCESS', + failed: 'DELETE_ACCOUNT_FAILED', }); -function useDeleteIdentityAccount(): () => Promise { +function useDeleteAccount(): () => Promise { const client = React.useContext(IdentityClientContext); const identityClient = client?.identityClient; - return React.useCallback(() => { - if (!identityClient) { - throw new Error('Identity service client is not initialized'); + + const preRequestUserState = useSelector(preRequestUserStateSelector); + const callKeyserverDeleteAccount = useKeyserverCall(deleteKeyserverAccount); + + return React.useCallback(async () => { + let identityAction = () => Promise.resolve(undefined); + if (usingCommServicesAccessToken) { + if (!identityClient) { + throw new Error('Identity service client is not initialized'); + } + identityAction = identityClient.deleteUser; } - return identityClient.deleteUser(); - }, [identityClient]); + const [keyserverResult] = await Promise.all([ + callKeyserverDeleteAccount({ + preRequestUserState, + }), + identityAction(), + ]); + const { keyserverIDs: _, ...result } = keyserverResult; + return result; + }, [callKeyserverDeleteAccount, identityClient, preRequestUserState]); } const keyserverRegisterActionTypes = Object.freeze({ @@ -743,8 +760,8 @@ updateUserAvatar, resetUserStateActionType, setAccessTokenActionType, - deleteIdentityAccountActionTypes, - useDeleteIdentityAccount, + deleteAccountActionTypes, + useDeleteAccount, keyserverAuthActionTypes, useKeyserverAuth, identityRegisterActionTypes, diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js --- a/lib/types/redux-types.js +++ b/lib/types/redux-types.js @@ -234,18 +234,18 @@ +loadingInfo: LoadingInfo, } | { - +type: 'DELETE_IDENTITY_ACCOUNT_STARTED', + +type: 'DELETE_ACCOUNT_STARTED', +payload?: void, +loadingInfo: LoadingInfo, } | { - +type: 'DELETE_IDENTITY_ACCOUNT_FAILED', + +type: 'DELETE_ACCOUNT_FAILED', +error: true, +payload: Error, +loadingInfo: LoadingInfo, } | { - +type: 'DELETE_IDENTITY_ACCOUNT_SUCCESS', + +type: 'DELETE_ACCOUNT_SUCCESS', +payload: LogOutResult, +loadingInfo: LoadingInfo, } diff --git a/native/profile/delete-account.react.js b/native/profile/delete-account.react.js --- a/native/profile/delete-account.react.js +++ b/native/profile/delete-account.react.js @@ -5,17 +5,11 @@ import { ScrollView } from 'react-native-gesture-handler'; import { - deleteIdentityAccountActionTypes, - deleteKeyserverAccountActionTypes, - useDeleteIdentityAccount, - useDeleteKeyserverAccount, + deleteAccountActionTypes, + useDeleteAccount, } from 'lib/actions/user-actions.js'; -import { - createLoadingStatusSelector, - combineLoadingStatuses, -} from 'lib/selectors/loading-selectors.js'; +import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; -import { usingCommServicesAccessToken } from 'lib/utils/services-utils.js'; import type { ProfileNavigationProp } from './profile.react.js'; import { deleteNativeCredentialsFor } from '../account/native-credentials.js'; @@ -25,11 +19,8 @@ import { useStyles } from '../themes/colors.js'; import Alert from '../utils/alert.js'; -const keyserverLoadingStatusSelector = createLoadingStatusSelector( - deleteKeyserverAccountActionTypes, -); -const identityLoadingStatusSelector = createLoadingStatusSelector( - deleteIdentityAccountActionTypes, +const deleteAccountLoadingStatusSelector = createLoadingStatusSelector( + deleteAccountActionTypes, ); type Props = { @@ -38,20 +29,16 @@ }; const DeleteAccount: React.ComponentType = React.memo( function DeleteAccount() { - const keyserverLoadingStatus = useSelector(keyserverLoadingStatusSelector); - const identityLoadingStatus = useSelector(identityLoadingStatusSelector); - const combinedLoadingStatuses = combineLoadingStatuses( - keyserverLoadingStatus, - identityLoadingStatus, + const deleteAccountLoadingStatus = useSelector( + deleteAccountLoadingStatusSelector, ); const styles = useStyles(unboundStyles); const dispatchActionPromise = useDispatchActionPromise(); - const callDeleteKeyserverAccount = useDeleteKeyserverAccount(); - const callDeleteIdentityAccount = useDeleteIdentityAccount(); + const callDeleteAccount = useDeleteAccount(); - const isButtonDisabled = combinedLoadingStatuses === 'loading'; + const isButtonDisabled = deleteAccountLoadingStatus === 'loading'; const buttonContent = isButtonDisabled ? ( @@ -64,26 +51,10 @@ [styles.warningText, styles.lastWarningText], ); - const deleteKeyserverAction = React.useCallback(async () => { + const deleteAccountAction = React.useCallback(async () => { try { await deleteNativeCredentialsFor(); - return await callDeleteKeyserverAccount(); - } catch (e) { - Alert.alert( - 'Unknown error deleting keyserver account', - 'Uhh... try again?', - [{ text: 'OK' }], - { - cancelable: false, - }, - ); - throw e; - } - }, [callDeleteKeyserverAccount]); - - const deleteIdentityAction = React.useCallback(async () => { - try { - return await callDeleteIdentityAccount(); + return await callDeleteAccount(); } catch (e) { Alert.alert( 'Unknown error deleting account', @@ -95,20 +66,14 @@ ); throw e; } - }, [callDeleteIdentityAccount]); + }, [callDeleteAccount]); const onDelete = React.useCallback(() => { void dispatchActionPromise( - deleteKeyserverAccountActionTypes, - deleteKeyserverAction(), + deleteAccountActionTypes, + deleteAccountAction(), ); - if (usingCommServicesAccessToken) { - void dispatchActionPromise( - deleteIdentityAccountActionTypes, - deleteIdentityAction(), - ); - } - }, [dispatchActionPromise, deleteKeyserverAction, deleteIdentityAction]); + }, [dispatchActionPromise, deleteAccountAction]); return ( = React.memo<{}>( function AccountDeleteModal(): React.Node { - const isDeleteKeyserverAccountLoading = useSelector( - state => deleteKeyserverAccountLoadingStatusSelector(state) === 'loading', - ); - const isDeleteIdentityAccountLoading = useSelector( - state => deleteIdentityAccountLoadingStatusSelector(state) === 'loading', + const inputDisabled = useSelector( + state => deleteAccountLoadingStatusSelector(state) === 'loading', ); - const inputDisabled = - isDeleteKeyserverAccountLoading || isDeleteIdentityAccountLoading; - - const callDeleteIdentityAccount = useDeleteIdentityAccount(); - const callDeleteKeyserverAccount = useDeleteKeyserverAccount(); + const callDeleteAccount = useDeleteAccount(); const dispatchActionPromise = useDispatchActionPromise(); - const { popModal } = useModalContext(); - const [keyserverErrorMessage, setKeyserverErrorMessage] = - React.useState(''); - const [identityErrorMessage, setIdentityErrorMessage] = React.useState(''); - - const keyserverError = keyserverErrorMessage ? ( -

{keyserverErrorMessage}

- ) : null; - const identityError = identityErrorMessage ? ( -

{identityErrorMessage}

- ) : null; - let combinedErrorMessages; - if (keyserverError || identityError) { - combinedErrorMessages = ( + const [errorMessage, setErrorMessage] = React.useState(''); + let error; + if (errorMessage) { + error = (
- {keyserverError} - {identityError} +

{errorMessage}

); } - const deleteKeyserverAction = React.useCallback(async () => { + const deleteAccountAction = React.useCallback(async () => { try { - setKeyserverErrorMessage(''); - const response = await callDeleteKeyserverAccount(); - // This check ensures that we don't call `popModal()` twice - if (!usingCommServicesAccessToken) { - popModal(); - } - return response; - } catch (e) { - setKeyserverErrorMessage( - 'unknown error deleting account from keyserver', - ); - throw e; - } - }, [callDeleteKeyserverAccount, popModal]); - - const deleteIdentityAction = React.useCallback(async () => { - try { - setIdentityErrorMessage(''); - const response = await callDeleteIdentityAccount(); + setErrorMessage(''); + const response = await callDeleteAccount(); popModal(); return response; } catch (e) { - setIdentityErrorMessage( - 'unknown error deleting account from identity service', - ); + setErrorMessage('unknown error deleting account'); throw e; } - }, [callDeleteIdentityAccount, popModal]); + }, [callDeleteAccount, popModal]); const onDelete = React.useCallback( (event: SyntheticEvent) => { event.preventDefault(); void dispatchActionPromise( - deleteKeyserverAccountActionTypes, - deleteKeyserverAction(), + deleteAccountActionTypes, + deleteAccountAction(), ); - if (usingCommServicesAccessToken) { - void dispatchActionPromise( - deleteIdentityAccountActionTypes, - deleteIdentityAction(), - ); - } }, - [dispatchActionPromise, deleteKeyserverAction, deleteIdentityAction], + [dispatchActionPromise, deleteAccountAction], ); return ( @@ -131,7 +82,7 @@ > Delete Account - {combinedErrorMessages} + {error}