Page MenuHomePhabricator

D12036.id40629.diff
No OneTemporary

D12036.id40629.diff

diff --git a/native/account/registration/siwe-backup-message-creation.react.js b/native/account/registration/siwe-backup-message-creation.react.js
--- a/native/account/registration/siwe-backup-message-creation.react.js
+++ b/native/account/registration/siwe-backup-message-creation.react.js
@@ -42,9 +42,9 @@
function CreateSIWEBackupMessageBase(
props: CreateSIWEBackupMessageBaseProps,
): React.Node {
+ const styles = useStyles(unboundStyles);
const { onSuccessfulWalletSignature, onExistingWalletSignature, onSkip } =
props;
- const styles = useStyles(unboundStyles);
const {
panelState,
@@ -215,6 +215,84 @@
);
}
+type SignSIWEBackupMessageForRestoreBaseProps = {
+ +siweNonce: string,
+ +siweIssuedAt: string,
+ +siweStatement: string,
+ +onSuccessfulWalletSignature: (result: SIWEResult) => void,
+ +onSkip: () => void,
+};
+
+function SignSIWEBackupMessageForRestore(
+ props: SignSIWEBackupMessageForRestoreBaseProps,
+): React.Node {
+ const styles = useStyles(unboundStyles);
+ const {
+ panelState,
+ openPanel,
+ onPanelClosed,
+ onPanelClosing,
+ siwePanelSetLoading,
+ } = useSIWEPanelState();
+
+ const {
+ siweIssuedAt,
+ siweNonce,
+ siweStatement,
+ onSuccessfulWalletSignature,
+ onSkip,
+ } = props;
+ const siweSignatureRequestData = React.useMemo(
+ () => ({
+ messageType: SIWEMessageTypes.MSG_BACKUP_RESTORE,
+ siweNonce,
+ siweStatement,
+ siweIssuedAt,
+ }),
+ [siweStatement, siweIssuedAt, siweNonce],
+ );
+
+ let siwePanel;
+ if (panelState !== 'closed') {
+ siwePanel = (
+ <SIWEPanel
+ onClosing={onPanelClosing}
+ onClosed={onPanelClosed}
+ closing={panelState === 'closing'}
+ onSuccessfulWalletSignature={onSuccessfulWalletSignature}
+ siweSignatureRequestData={siweSignatureRequestData}
+ setLoading={siwePanelSetLoading}
+ />
+ );
+ }
+
+ return (
+ <>
+ <RegistrationContainer>
+ <RegistrationContentContainer style={styles.scrollViewContentContainer}>
+ <Text style={styles.header}>Decrypting your Comm backup</Text>
+ <Text style={styles.body}>
+ To make sure we can’t see your data, Comm encrypts your backup using
+ a signature from your wallet.
+ </Text>
+ <View style={styles.siweBackupIconContainer}>
+ <Icon name="backup" size={200} style={styles.siweBackupIcon} />
+ </View>
+ </RegistrationContentContainer>
+ <RegistrationButtonContainer>
+ <RegistrationButton
+ onPress={openPanel}
+ label="Decrypt with Ethereum signature"
+ variant="enabled"
+ />
+ <RegistrationButton onPress={onSkip} label="Skip" variant="outline" />
+ </RegistrationButtonContainer>
+ </RegistrationContainer>
+ {siwePanel}
+ </>
+ );
+}
+
const unboundStyles = {
scrollViewContentContainer: {
flexGrow: 1,
@@ -241,4 +319,8 @@
},
};
-export { CreateSIWEBackupMessageBase, CreateSIWEBackupMessage };
+export {
+ CreateSIWEBackupMessageBase,
+ CreateSIWEBackupMessage,
+ SignSIWEBackupMessageForRestore,
+};
diff --git a/native/backup/restore-siwe-backup.react.js b/native/backup/restore-siwe-backup.react.js
new file mode 100644
--- /dev/null
+++ b/native/backup/restore-siwe-backup.react.js
@@ -0,0 +1,78 @@
+// @flow
+
+import * as React from 'react';
+import { Alert } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+import { type SIWEResult } from 'lib/types/siwe-types.js';
+import { getMessageForException } from 'lib/utils/errors.js';
+
+import { SignSIWEBackupMessageForRestore } from '../account/registration/siwe-backup-message-creation.react.js';
+import { commCoreModule } from '../native-modules.js';
+import { type RootNavigationProp } from '../navigation/root-navigator.react.js';
+import { type NavigationRoute } from '../navigation/route-names.js';
+import { useStyles } from '../themes/colors.js';
+
+export type RestoreSIWEBackupParams = {
+ +backupID: string,
+ +siweNonce: string,
+ +siweStatement: string,
+ +siweIssuedAt: string,
+};
+
+type Props = {
+ +navigation: RootNavigationProp<'RestoreSIWEBackup'>,
+ +route: NavigationRoute<'RestoreSIWEBackup'>,
+};
+
+function RestoreSIWEBackup(props: Props): React.Node {
+ const styles = useStyles(unboundStyles);
+ const { goBack } = props.navigation;
+ const { route } = props;
+ const {
+ params: { backupID, siweStatement, siweIssuedAt, siweNonce },
+ } = route;
+
+ const onSuccessfulWalletSignature = React.useCallback(
+ (result: SIWEResult) => {
+ void (async () => {
+ const { signature } = result;
+ let message = 'success';
+ try {
+ await commCoreModule.restoreSIWEBackup(signature, backupID);
+ } catch (e) {
+ message = `Backup restore error: ${String(
+ getMessageForException(e),
+ )}`;
+ console.error(message);
+ }
+ Alert.alert('Restore protocol result', message);
+ goBack();
+ })();
+ },
+ [goBack, backupID],
+ );
+
+ return (
+ <SafeAreaView edges={safeAreaEdges} style={styles.container}>
+ <SignSIWEBackupMessageForRestore
+ siweNonce={siweNonce}
+ siweStatement={siweStatement}
+ siweIssuedAt={siweIssuedAt}
+ onSkip={goBack}
+ onSuccessfulWalletSignature={onSuccessfulWalletSignature}
+ />
+ </SafeAreaView>
+ );
+}
+
+const safeAreaEdges = ['top'];
+const unboundStyles = {
+ container: {
+ flex: 1,
+ backgroundColor: 'panelBackground',
+ justifyContent: 'space-between',
+ },
+};
+
+export default RestoreSIWEBackup;
diff --git a/native/backup/use-client-backup.js b/native/backup/use-client-backup.js
--- a/native/backup/use-client-backup.js
+++ b/native/backup/use-client-backup.js
@@ -11,9 +11,18 @@
import { commCoreModule } from '../native-modules.js';
import { useSelector } from '../redux/redux-utils.js';
+type SIWEBackupData = {
+ +backupID: string,
+ +siweBackupMsg: string,
+ +siweBackupMsgNonce: string,
+ +siweBackupMsgStatement: string,
+ +siweBackupMsgIssuedAt: string,
+};
+
type ClientBackup = {
+uploadBackupProtocol: () => Promise<void>,
- +restoreBackupProtocol: () => Promise<void>,
+ +restorePasswordUserBackupProtocol: () => Promise<void>,
+ +retrieveLatestSIWEBackupData: () => Promise<SIWEBackupData>,
};
async function getBackupSecret(): Promise<string> {
@@ -78,21 +87,60 @@
currentUserInfo,
]);
- const restoreBackupProtocol = React.useCallback(async () => {
+ const restorePasswordUserBackupProtocol = React.useCallback(async () => {
if (!loggedIn || !currentUserID) {
throw new Error('Attempt to restore backup for not logged in user.');
}
- console.info('Start restoring backup...');
+ if (!accountHasPassword(currentUserInfo)) {
+ throw new Error(
+ 'Attempt to restore from password for non-password user.',
+ );
+ }
+ console.info('Start restoring backup...');
await setMockCommServicesAuthMetadata();
+
const backupSecret = await getBackupSecret();
await commCoreModule.restoreBackup(backupSecret);
console.info('Backup restored.');
- }, [currentUserID, loggedIn, setMockCommServicesAuthMetadata]);
+ return;
+ }, [
+ currentUserID,
+ loggedIn,
+ setMockCommServicesAuthMetadata,
+ currentUserInfo,
+ ]);
+
+ const retrieveLatestSIWEBackupData = React.useCallback(async () => {
+ if (!loggedIn || !currentUserID) {
+ throw new Error('Attempt to restore backup for not logged in user.');
+ }
+
+ if (accountHasPassword(currentUserInfo)) {
+ throw new Error(
+ 'Attempt to retrieve siwe backup data for password user.',
+ );
+ }
+
+ await setMockCommServicesAuthMetadata();
+ const serializedBackupData =
+ await commCoreModule.retrieveLatestSIWEBackupData();
+ const siweBackupData: SIWEBackupData = JSON.parse(serializedBackupData);
+ return siweBackupData;
+ }, [
+ currentUserID,
+ currentUserInfo,
+ loggedIn,
+ setMockCommServicesAuthMetadata,
+ ]);
- return { uploadBackupProtocol, restoreBackupProtocol };
+ return {
+ uploadBackupProtocol,
+ restorePasswordUserBackupProtocol,
+ retrieveLatestSIWEBackupData,
+ };
}
export { getBackupSecret, useClientBackup };
diff --git a/native/navigation/root-navigator.react.js b/native/navigation/root-navigator.react.js
--- a/native/navigation/root-navigator.react.js
+++ b/native/navigation/root-navigator.react.js
@@ -55,11 +55,13 @@
ConnectFarcasterBottomSheetRouteName,
TagFarcasterChannelNavigatorRouteName,
CreateMissingSIWEBackupMessageRouteName,
+ RestoreSIWEBackupRouteName,
} from './route-names.js';
import LoggedOutModal from '../account/logged-out-modal.react.js';
import CreateMissingSIWEBackupMessage from '../account/registration/missing-registration-data/missing-siwe-backup-message.react.js';
import RegistrationNavigator from '../account/registration/registration-navigator.react.js';
import TermsAndPrivacyModal from '../account/terms-and-privacy-modal.react.js';
+import RestoreSIWEBackup from '../backup/restore-siwe-backup.react.js';
import ThreadPickerModal from '../calendar/thread-picker-modal.react.js';
import ImagePasteModal from '../chat/image-paste-modal.react.js';
import MessageReactionsModal from '../chat/message-reactions-modal.react.js';
@@ -303,6 +305,10 @@
name={CreateMissingSIWEBackupMessageRouteName}
component={CreateMissingSIWEBackupMessage}
/>
+ <Root.Screen
+ name={RestoreSIWEBackupRouteName}
+ component={RestoreSIWEBackup}
+ />
</Root.Navigator>
);
}
diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js
--- a/native/navigation/route-names.js
+++ b/native/navigation/route-names.js
@@ -15,6 +15,7 @@
import type { CreateSIWEBackupMessageParams } from '../account/registration/siwe-backup-message-creation.react.js';
import type { UsernameSelectionParams } from '../account/registration/username-selection.react.js';
import type { TermsAndPrivacyModalParams } from '../account/terms-and-privacy-modal.react.js';
+import type { RestoreSIWEBackupParams } from '../backup/restore-siwe-backup.react.js';
import type { ThreadPickerModalParams } from '../calendar/thread-picker-modal.react.js';
import type { ComposeSubchannelParams } from '../chat/compose-subchannel.react.js';
import type { FullScreenThreadMediaGalleryParams } from '../chat/fullscreen-thread-media-gallery.react.js';
@@ -127,6 +128,7 @@
export const CreateSIWEBackupMessageRouteName = 'CreateSIWEBackupMessage';
export const CreateMissingSIWEBackupMessageRouteName =
'CreateMissingSIWEBackupMessage';
+export const RestoreSIWEBackupRouteName = 'RestoreSIWEBackup';
export const ExistingEthereumAccountRouteName = 'ExistingEthereumAccount';
export const ConnectFarcasterRouteName = 'ConnectFarcaster';
export const UsernameSelectionRouteName = 'UsernameSelection';
@@ -187,6 +189,7 @@
+ConnectFarcasterBottomSheet: void,
+TagFarcasterChannelNavigator: void,
+CreateMissingSIWEBackupMessage: void,
+ +RestoreSIWEBackup: RestoreSIWEBackupParams,
};
export type MessageTooltipRouteNames =
diff --git a/native/profile/backup-menu.react.js b/native/profile/backup-menu.react.js
--- a/native/profile/backup-menu.react.js
+++ b/native/profile/backup-menu.react.js
@@ -1,9 +1,11 @@
// @flow
+import { useNavigation } from '@react-navigation/native';
import * as React from 'react';
import { Switch, Text, View } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
+import { accountHasPassword } from 'lib/shared/account-utils.js';
import { getMessageForException } from 'lib/utils/errors.js';
import { useDispatch } from 'lib/utils/redux-utils.js';
@@ -11,6 +13,7 @@
import { useClientBackup } from '../backup/use-client-backup.js';
import Button from '../components/button.react.js';
import type { NavigationRoute } from '../navigation/route-names.js';
+import { RestoreSIWEBackupRouteName } from '../navigation/route-names.js';
import { setLocalSettingsActionType } from '../redux/action-types.js';
import { useSelector } from '../redux/redux-utils.js';
import { useColors, useStyles } from '../themes/colors.js';
@@ -25,12 +28,18 @@
const styles = useStyles(unboundStyles);
const dispatch = useDispatch();
const colors = useColors();
+ const currentUserInfo = useSelector(state => state.currentUserInfo);
+ const navigation = useNavigation();
const isBackupEnabled = useSelector(
state => state.localSettings.isBackupEnabled,
);
- const { uploadBackupProtocol, restoreBackupProtocol } = useClientBackup();
+ const {
+ uploadBackupProtocol,
+ restorePasswordUserBackupProtocol,
+ retrieveLatestSIWEBackupData,
+ } = useClientBackup();
const uploadBackup = React.useCallback(async () => {
let message = 'Success';
@@ -43,16 +52,43 @@
Alert.alert('Upload protocol result', message);
}, [uploadBackupProtocol]);
- const testRestore = React.useCallback(async () => {
+ const testRestoreForPasswordUser = React.useCallback(async () => {
let message = 'success';
try {
- await restoreBackupProtocol();
+ await restorePasswordUserBackupProtocol();
} catch (e) {
message = `Backup restore error: ${String(getMessageForException(e))}`;
console.error(message);
}
Alert.alert('Restore protocol result', message);
- }, [restoreBackupProtocol]);
+ }, [restorePasswordUserBackupProtocol]);
+
+ const testRestoreForSIWEUser = React.useCallback(async () => {
+ let message = 'success';
+ try {
+ const siweBackupData = await retrieveLatestSIWEBackupData();
+
+ const {
+ backupID,
+ siweBackupMsgNonce,
+ siweBackupMsgIssuedAt,
+ siweBackupMsgStatement,
+ } = siweBackupData;
+
+ navigation.navigate<'RestoreSIWEBackup'>({
+ name: RestoreSIWEBackupRouteName,
+ params: {
+ backupID,
+ siweNonce: siweBackupMsgNonce,
+ siweStatement: siweBackupMsgStatement,
+ siweIssuedAt: siweBackupMsgIssuedAt,
+ },
+ });
+ } catch (e) {
+ message = `Backup restore error: ${String(getMessageForException(e))}`;
+ console.error(message);
+ }
+ }, [navigation, retrieveLatestSIWEBackupData]);
const onBackupToggled = React.useCallback(
(value: boolean) => {
@@ -64,6 +100,10 @@
[dispatch],
);
+ const onPressRestoreButton = accountHasPassword(currentUserInfo)
+ ? testRestoreForPasswordUser
+ : testRestoreForSIWEUser;
+
return (
<ScrollView
contentContainerStyle={styles.scrollViewContentContainer}
@@ -91,7 +131,7 @@
</View>
<View style={styles.section}>
<Button
- onPress={testRestore}
+ onPress={onPressRestoreButton}
style={styles.row}
iosFormat="highlight"
iosHighlightUnderlayColor={colors.panelIosHighlightUnderlay}

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 27, 11:39 AM (21 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2590027
Default Alt Text
D12036.id40629.diff (14 KB)

Event Timeline