Skip to content

Commit

Permalink
backups: warning + new secret phrase styles (#5322)
Browse files Browse the repository at this point in the history
* save progress

* cleanup and finish secret phrase / private key viewiing screens

---------

Co-authored-by: Matthew Wall <[email protected]>
  • Loading branch information
skylarbarrera and walmat authored Jan 23, 2024
1 parent 948af92 commit c8cd9c3
Show file tree
Hide file tree
Showing 18 changed files with 676 additions and 213 deletions.
Binary file added src/assets/manuallyBackedUp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/components/secret-display/SecretDisplayCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import lang from 'i18n-js';
import * as i18n from '@/languages';
import React from 'react';
import CopyTooltip from '../copy-tooltip';
import { Centered } from '../layout';
Expand All @@ -22,14 +22,14 @@ export default function SecretDisplayCard({
<Box
background="card (Deprecated)"
borderRadius={25}
height={{ custom: 240 }}
height={{ custom: type === WalletTypes.privateKey ? 90 : 240 }}
paddingHorizontal="30px (Deprecated)"
paddingVertical="19px (Deprecated)"
shadow="21px light (Deprecated)"
>
<CopyTooltip
textToCopy={seed}
tooltipText={lang.t('back_up.secret.copy_to_clipboard')}
tooltipText={i18n.t(i18n.l.back_up.secret.copy_to_clipboard)}
>
<Box alignItems="center" height="full" justifyContent="center">
{seed && type === WalletTypes.mnemonic && (
Expand Down
220 changes: 166 additions & 54 deletions src/components/secret-display/SecretDisplaySection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useRoute } from '@react-navigation/native';
import { RouteProp, useRoute } from '@react-navigation/native';
import { captureException } from '@sentry/react-native';
import lang from 'i18n-js';
import React, { ReactNode, useCallback, useEffect, useState } from 'react';
import {
createdWithBiometricError,
Expand All @@ -10,24 +9,37 @@ import {
import ActivityIndicator from '../ActivityIndicator';
import Spinner from '../Spinner';
import { CopyFloatingEmojis } from '../floating-emojis';
import { Icon } from '../icons';
import * as i18n from '@/languages';
import SecretDisplayCard from './SecretDisplayCard';
import { Box, Inline, Stack, Text } from '@/design-system';
import { Bleed, Box, Inline, Inset, Stack, Text } from '@/design-system';
import WalletTypes, { EthereumWalletType } from '@/helpers/walletTypes';
import { useWallets } from '@/hooks';
import { position } from '@/styles';
import { useDimensions, useWalletManualBackup, useWallets } from '@/hooks';
import { useTheme } from '@/theme';
import { logger, RainbowError } from '@/logger';
import { IS_ANDROID } from '@/env';
import { IS_ANDROID, IS_TEST } from '@/env';
import RadialGradient from 'react-native-radial-gradient';
import ManuallyBackedUpIcon from '@/assets/manuallyBackedUp.png';

import {
SecretDisplayStates,
SecretDisplayStatesType,
} from '@/components/secret-display/states';
import { SecretDisplayError } from '@/components/secret-display/SecretDisplayError';
import { InteractionManager, StyleSheet } from 'react-native';
import { InteractionManager, StyleSheet, View } from 'react-native';
import WalletBackupTypes from '@/helpers/walletBackupTypes';
import { SheetActionButton } from '../sheet';
import { sharedCoolModalTopOffset } from '@/navigation/config';
import { useNavigation } from '@/navigation';
import { ImgixImage } from '../images';

const MIN_HEIGHT = 740;

const LoadingSpinner = IS_ANDROID ? Spinner : ActivityIndicator;

const SafeRadialGradient = (IS_TEST
? View
: RadialGradient) as typeof RadialGradient;

type WrapperProps = {
children?: ReactNode;
};
Expand All @@ -46,19 +58,48 @@ function Wrapper({ children }: WrapperProps) {
}

type Props = {
onSecretLoaded?: (seedExists: boolean) => void;
onSecretLoaded?: (loaded: boolean) => void;
onWalletTypeIdentified?: (walletType: EthereumWalletType) => void;
};

type SecretDisplaySectionParams = {
SecretDisplaySection: {
walletId: string;
isBackingUp?: boolean;
backupType?: keyof typeof WalletBackupTypes;
secretText: string;
};
};

const SIZE = 40;

export function SecretDisplaySection({
onSecretLoaded,
onWalletTypeIdentified,
}: Props) {
const { goBack } = useNavigation();
const { height: deviceHeight } = useDimensions();
const { colors } = useTheme();
const { params } = useRoute();
const { params } = useRoute<
RouteProp<SecretDisplaySectionParams, 'SecretDisplaySection'>
>();
const { selectedWallet, wallets } = useWallets();
const walletId = (params as any)?.walletId || selectedWallet.id;
const { onManuallyBackupWalletId } = useWalletManualBackup();

const { isBackingUp, backupType } = params;

const walletId = params.walletId || selectedWallet.id;
const currentWallet = wallets?.[walletId];

const isSecretPhrase = WalletTypes.mnemonic === currentWallet?.type;
const btnText = isSecretPhrase
? i18n.t(i18n.l.back_up.manual.seed.confirm_save)
: i18n.t(i18n.l.back_up.manual.pkey.confirm_save);

const description = isSecretPhrase
? i18n.t(i18n.l.back_up.manual.seed.description)
: i18n.t(i18n.l.back_up.manual.pkey.description);

const [sectionState, setSectionState] = useState<SecretDisplayStatesType>(
SecretDisplayStates.loading
);
Expand Down Expand Up @@ -100,6 +141,32 @@ export function SecretDisplaySection({
});
}, [loadSeed]);

const handleConfirmSaved = useCallback(() => {
if (backupType === WalletBackupTypes.manual) {
onManuallyBackupWalletId(walletId);
goBack();
}
}, [backupType, walletId, onManuallyBackupWalletId, goBack]);

const getIconForBackupType = useCallback(() => {
if (isBackingUp) {
if (backupType === WalletBackupTypes.manual) {
return '􀈌';
}
return '􀉆';
}

if (isSecretPhrase) {
return '􀉆';
}

return '􀟖';
}, [isBackingUp, backupType, isSecretPhrase]);

const isSmallPhone = deviceHeight < MIN_HEIGHT;
const contentHeight =
deviceHeight - (!isSmallPhone ? sharedCoolModalTopOffset : 0) - 100;

switch (sectionState) {
case SecretDisplayStates.loading:
return (
Expand All @@ -109,56 +176,98 @@ export function SecretDisplaySection({
);
case SecretDisplayStates.noSeed:
return (
<SecretDisplayError message={lang.t('back_up.secret.no_seed_phrase')} />
<SecretDisplayError
message={i18n.t(i18n.l.back_up.secret.no_seed_phrase)}
/>
);
case SecretDisplayStates.securedWithBiometrics:
return (
<SecretDisplayError
message={lang.t('back_up.secret.biometrically_secured')}
message={i18n.t(i18n.l.back_up.secret.biometrically_secured)}
/>
);
case SecretDisplayStates.revealed:
return (
<Wrapper>
<Box paddingBottom="19px (Deprecated)">
{/* @ts-expect-error JS component*/}
<CopyFloatingEmojis textToCopy={seed}>
<Inline alignVertical="center" space="6px">
<Icon
name="copy"
color={colors.appleBlue}
style={styles.copyIcon}
/>
<Text
color="action (Deprecated)"
size="16px / 22px (Deprecated)"
weight="bold"
>
{lang.t('back_up.secret.copy_to_clipboard')}
<Box style={{ height: contentHeight }}>
<Inset horizontal={'24px'} top={'104px'}>
<Bleed top={'42px (Deprecated)'}>
<Stack alignHorizontal="center" space={'20px'}>
{backupType === WalletBackupTypes.manual ? (
<Box
as={ImgixImage}
borderRadius={72 / 2}
height={{ custom: 72 }}
marginLeft={{ custom: -12 }}
marginRight={{ custom: -12 }}
marginTop={{ custom: 0 }}
marginBottom={{ custom: -12 }}
source={ManuallyBackedUpIcon}
width={{ custom: 72 }}
size={72}
/>
) : (
<Text align="center" color="orange" size="34pt" weight="bold">
{getIconForBackupType()}
</Text>
)}

<Text align="center" color="label" size="20pt" weight="bold">
{isSecretPhrase
? i18n.t(i18n.l.back_up.manual.seed.title)
: i18n.t(i18n.l.back_up.manual.pkey.title)}
</Text>
</Inline>
</CopyFloatingEmojis>
</Box>
<Stack alignHorizontal="center" space="19px (Deprecated)">
<SecretDisplayCard seed={seed ?? ''} type={walletType} />
<Text
containsEmoji
color="primary (Deprecated)"
size="16px / 22px (Deprecated)"
weight="bold"
>
👆{lang.t('back_up.secret.for_your_eyes_only')} 👆
</Text>
<Text
align="center"
color="secondary60 (Deprecated)"
size="16px / 22px (Deprecated)"
weight="semibold"
<Stack space="36px">
<Text
align="center"
color="labelTertiary"
size="15pt"
weight="semibold"
>
{description}
</Text>

{/* @ts-expect-error JS component*/}
<CopyFloatingEmojis textToCopy={seed}>
<Inline
alignHorizontal="center"
alignVertical="center"
space="6px"
>
<Text
color="action (Deprecated)"
size="16px / 22px (Deprecated)"
weight="bold"
align="center"
>
􀐅 {i18n.t(i18n.l.back_up.secret.copy_to_clipboard)}
</Text>
</Inline>
</CopyFloatingEmojis>
</Stack>

<Stack alignHorizontal="center" space="19px (Deprecated)">
<SecretDisplayCard seed={seed ?? ''} type={walletType} />
</Stack>
</Stack>
</Bleed>
</Inset>

{isBackingUp && (
<Box
position="absolute"
bottom={{ custom: 20 }}
alignItems="center"
style={{ paddingHorizontal: 24 }}
>
{lang.t('back_up.secret.anyone_who_has_these')}
</Text>
</Stack>
</Wrapper>
<SheetActionButton
label={btnText}
color="blue"
weight="bold"
onPress={handleConfirmSaved}
/>
</Box>
)}
</Box>
);
default:
logger.error(
Expand All @@ -171,8 +280,11 @@ export function SecretDisplaySection({
}

const styles = StyleSheet.create({
copyIcon: {
...position.sizeAsObject(16),
marginTop: 0.5,
gradient: {
width: SIZE,
height: SIZE,
justifyContent: 'center',
alignItems: 'center',
},
overflowHidden: { overflow: 'hidden' },
});
Loading

0 comments on commit c8cd9c3

Please sign in to comment.