Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3509530
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
28 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/hooks/login-hooks.js b/lib/hooks/login-hooks.js
index d49f7fed0..4ef63a891 100644
--- a/lib/hooks/login-hooks.js
+++ b/lib/hooks/login-hooks.js
@@ -1,192 +1,192 @@
// @flow
import * as React from 'react';
import { setDataLoadedActionType } from '../actions/client-db-store-actions.js';
import {
identityLogInActionTypes,
useIdentityPasswordLogIn,
useIdentityWalletLogIn,
logOutActionTypes,
useIdentityLogOut,
} from '../actions/user-actions.js';
-import { useKeyserverAuth } from '../keyserver-conn/keyserver-auth.js';
+import { useKeyserverAuthWithRetry } from '../keyserver-conn/keyserver-auth.js';
import { logInActionSources } from '../types/account-types.js';
import { authoritativeKeyserverID } from '../utils/authoritative-keyserver.js';
import { useDispatchActionPromise } from '../utils/redux-promise-utils.js';
import { useSelector, useDispatch } from '../utils/redux-utils.js';
import { waitUntilDatabaseDeleted } from '../utils/wait-until-db-deleted.js';
// We can't just do everything in one async callback, since the server calls
// would get bound to Redux state from before the login. In order to pick up the
// updated CSAT and currentUserInfo from Redux, we break the login into two
// steps.
type CurrentStep =
| { +step: 'inactive' }
| {
+step: 'identity_login_dispatched',
+resolve: () => void,
+reject: Error => void,
};
const inactiveStep = { step: 'inactive' };
type LogInInputs =
| {
+accountType: 'username',
+username: string,
+password: string,
}
| {
+accountType: 'ethereum',
+walletAddress: string,
+siweMessage: string,
+siweSignature: string,
};
function useLogIn(): LogInInputs => Promise<void> {
const [currentStep, setCurrentStep] =
React.useState<CurrentStep>(inactiveStep);
const identityPasswordLogIn = useIdentityPasswordLogIn();
const identityWalletLogIn = useIdentityWalletLogIn();
const dispatchActionPromise = useDispatchActionPromise();
const returnedFunc = React.useCallback(
(logInInputs: LogInInputs) =>
new Promise<void>(
// eslint-disable-next-line no-async-promise-executor
async (resolve, reject) => {
if (currentStep.step !== 'inactive') {
return;
}
const action =
logInInputs.accountType === 'username'
? identityPasswordLogIn(
logInInputs.username,
logInInputs.password,
)
: identityWalletLogIn(
logInInputs.walletAddress,
logInInputs.siweMessage,
logInInputs.siweSignature,
);
void dispatchActionPromise(identityLogInActionTypes, action);
try {
await action;
setCurrentStep({
step: 'identity_login_dispatched',
resolve,
reject,
});
} catch (e) {
reject(e);
}
},
),
[
currentStep,
dispatchActionPromise,
identityPasswordLogIn,
identityWalletLogIn,
],
);
- const keyserverAuth = useKeyserverAuth(authoritativeKeyserverID());
+ const keyserverAuth = useKeyserverAuthWithRetry(authoritativeKeyserverID());
const isRegisteredOnIdentity = useSelector(
state =>
!!state.commServicesAccessToken &&
!!state.currentUserInfo &&
!state.currentUserInfo.anonymous,
);
// We call identityLogOut in order to reset state if identity auth succeeds
// but authoritative keyserver auth fails
const identityLogOut = useIdentityLogOut();
const registeringOnAuthoritativeKeyserverRef = React.useRef(false);
const dispatch = useDispatch();
React.useEffect(() => {
if (
!isRegisteredOnIdentity ||
currentStep.step !== 'identity_login_dispatched' ||
registeringOnAuthoritativeKeyserverRef.current
) {
return;
}
registeringOnAuthoritativeKeyserverRef.current = true;
const { resolve, reject } = currentStep;
void (async () => {
try {
await keyserverAuth({
authActionSource: process.env.BROWSER
? logInActionSources.keyserverAuthFromWeb
: logInActionSources.keyserverAuthFromNative,
setInProgress: () => {},
hasBeenCancelled: () => false,
doNotRegister: false,
});
dispatch({
type: setDataLoadedActionType,
payload: {
dataLoaded: true,
},
});
resolve();
} catch (e) {
void dispatchActionPromise(logOutActionTypes, identityLogOut());
await waitUntilDatabaseDeleted();
reject(e);
} finally {
setCurrentStep(inactiveStep);
registeringOnAuthoritativeKeyserverRef.current = false;
}
})();
}, [
currentStep,
isRegisteredOnIdentity,
keyserverAuth,
dispatch,
dispatchActionPromise,
identityLogOut,
]);
return returnedFunc;
}
function usePasswordLogIn(): (
username: string,
password: string,
) => Promise<void> {
const logIn = useLogIn();
return React.useCallback(
(username: string, password: string) =>
logIn({
accountType: 'username',
username,
password,
}),
[logIn],
);
}
function useWalletLogIn(): (
walletAddress: string,
siweMessage: string,
siweSignature: string,
) => Promise<void> {
const logIn = useLogIn();
return React.useCallback(
(walletAddress: string, siweMessage: string, siweSignature: string) =>
logIn({
accountType: 'ethereum',
walletAddress,
siweMessage,
siweSignature,
}),
[logIn],
);
}
export { usePasswordLogIn, useWalletLogIn };
diff --git a/lib/keyserver-conn/keyserver-auth.js b/lib/keyserver-conn/keyserver-auth.js
index 46ede7793..884e29133 100644
--- a/lib/keyserver-conn/keyserver-auth.js
+++ b/lib/keyserver-conn/keyserver-auth.js
@@ -1,188 +1,199 @@
// @flow
import invariant from 'invariant';
import * as React from 'react';
import { useCallKeyserverEndpointContext } from './call-keyserver-endpoint-provider.react.js';
import { extractKeyserverIDFromID } from './keyserver-call-utils.js';
import {
CANCELLED_ERROR,
type CallKeyserverEndpoint,
} from './keyserver-conn-types.js';
import {
keyserverAuthActionTypes,
keyserverAuthRawAction,
} from '../actions/user-actions.js';
import { filterThreadIDsInFilterList } from '../reducers/calendar-filters-reducer.js';
import {
cookieSelector,
deviceTokenSelector,
} from '../selectors/keyserver-selectors.js';
import { IdentityClientContext } from '../shared/identity-client-context.js';
import type { AuthActionSource } from '../types/account-types.js';
import { authoritativeKeyserverID } from '../utils/authoritative-keyserver.js';
import { getConfig } from '../utils/config.js';
+import { getMessageForException } from '../utils/errors.js';
import { useDispatchActionPromise } from '../utils/redux-promise-utils.js';
import { useSelector } from '../utils/redux-utils.js';
import sleep from '../utils/sleep.js';
const AUTH_RETRY_DELAY_MS = 60000;
type KeyserverAuthInputs = {
+authActionSource: AuthActionSource,
+setInProgress: boolean => mixed,
+hasBeenCancelled: () => boolean,
+doNotRegister: boolean,
};
type RawKeyserverAuthFunc =
KeyserverAuthInputs => CallKeyserverEndpoint => Promise<void>;
function useRawKeyserverAuth(keyserverID: string): RawKeyserverAuthFunc {
const navInfo = useSelector(state => state.navInfo);
const calendarFilters = useSelector(state => state.calendarFilters);
const calendarQuery = React.useMemo(() => {
const filters = filterThreadIDsInFilterList(
calendarFilters,
(threadID: string) => extractKeyserverIDFromID(threadID) === keyserverID,
);
return {
startDate: navInfo.startDate,
endDate: navInfo.endDate,
filters,
};
}, [calendarFilters, keyserverID, navInfo.endDate, navInfo.startDate]);
const cookie = useSelector(cookieSelector(keyserverID));
const keyserverDeviceToken = useSelector(deviceTokenSelector(keyserverID));
// We have an assumption that we should be always connected to the
// authoritative keyserver. It is possible that a token which it has is
// correct, so we can try to use it. In worst case it is invalid and our
// push-handler will try to fix it.
const authoritativeKeyserverDeviceToken = useSelector(
deviceTokenSelector(authoritativeKeyserverID()),
);
const deviceToken = keyserverDeviceToken ?? authoritativeKeyserverDeviceToken;
const dispatchActionPromise = useDispatchActionPromise();
const identityContext = React.useContext(IdentityClientContext);
invariant(identityContext, 'Identity context should be set');
const { identityClient, getAuthMetadata } = identityContext;
const { olmAPI } = getConfig();
const currentUserInfo = useSelector(state => state.currentUserInfo);
return React.useCallback(
(inputs: KeyserverAuthInputs) =>
async (innerCallKeyserverEndpoint: CallKeyserverEndpoint) => {
const {
authActionSource,
setInProgress,
hasBeenCancelled,
doNotRegister,
} = inputs;
try {
const [keyserverKeys] = await Promise.all([
identityClient.getKeyserverKeys(keyserverID),
olmAPI.initializeCryptoAccount(),
]);
if (hasBeenCancelled()) {
throw new Error(CANCELLED_ERROR);
}
const [notifsSession, contentSession, { userID, deviceID }] =
await Promise.all([
olmAPI.notificationsSessionCreator(
cookie,
keyserverKeys.identityKeysBlob.notificationIdentityPublicKeys,
keyserverKeys.notifInitializationInfo,
keyserverID,
),
olmAPI.contentOutboundSessionCreator(
keyserverKeys.identityKeysBlob.primaryIdentityPublicKeys,
keyserverKeys.contentInitializationInfo,
),
getAuthMetadata(),
]);
invariant(userID, 'userID should be set');
invariant(deviceID, 'deviceID should be set');
const deviceTokenUpdateInput = deviceToken
? { [keyserverID]: { deviceToken } }
: {};
if (hasBeenCancelled()) {
throw new Error(CANCELLED_ERROR);
}
const authPromise = keyserverAuthRawAction(
innerCallKeyserverEndpoint,
)({
userID,
deviceID,
doNotRegister,
calendarQuery,
deviceTokenUpdateInput,
authActionSource,
keyserverData: {
[keyserverID]: {
initialContentEncryptedMessage:
contentSession.encryptedData.message,
initialNotificationsEncryptedMessage: notifsSession,
},
},
preRequestUserInfo: currentUserInfo,
});
void dispatchActionPromise(keyserverAuthActionTypes, authPromise);
await authPromise;
} catch (e) {
if (hasBeenCancelled()) {
return;
}
console.log(
`Error while authenticating to keyserver with id ${keyserverID}`,
e,
);
throw e;
} finally {
if (!hasBeenCancelled()) {
void (async () => {
await sleep(AUTH_RETRY_DELAY_MS);
setInProgress(false);
})();
}
}
},
[
calendarQuery,
cookie,
deviceToken,
dispatchActionPromise,
getAuthMetadata,
identityClient,
keyserverID,
olmAPI,
currentUserInfo,
],
);
}
type KeyserverAuthFunc = KeyserverAuthInputs => Promise<void>;
-function useKeyserverAuth(keyserverID: string): KeyserverAuthFunc {
+function useKeyserverAuthWithRetry(keyserverID: string): KeyserverAuthFunc {
const rawKeyserverAuth = useRawKeyserverAuth(keyserverID);
const { callKeyserverEndpoint } = useCallKeyserverEndpointContext();
return React.useCallback(
- (inputs: KeyserverAuthInputs) =>
- rawKeyserverAuth(inputs)(callKeyserverEndpoint),
+ async (inputs: KeyserverAuthInputs) => {
+ try {
+ return await rawKeyserverAuth(inputs)(callKeyserverEndpoint);
+ } catch (e) {
+ if (getMessageForException(e) === 'olm_session_creation_failure') {
+ // We retry in case we were accidentally vended an invalid OTK the
+ // first time
+ return await rawKeyserverAuth(inputs)(callKeyserverEndpoint);
+ }
+ throw e;
+ }
+ },
[rawKeyserverAuth, callKeyserverEndpoint],
);
}
-export { useRawKeyserverAuth, useKeyserverAuth };
+export { useRawKeyserverAuth, useKeyserverAuthWithRetry };
diff --git a/native/account/registration/registration-server-call.js b/native/account/registration/registration-server-call.js
index 32ee6d9ad..477553e92 100644
--- a/native/account/registration/registration-server-call.js
+++ b/native/account/registration/registration-server-call.js
@@ -1,461 +1,461 @@
// @flow
import * as React from 'react';
import { setDataLoadedActionType } from 'lib/actions/client-db-store-actions.js';
import { setSyncedMetadataEntryActionType } from 'lib/actions/synced-metadata-actions.js';
import {
legacyKeyserverRegisterActionTypes,
legacyKeyserverRegister,
useIdentityPasswordRegister,
identityRegisterActionTypes,
deleteAccountActionTypes,
useDeleteDiscardedIdentityAccount,
} from 'lib/actions/user-actions.js';
-import { useKeyserverAuth } from 'lib/keyserver-conn/keyserver-auth.js';
+import { useKeyserverAuthWithRetry } from 'lib/keyserver-conn/keyserver-auth.js';
import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
import { isLoggedInToKeyserver } from 'lib/selectors/user-selectors.js';
import {
type LegacyLogInStartingPayload,
logInActionSources,
} from 'lib/types/account-types.js';
import { syncedMetadataNames } from 'lib/types/synced-metadata-types.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 { waitUntilDatabaseDeleted } from 'lib/utils/wait-until-db-deleted.js';
import type {
RegistrationServerCallInput,
UsernameAccountSelection,
AvatarData,
} from './registration-types.js';
import { authoritativeKeyserverID } from '../../authoritative-keyserver.js';
import {
useNativeSetUserAvatar,
useUploadSelectedMedia,
} from '../../avatars/avatar-hooks.js';
import { commCoreModule } from '../../native-modules.js';
import { useSelector } from '../../redux/redux-utils.js';
import { nativeLegacyLogInExtraInfoSelector } 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 {
useLegacySIWEServerCall,
useIdentityWalletRegisterCall,
} 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: 'identity_registration_dispatched',
+clearCachedSelections: () => void,
+avatarData: ?AvatarData,
+credentialsToSave: ?{ +username: string, +password: string },
+resolve: () => void,
+reject: Error => void,
}
| {
+step: 'authoritative_keyserver_registration_dispatched',
+clearCachedSelections: () => void,
+avatarData: ?AvatarData,
+credentialsToSave: ?{ +username: string, +password: string },
+resolve: () => void,
+reject: Error => void,
};
const inactiveStep = { step: 'inactive' };
function useRegistrationServerCall(): RegistrationServerCallInput => Promise<void> {
const [currentStep, setCurrentStep] =
React.useState<CurrentStep>(inactiveStep);
// STEP 1: ACCOUNT REGISTRATION
const legacyLogInExtraInfo = useSelector(nativeLegacyLogInExtraInfoSelector);
const dispatchActionPromise = useDispatchActionPromise();
const callLegacyKeyserverRegister = useLegacyAshoatKeyserverCall(
legacyKeyserverRegister,
);
const callIdentityPasswordRegister = useIdentityPasswordRegister();
const identityRegisterUsernameAccount = React.useCallback(
async (
accountSelection: UsernameAccountSelection,
farcasterID: ?string,
) => {
const identityRegisterPromise = (async () => {
try {
return await callIdentityPasswordRegister(
accountSelection.username,
accountSelection.password,
farcasterID,
);
} 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;
},
[callIdentityPasswordRegister, dispatchActionPromise],
);
const legacyKeyserverRegisterUsernameAccount = React.useCallback(
async (
accountSelection: UsernameAccountSelection,
keyserverURL: string,
) => {
const extraInfo = await legacyLogInExtraInfo();
const legacyKeyserverRegisterPromise = (async () => {
try {
return await callLegacyKeyserverRegister(
{
...extraInfo,
username: accountSelection.username,
password: accountSelection.password,
},
{
urlPrefixOverride: keyserverURL,
},
);
} 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(
legacyKeyserverRegisterActionTypes,
legacyKeyserverRegisterPromise,
undefined,
({
calendarQuery: extraInfo.calendarQuery,
}: LegacyLogInStartingPayload),
);
await legacyKeyserverRegisterPromise;
},
[legacyLogInExtraInfo, callLegacyKeyserverRegister, dispatchActionPromise],
);
const legacySiweServerCall = useLegacySIWEServerCall();
const identityWalletRegisterCall = useIdentityWalletRegisterCall();
const dispatch = useDispatch();
const returnedFunc = React.useCallback(
(input: RegistrationServerCallInput) =>
new Promise<void>(
// eslint-disable-next-line no-async-promise-executor
async (resolve, reject) => {
try {
if (currentStep.step !== 'inactive') {
return;
}
const {
accountSelection,
avatarData,
keyserverURL,
farcasterID,
siweBackupSecrets,
clearCachedSelections,
} = input;
if (
accountSelection.accountType === 'username' &&
!usingCommServicesAccessToken
) {
await legacyKeyserverRegisterUsernameAccount(
accountSelection,
keyserverURL,
);
} else if (accountSelection.accountType === 'username') {
await identityRegisterUsernameAccount(
accountSelection,
farcasterID,
);
} else if (!usingCommServicesAccessToken) {
try {
await legacySiweServerCall(accountSelection, {
urlPrefixOverride: keyserverURL,
});
} catch (e) {
Alert.alert(
UnknownErrorAlertDetails.title,
UnknownErrorAlertDetails.message,
);
throw e;
}
} else {
try {
await identityWalletRegisterCall({
address: accountSelection.address,
message: accountSelection.message,
signature: accountSelection.signature,
fid: farcasterID,
});
} catch (e) {
Alert.alert(
UnknownErrorAlertDetails.title,
UnknownErrorAlertDetails.message,
);
throw e;
}
}
dispatch({
type: setURLPrefix,
payload: keyserverURL,
});
if (farcasterID) {
dispatch({
type: setSyncedMetadataEntryActionType,
payload: {
name: syncedMetadataNames.CURRENT_USER_FID,
data: farcasterID,
},
});
}
if (siweBackupSecrets) {
await commCoreModule.setSIWEBackupSecrets(siweBackupSecrets);
}
const credentialsToSave =
accountSelection.accountType === 'username'
? {
username: accountSelection.username,
password: accountSelection.password,
}
: null;
setCurrentStep({
step: 'identity_registration_dispatched',
avatarData,
clearCachedSelections,
credentialsToSave,
resolve,
reject,
});
} catch (e) {
reject(e);
}
},
),
[
currentStep,
legacyKeyserverRegisterUsernameAccount,
identityRegisterUsernameAccount,
legacySiweServerCall,
dispatch,
identityWalletRegisterCall,
],
);
// STEP 2: REGISTERING ON AUTHORITATIVE KEYSERVER
- const keyserverAuth = useKeyserverAuth(authoritativeKeyserverID);
+ const keyserverAuth = useKeyserverAuthWithRetry(authoritativeKeyserverID);
const isRegisteredOnIdentity = useSelector(
state =>
!!state.commServicesAccessToken &&
!!state.currentUserInfo &&
!state.currentUserInfo.anonymous,
);
// We call deleteDiscardedIdentityAccount in order to reset state if identity
// registration succeeds but authoritative keyserver auth fails
const deleteDiscardedIdentityAccount = useDeleteDiscardedIdentityAccount();
const registeringOnAuthoritativeKeyserverRef = React.useRef(false);
React.useEffect(() => {
if (
!isRegisteredOnIdentity ||
currentStep.step !== 'identity_registration_dispatched' ||
registeringOnAuthoritativeKeyserverRef.current
) {
return;
}
registeringOnAuthoritativeKeyserverRef.current = true;
const {
avatarData,
clearCachedSelections,
credentialsToSave,
resolve,
reject,
} = currentStep;
void (async () => {
try {
await keyserverAuth({
authActionSource: process.env.BROWSER
? logInActionSources.keyserverAuthFromWeb
: logInActionSources.keyserverAuthFromNative,
setInProgress: () => {},
hasBeenCancelled: () => false,
doNotRegister: false,
});
setCurrentStep({
step: 'authoritative_keyserver_registration_dispatched',
avatarData,
clearCachedSelections,
credentialsToSave,
resolve,
reject,
});
} catch (keyserverAuthException) {
const discardIdentityAccountPromise = (async () => {
try {
const deletionResult = await deleteDiscardedIdentityAccount();
Alert.alert(
UnknownErrorAlertDetails.title,
UnknownErrorAlertDetails.message,
);
return deletionResult;
} catch (deleteException) {
Alert.alert(
'Account created but login failed',
'We were able to create your account, but were unable to log ' +
'you in. Try going back to the login screen and logging in ' +
'with your new credentials.',
);
throw deleteException;
}
})();
void dispatchActionPromise(
deleteAccountActionTypes,
discardIdentityAccountPromise,
);
await waitUntilDatabaseDeleted();
reject(keyserverAuthException);
setCurrentStep(inactiveStep);
} finally {
registeringOnAuthoritativeKeyserverRef.current = false;
}
})();
}, [
currentStep,
isRegisteredOnIdentity,
keyserverAuth,
dispatchActionPromise,
deleteDiscardedIdentityAccount,
]);
// STEP 3: SETTING AVATAR
const uploadSelectedMedia = useUploadSelectedMedia();
const nativeSetUserAvatar = useNativeSetUserAvatar();
const isLoggedInToAuthoritativeKeyserver = useSelector(
isLoggedInToKeyserver(authoritativeKeyserverID),
);
const avatarBeingSetRef = React.useRef(false);
React.useEffect(() => {
if (
!isLoggedInToAuthoritativeKeyserver ||
currentStep.step !== 'authoritative_keyserver_registration_dispatched' ||
avatarBeingSetRef.current
) {
return;
}
avatarBeingSetRef.current = true;
const { avatarData, resolve, clearCachedSelections, credentialsToSave } =
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,
},
});
clearCachedSelections();
if (credentialsToSave) {
void setNativeCredentials(credentialsToSave);
}
setCurrentStep(inactiveStep);
avatarBeingSetRef.current = false;
resolve();
}
})();
}, [
currentStep,
isLoggedInToAuthoritativeKeyserver,
uploadSelectedMedia,
nativeSetUserAvatar,
dispatch,
]);
return returnedFunc;
}
export { useRegistrationServerCall };
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 23, 6:00 AM (1 d, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2690436
Default Alt Text
(28 KB)
Attached To
Mode
rCOMM Comm
Attached
Detach File
Event Timeline
Log In to Comment