Page MenuHomePhabricator

No OneTemporary

diff --git a/native/account/registration/registration-server-call.js b/native/account/registration/registration-server-call.js
index f4ada4c5c..2d23cc2cb 100644
--- a/native/account/registration/registration-server-call.js
+++ b/native/account/registration/registration-server-call.js
@@ -1,338 +1,351 @@
// @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 {
keyserverRegisterActionTypes,
keyserverRegister,
useIdentityPasswordRegister,
identityRegisterActionTypes,
} from 'lib/actions/user-actions.js';
import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
import { isLoggedInToKeyserver } from 'lib/selectors/user-selectors.js';
import type { LogInStartingPayload } 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 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 { 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 {
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: 'waiting_for_registration_call',
+ +clearCachedSelections: () => void,
+avatarData: ?AvatarData,
+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 logInExtraInfo = useSelector(nativeLogInExtraInfoSelector);
const dispatchActionPromise = useDispatchActionPromise();
const callKeyserverRegister = useLegacyAshoatKeyserverCall(keyserverRegister);
const callIdentityPasswordRegister = useIdentityPasswordRegister();
const identityRegisterUsernameAccount = React.useCallback(
async (
accountSelection: UsernameAccountSelection,
farcasterID: ?string,
) => {
const identityRegisterPromise = (async () => {
try {
const result = await callIdentityPasswordRegister(
accountSelection.username,
accountSelection.password,
farcasterID,
);
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;
},
[callIdentityPasswordRegister, 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 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 } =
- input;
+ const {
+ accountSelection,
+ avatarData,
+ keyserverURL,
+ farcasterID,
+ siweBackupSecrets,
+ clearCachedSelections,
+ } = input;
if (
accountSelection.accountType === 'username' &&
!usingCommServicesAccessToken
) {
await keyserverRegisterUsernameAccount(
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);
+ }
setCurrentStep({
step: 'waiting_for_registration_call',
avatarData,
+ clearCachedSelections,
resolve,
reject,
});
} catch (e) {
reject(e);
}
},
),
[
currentStep,
keyserverRegisterUsernameAccount,
identityRegisterUsernameAccount,
legacySiweServerCall,
dispatch,
identityWalletRegisterCall,
],
);
// STEP 2: SETTING AVATAR
const uploadSelectedMedia = useUploadSelectedMedia();
const nativeSetUserAvatar = useNativeSetUserAvatar();
const isLoggedInToAuthoritativeKeyserver = useSelector(
isLoggedInToKeyserver(authoritativeKeyserverID),
);
const avatarBeingSetRef = React.useRef(false);
React.useEffect(() => {
if (
!isLoggedInToAuthoritativeKeyserver ||
currentStep.step !== 'waiting_for_registration_call' ||
avatarBeingSetRef.current
) {
return;
}
avatarBeingSetRef.current = true;
- const { avatarData, resolve } = currentStep;
+ const { avatarData, resolve, clearCachedSelections } = 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();
setCurrentStep(inactiveStep);
avatarBeingSetRef.current = false;
resolve();
}
})();
}, [
currentStep,
isLoggedInToAuthoritativeKeyserver,
uploadSelectedMedia,
nativeSetUserAvatar,
dispatch,
]);
return returnedFunc;
}
export { useRegistrationServerCall };
diff --git a/native/account/registration/registration-terms.react.js b/native/account/registration/registration-terms.react.js
index 2823d3ebc..bfbc24bb5 100644
--- a/native/account/registration/registration-terms.react.js
+++ b/native/account/registration/registration-terms.react.js
@@ -1,130 +1,138 @@
// @flow
import invariant from 'invariant';
import * as React from 'react';
import { Text, View, Image, Linking } from 'react-native';
+import type { SIWEBackupSecrets } from 'lib/types/siwe-types.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 { RegistrationContext } from './registration-context.js';
import type { RegistrationNavigationProp } from './registration-navigator.react.js';
import type {
CoolOrNerdMode,
AccountSelection,
AvatarData,
} from './registration-types.js';
import commSwooshSource from '../../img/comm-swoosh.png';
import type { NavigationRoute } from '../../navigation/route-names.js';
import { useStyles } from '../../themes/colors.js';
export type RegistrationTermsParams = {
+userSelections: {
+coolOrNerdMode: CoolOrNerdMode,
+keyserverURL: string,
+farcasterID: ?string,
+accountSelection: AccountSelection,
+avatarData: ?AvatarData,
+ +siweBackupSecrets?: ?SIWEBackupSecrets,
},
};
const onTermsOfUsePressed = () => {
void Linking.openURL('https://comm.app/terms');
};
const onPrivacyPolicyPressed = () => {
void Linking.openURL('https://comm.app/privacy');
};
type Props = {
+navigation: RegistrationNavigationProp<'RegistrationTerms'>,
+route: NavigationRoute<'RegistrationTerms'>,
};
function RegistrationTerms(props: Props): React.Node {
const registrationContext = React.useContext(RegistrationContext);
invariant(registrationContext, 'registrationContext should be set');
- const { register } = registrationContext;
+ const { register, setCachedSelections } = registrationContext;
const [registrationInProgress, setRegistrationInProgress] =
React.useState(false);
const { userSelections } = props.route.params;
+
+ const clearCachedSelections = React.useCallback(() => {
+ setCachedSelections({});
+ }, [setCachedSelections]);
+
const onProceed = React.useCallback(async () => {
setRegistrationInProgress(true);
try {
- await register(userSelections);
+ await register({ ...userSelections, clearCachedSelections });
} finally {
setRegistrationInProgress(false);
}
- }, [register, userSelections]);
+ }, [register, userSelections, clearCachedSelections]);
const styles = useStyles(unboundStyles);
const termsNotice = (
<Text style={styles.body}>
By registering, you are agreeing to our{' '}
<Text style={styles.hyperlinkText} onPress={onTermsOfUsePressed}>
Terms of Use
</Text>
{' and '}
<Text style={styles.hyperlinkText} onPress={onPrivacyPolicyPressed}>
Privacy Policy
</Text>
.
</Text>
);
return (
<RegistrationContainer>
<RegistrationContentContainer style={styles.scrollViewContentContainer}>
<Text style={styles.header}>Finish registration</Text>
{termsNotice}
<View style={styles.commSwooshContainer}>
<Image source={commSwooshSource} style={styles.commSwoosh} />
</View>
</RegistrationContentContainer>
<RegistrationButtonContainer>
<RegistrationButton
onPress={onProceed}
label="Register"
variant={registrationInProgress ? 'loading' : 'enabled'}
/>
</RegistrationButtonContainer>
</RegistrationContainer>
);
}
const unboundStyles = {
scrollViewContentContainer: {
flexGrow: 1,
},
header: {
fontSize: 24,
color: 'panelForegroundLabel',
paddingBottom: 16,
},
body: {
fontFamily: 'Arial',
fontSize: 15,
lineHeight: 20,
color: 'panelForegroundSecondaryLabel',
paddingBottom: 16,
},
commSwooshContainer: {
flexGrow: 1,
flexShrink: 1,
alignItems: 'center',
justifyContent: 'center',
},
commSwoosh: {
resizeMode: 'center',
width: '100%',
height: '100%',
},
hyperlinkText: {
color: 'purpleLink',
},
};
export default RegistrationTerms;
diff --git a/native/account/registration/registration-types.js b/native/account/registration/registration-types.js
index 22681afa6..8db7816ef 100644
--- a/native/account/registration/registration-types.js
+++ b/native/account/registration/registration-types.js
@@ -1,67 +1,69 @@
// @flow
import type {
UpdateUserAvatarRequest,
ClientAvatar,
} from 'lib/types/avatar-types.js';
import type { NativeMediaSelection } from 'lib/types/media-types.js';
import type { SIWEResult, SIWEBackupSecrets } from 'lib/types/siwe-types.js';
export type CoolOrNerdMode = 'cool' | 'nerd';
export type EthereumAccountSelection = {
+accountType: 'ethereum',
...SIWEResult,
+avatarURI: ?string,
};
export type UsernameAccountSelection = {
+accountType: 'username',
+username: string,
+password: string,
};
export type AccountSelection =
| EthereumAccountSelection
| UsernameAccountSelection;
export type AvatarData =
| {
+needsUpload: true,
+mediaSelection: NativeMediaSelection,
+clientAvatar: ClientAvatar,
}
| {
+needsUpload: false,
+updateUserAvatarRequest: UpdateUserAvatarRequest,
+clientAvatar: ClientAvatar,
};
export type RegistrationServerCallInput = {
+coolOrNerdMode: CoolOrNerdMode,
+keyserverURL: string,
+farcasterID: ?string,
+accountSelection: AccountSelection,
+avatarData: ?AvatarData,
+ +siweBackupSecrets?: ?SIWEBackupSecrets,
+ +clearCachedSelections: () => void,
};
export type CachedUserSelections = {
+coolOrNerdMode?: CoolOrNerdMode,
+keyserverURL?: string,
+username?: string,
+password?: string,
+avatarData?: ?AvatarData,
+ethereumAccount?: EthereumAccountSelection,
+farcasterID?: string,
+siweBackupSecrets?: ?SIWEBackupSecrets,
};
export const ensAvatarSelection: AvatarData = {
needsUpload: false,
updateUserAvatarRequest: { type: 'ens' },
clientAvatar: { type: 'ens' },
};
export const enableNewRegistrationMode = __DEV__;
export const enableSIWEBackupCreation = __DEV__;
diff --git a/native/account/registration/siwe-backup-message-creation.react.js b/native/account/registration/siwe-backup-message-creation.react.js
index 0d1abbc70..4ecb4b806 100644
--- a/native/account/registration/siwe-backup-message-creation.react.js
+++ b/native/account/registration/siwe-backup-message-creation.react.js
@@ -1,187 +1,198 @@
// @flow
import Icon from '@expo/vector-icons/MaterialIcons.js';
import invariant from 'invariant';
import * as React from 'react';
import { View, Text } from 'react-native';
import { type SIWEResult, SIWEMessageTypes } from 'lib/types/siwe-types.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 { RegistrationContext } from './registration-context.js';
import { type RegistrationNavigationProp } from './registration-navigator.react.js';
import type {
CoolOrNerdMode,
AccountSelection,
AvatarData,
} from './registration-types.js';
import {
type NavigationRoute,
RegistrationTermsRouteName,
} from '../../navigation/route-names.js';
import { useStyles } from '../../themes/colors.js';
import SIWEPanel from '../siwe-panel.react.js';
export type CreateSIWEBackupMessageParams = {
+userSelections: {
+coolOrNerdMode: CoolOrNerdMode,
+keyserverURL: string,
+farcasterID: ?string,
+accountSelection: AccountSelection,
+avatarData: ?AvatarData,
},
};
type PanelState = 'closed' | 'opening' | 'open' | 'closing';
type Props = {
+navigation: RegistrationNavigationProp<'CreateSIWEBackupMessage'>,
+route: NavigationRoute<'CreateSIWEBackupMessage'>,
};
function CreateSIWEBackupMessage(props: Props): React.Node {
const { navigate } = props.navigation;
const { params } = props.route;
+ const { userSelections } = params;
const styles = useStyles(unboundStyles);
const [panelState, setPanelState] = React.useState<PanelState>('closed');
const openPanel = React.useCallback(() => {
setPanelState('opening');
}, []);
const onPanelClosed = React.useCallback(() => {
setPanelState('closed');
}, []);
const onPanelClosing = React.useCallback(() => {
setPanelState('closing');
}, []);
const siwePanelSetLoading = React.useCallback(
(loading: boolean) => {
if (panelState === 'closing' || panelState === 'closed') {
return;
}
setPanelState(loading ? 'opening' : 'open');
},
[panelState],
);
const registrationContext = React.useContext(RegistrationContext);
invariant(registrationContext, 'registrationContext should be set');
const { cachedSelections, setCachedSelections } = registrationContext;
const onSuccessfulWalletSignature = React.useCallback(
(result: SIWEResult) => {
const { message, signature } = result;
+ const newUserSelections = {
+ ...userSelections,
+ siweBackupSecrets: { message, signature },
+ };
setCachedSelections(oldUserSelections => ({
...oldUserSelections,
siweBackupSecrets: { message, signature },
}));
navigate<'RegistrationTerms'>({
name: RegistrationTermsRouteName,
- params,
+ params: { userSelections: newUserSelections },
});
},
- [navigate, params, setCachedSelections],
+ [navigate, setCachedSelections, userSelections],
);
+ const { siweBackupSecrets } = cachedSelections;
const onExistingWalletSignature = React.useCallback(() => {
+ const registrationTermsParams = {
+ userSelections: {
+ ...userSelections,
+ siweBackupSecrets,
+ },
+ };
+
navigate<'RegistrationTerms'>({
name: RegistrationTermsRouteName,
- params,
+ params: registrationTermsParams,
});
- }, [params, navigate]);
+ }, [navigate, siweBackupSecrets, userSelections]);
let siwePanel;
if (panelState !== 'closed') {
siwePanel = (
<SIWEPanel
onClosing={onPanelClosing}
onClosed={onPanelClosed}
closing={panelState === 'closing'}
onSuccessfulWalletSignature={onSuccessfulWalletSignature}
siweMessageType={SIWEMessageTypes.MSG_BACKUP}
setLoading={siwePanelSetLoading}
/>
);
}
- const { siweBackupSecrets } = cachedSelections;
-
const newSignatureButtonText = siweBackupSecrets
? 'Encrypt with new signature'
: 'Encrypt with Ethereum signature';
const newSignatureButtonVariant = siweBackupSecrets ? 'outline' : 'enabled';
let useExistingSignatureButton;
if (siweBackupSecrets) {
useExistingSignatureButton = (
<RegistrationButton
onPress={onExistingWalletSignature}
label="Encrypt with existing signature"
variant="enabled"
/>
);
}
const body = (
<Text style={styles.body}>
Comm encrypts user backups so that our backend is not able to see user
data.
</Text>
);
return (
<>
<RegistrationContainer>
<RegistrationContentContainer style={styles.scrollViewContentContainer}>
<Text style={styles.header}>Encrypting your Comm Backup</Text>
{body}
<View style={styles.siweBackupIconContainer}>
<Icon name="backup" size={200} style={styles.siweBackupIcon} />
</View>
</RegistrationContentContainer>
<RegistrationButtonContainer>
{useExistingSignatureButton}
<RegistrationButton
onPress={openPanel}
label={newSignatureButtonText}
variant={newSignatureButtonVariant}
/>
</RegistrationButtonContainer>
</RegistrationContainer>
{siwePanel}
</>
);
}
const unboundStyles = {
scrollViewContentContainer: {
flexGrow: 1,
},
header: {
fontSize: 24,
color: 'panelForegroundLabel',
paddingBottom: 16,
},
body: {
fontFamily: 'Arial',
fontSize: 15,
lineHeight: 20,
color: 'panelForegroundSecondaryLabel',
paddingBottom: 16,
},
siweBackupIcon: {
color: 'panelForegroundIcon',
},
siweBackupIconContainer: {
flexGrow: 1,
alignItems: 'center',
justifyContent: 'center',
},
};
export default CreateSIWEBackupMessage;

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 12:21 AM (2 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2689989
Default Alt Text
(24 KB)

Event Timeline