From 06b90b065ccb15cc528b752a16da85509ad224d6 Mon Sep 17 00:00:00 2001 From: Luis Perrone Date: Sat, 7 Dec 2024 17:25:36 -0600 Subject: [PATCH 1/5] New onboarding --- ios/Podfile.lock | 27 + package.json | 9 +- src/app/RootNavigator.tsx | 16 +- src/app/services/AccountsService/index.tsx | 120 ++--- .../pages/AddExistingWalletPage.tsx | 186 +++++++ .../pages/ConnectKeystonePage.tsx | 72 +++ .../pages/CreateSeedPhrasePage.tsx | 82 +++ .../AccountsService/pages/PairLedgerPage.tsx | 77 +++ .../AccountsService/pages/YourWalletsPage.tsx | 468 +++--------------- src/app/services/ServiceSheet.tsx | 7 +- src/assets/images/acorn.png | Bin 0 -> 33596 bytes src/assets/images/acorn@2x.png | Bin 0 -> 114850 bytes src/assets/images/acorn@3x.png | Bin 0 -> 242592 bytes src/assets/images/dewicatSticker.png | Bin 0 -> 29832 bytes src/assets/images/dewicatSticker@2x.png | Bin 0 -> 94970 bytes src/assets/images/dewicatSticker@3x.png | Bin 0 -> 183170 bytes src/assets/images/keystone.png | Bin 0 -> 28605 bytes src/assets/images/keystone@2x.png | Bin 0 -> 73716 bytes src/assets/images/keystone@3x.png | Bin 0 -> 127605 bytes src/assets/images/ledger.png | Bin 0 -> 30624 bytes src/assets/images/ledger@2x.png | Bin 0 -> 93419 bytes src/assets/images/ledger@3x.png | Bin 0 -> 181775 bytes src/assets/images/lock.png | Bin 0 -> 3264 bytes src/assets/images/lock@2x.png | Bin 0 -> 8389 bytes src/assets/images/lock@3x.png | Bin 0 -> 15584 bytes src/assets/images/login.png | Bin 0 -> 4056 bytes src/assets/images/login@2x.png | Bin 0 -> 10783 bytes src/assets/images/login@3x.png | Bin 0 -> 20293 bytes src/assets/images/welcomeToHelium.png | Bin 0 -> 98940 bytes src/assets/images/welcomeToHelium@2x.png | Bin 0 -> 326680 bytes src/assets/images/welcomeToHelium@3x.png | Bin 0 -> 663715 bytes src/assets/svgs/addExistingWallet.svg | 3 + src/assets/svgs/blueCircleLoader.svg | 4 + src/assets/svgs/bluetooth.svg | 12 +- src/assets/svgs/commandLine.svg | 4 + src/assets/svgs/heliumLogoSticker.svg | 24 + src/assets/svgs/infoError.svg | 2 +- src/assets/svgs/key.svg | 3 + src/assets/svgs/keystone.svg | 4 + src/assets/svgs/keystoneCircle.svg | 5 + src/assets/svgs/keystoneSelected.svg | 4 + src/assets/svgs/ledger.svg | 7 +- src/assets/svgs/ledgerCircle.svg | 4 + src/assets/svgs/ledgerSelected.svg | 3 + src/assets/svgs/privateKey.svg | 4 + src/assets/svgs/scan.svg | 3 + src/assets/svgs/secretPhrase.svg | 4 + src/components/CameraScannerLayout.tsx | 1 - .../components/CheckButton.tsx | 0 src/components/CircleLoader.tsx | 22 +- src/components/DynamicQrScanner.tsx | 11 +- .../components/ForwardButton.tsx | 0 src/components/HeliumBottomSheet.tsx | 144 +++--- .../LoadingButton.tsx} | 0 src/components/QrScanner.tsx | 13 +- src/components/RevealWords.tsx | 34 +- src/components/ServiceNavBar.tsx | 14 +- src/components/ServiceSheetPage.tsx | 3 + .../components/WalletButton.tsx | 6 +- src/config/locales/en.ts | 65 ++- src/features/accounts/WalletList.tsx | 378 ++++++++++++++ .../accounts}/accountServiceTypes.ts | 0 .../hotspot-onboarding/OnboardingSheet.tsx | 3 +- .../screens/SelectFloorScreen.tsx | 2 +- .../screens/SelectLocationScreen.tsx | 2 +- .../screens/SelectNetworkScreen.tsx | 4 +- .../screens/iot/AddToWalletScreen.tsx | 2 +- .../screens/iot/WifiSetup.tsx | 4 +- .../screens/mobile/AcquireLocationScreen.tsx | 2 +- .../screens/mobile/AddToWalletScreen.tsx | 2 +- .../screens/mobile/ConnectEthernetScreen.tsx | 2 +- .../screens/mobile/ConnectToHotspotScreen.tsx | 2 +- .../screens/mobile/KeepYourBoxScreen.tsx | 2 +- .../screens/mobile/ManualEntryScreen.tsx | 4 +- .../screens/mobile/ScanQRCodeScreen.tsx | 7 +- .../screens/mobile/SetDirectionScreen.tsx | 2 +- src/features/hotspots/HotspotDetails.tsx | 2 + .../keystone/KeystoneAccountAssignScreen.tsx | 60 +-- .../keystone/KeystoneOnboardingProvider.tsx | 4 + src/features/keystone/ScanQrCodeScreen.tsx | 27 +- .../keystone/SelectKeystoneAccountsScreen.tsx | 161 +++--- src/features/ledger/DeviceScan.tsx | 50 +- src/features/ledger/DeviceShow.tsx | 154 ++---- src/features/ledger/LedgerAccountListItem.tsx | 12 +- src/features/ledger/PairSuccess.tsx | 43 +- .../onboarding/AccountAssignScreen.tsx | 276 +++++------ .../onboarding/CreateImportAccountScreen.tsx | 249 +++++++--- src/features/onboarding/NewAccountScreen.tsx | 94 ++++ .../onboarding/OnboardingNavigator.tsx | 10 + .../onboarding/OnboardingProvider.tsx | 5 +- src/features/onboarding/OnboardingSheet.tsx | 362 ++++++++++++++ .../onboarding/WelcomeToHeliumScreen.tsx | 67 +++ .../CLIAccountImportStartScreen.tsx | 94 ++-- .../cli-import/CLIPasswordScreen.tsx | 144 +++--- .../onboarding/cli-import/CLIQrScanner.tsx | 21 +- .../create/AccountCreatePassphraseScreen.tsx | 124 +++-- .../create/AccountCreateStartScreen.tsx | 15 - .../create/AccountEnterPassphraseScreen.tsx | 17 +- .../onboarding/create/ConfirmWordsScreen.tsx | 248 +++++----- .../create/CreateAccountNavigator.tsx | 10 - .../onboarding/import/AccountImportScreen.tsx | 319 ++++++------ .../onboarding/import/ImportPrivateKey.tsx | 259 +++++----- .../import/ImportSubAccountsScreen.tsx | 127 ++--- .../onboarding/import/MatchingWord.tsx | 27 +- .../import/PassphraseAutocomplete.tsx | 101 ++-- src/features/onboarding/onboardingTypes.ts | 2 + src/features/stickers/GestureHandler.tsx | 89 ++++ src/features/stickers/MatrixHelpers.tsx | 78 +++ src/features/stickers/StickerContext.tsx | 67 +++ src/features/stickers/StickersPage.tsx | 43 ++ .../stickers/components/DeWiCatSticker.tsx | 25 + .../stickers/components/HeliumSticker.tsx | 44 ++ .../stickers/components/HotspotSticker.tsx | 65 +++ src/features/stickers/components/Sticker.ts | 7 + src/hooks/useDerivationAccounts.ts | 3 +- yarn.lock | 162 ++++-- 116 files changed, 3511 insertions(+), 2041 deletions(-) create mode 100644 src/app/services/AccountsService/pages/AddExistingWalletPage.tsx create mode 100644 src/app/services/AccountsService/pages/ConnectKeystonePage.tsx create mode 100644 src/app/services/AccountsService/pages/CreateSeedPhrasePage.tsx create mode 100644 src/app/services/AccountsService/pages/PairLedgerPage.tsx create mode 100644 src/assets/images/acorn.png create mode 100644 src/assets/images/acorn@2x.png create mode 100644 src/assets/images/acorn@3x.png create mode 100644 src/assets/images/dewicatSticker.png create mode 100644 src/assets/images/dewicatSticker@2x.png create mode 100644 src/assets/images/dewicatSticker@3x.png create mode 100644 src/assets/images/keystone.png create mode 100644 src/assets/images/keystone@2x.png create mode 100644 src/assets/images/keystone@3x.png create mode 100644 src/assets/images/ledger.png create mode 100644 src/assets/images/ledger@2x.png create mode 100644 src/assets/images/ledger@3x.png create mode 100644 src/assets/images/lock.png create mode 100644 src/assets/images/lock@2x.png create mode 100644 src/assets/images/lock@3x.png create mode 100644 src/assets/images/login.png create mode 100644 src/assets/images/login@2x.png create mode 100644 src/assets/images/login@3x.png create mode 100644 src/assets/images/welcomeToHelium.png create mode 100644 src/assets/images/welcomeToHelium@2x.png create mode 100644 src/assets/images/welcomeToHelium@3x.png create mode 100644 src/assets/svgs/addExistingWallet.svg create mode 100644 src/assets/svgs/blueCircleLoader.svg create mode 100644 src/assets/svgs/commandLine.svg create mode 100644 src/assets/svgs/heliumLogoSticker.svg create mode 100644 src/assets/svgs/key.svg create mode 100644 src/assets/svgs/keystone.svg create mode 100644 src/assets/svgs/keystoneCircle.svg create mode 100644 src/assets/svgs/keystoneSelected.svg create mode 100644 src/assets/svgs/ledgerCircle.svg create mode 100644 src/assets/svgs/ledgerSelected.svg create mode 100644 src/assets/svgs/privateKey.svg create mode 100644 src/assets/svgs/scan.svg create mode 100644 src/assets/svgs/secretPhrase.svg rename src/{features/hotspot-onboarding => }/components/CheckButton.tsx (100%) rename src/{features/hotspot-onboarding => }/components/ForwardButton.tsx (100%) rename src/{features/hotspot-onboarding/components/Loading.tsx => components/LoadingButton.tsx} (100%) rename src/{features/hotspot-onboarding => }/components/WalletButton.tsx (94%) create mode 100644 src/features/accounts/WalletList.tsx rename src/{app/services/AccountsService => features/accounts}/accountServiceTypes.ts (100%) create mode 100644 src/features/onboarding/NewAccountScreen.tsx create mode 100644 src/features/onboarding/OnboardingSheet.tsx create mode 100644 src/features/onboarding/WelcomeToHeliumScreen.tsx delete mode 100644 src/features/onboarding/create/AccountCreateStartScreen.tsx create mode 100644 src/features/stickers/GestureHandler.tsx create mode 100644 src/features/stickers/MatrixHelpers.tsx create mode 100644 src/features/stickers/StickerContext.tsx create mode 100644 src/features/stickers/StickersPage.tsx create mode 100644 src/features/stickers/components/DeWiCatSticker.tsx create mode 100644 src/features/stickers/components/HeliumSticker.tsx create mode 100644 src/features/stickers/components/HotspotSticker.tsx create mode 100644 src/features/stickers/components/Sticker.ts diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 4a38b3db0..c3b2c39d0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1138,6 +1138,29 @@ PODS: - react-native-simple-toast (1.1.4): - React-Core - Toast (~> 4.0.0) + - react-native-skia (1.5.10): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React + - React-callinvoker + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - react-native-slider (4.5.2): - DoubleConversion - glog @@ -1628,6 +1651,7 @@ DEPENDENCIES: - react-native-randombytes (from `../node_modules/react-native-randombytes`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-simple-toast (from `../node_modules/react-native-simple-toast`) + - "react-native-skia (from `../node_modules/@shopify/react-native-skia`)" - "react-native-slider (from `../node_modules/@react-native-community/slider`)" - react-native-sodium (from `../node_modules/react-native-sodium`) - react-native-udp (from `../node_modules/react-native-udp`) @@ -1824,6 +1848,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-safe-area-context" react-native-simple-toast: :path: "../node_modules/react-native-simple-toast" + react-native-skia: + :path: "../node_modules/@shopify/react-native-skia" react-native-slider: :path: "../node_modules/@react-native-community/slider" react-native-sodium: @@ -1994,6 +2020,7 @@ SPEC CHECKSUMS: react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846 react-native-safe-area-context: b7daa1a8df36095a032dff095a1ea8963cb48371 react-native-simple-toast: 8ee5d23f0b92b935ab7434cdb65159ce12dfb4b7 + react-native-skia: 6e137273ac478a9c840b45ca53b7a248f9103fe2 react-native-slider: ce295d2bf830a7990af05b0bd70ab28c133e230c react-native-sodium: 274874541aa6bd00040f28c2e1e5118cbf113c0e react-native-udp: ff9d13e523f2b58e6bc5d4d32321ac60671b5dc9 diff --git a/package.json b/package.json index 14848b8f4..d4bfff57f 100644 --- a/package.json +++ b/package.json @@ -68,10 +68,10 @@ "@helium/wallet-link": "4.11.0", "@jup-ag/api": "^6.0.6", "@keystonehq/keystone-sdk": "^0.8.0", - "@ledgerhq/hw-app-solana": "7.0.13", - "@ledgerhq/react-native-hid": "6.30.0", - "@ledgerhq/react-native-hw-transport-ble": "6.29.5", - "@ledgerhq/types-devices": "^6.22.4", + "@ledgerhq/hw-app-solana": "7.2.4", + "@ledgerhq/react-native-hid": "6.32.4", + "@ledgerhq/react-native-hw-transport-ble": "6.29.4", + "@ledgerhq/types-devices": "^6.25.3", "@metaplex-foundation/js": "^0.19.5", "@metaplex-foundation/mpl-bubblegum": "0.6.0", "@metaplex-foundation/mpl-token-metadata": "2.10.0", @@ -93,6 +93,7 @@ "@react-navigation/stack": "6.2.2", "@reduxjs/toolkit": "1.9.1", "@rnmapbox/maps": "^10.1.31", + "@shopify/react-native-skia": "^1.5.10", "@shopify/restyle": "2.4.2", "@solana/spl-account-compression": "0.1.4", "@solana/spl-memo": "0.2.3", diff --git a/src/app/RootNavigator.tsx b/src/app/RootNavigator.tsx index 1535cdc85..ebc059786 100644 --- a/src/app/RootNavigator.tsx +++ b/src/app/RootNavigator.tsx @@ -15,7 +15,8 @@ import ImportPrivateKey from '@features/onboarding/import/ImportPrivateKey' import PaymentScreen from '@features/payment/PaymentScreen' import LinkWallet from '@features/txnDelegation/LinkWallet' import SignHotspot from '@features/txnDelegation/SignHotspot' -import { RootStackParamList } from './rootTypes' +import { useNavigation } from '@react-navigation/native' +import { RootNavigationProp, RootStackParamList } from './rootTypes' const screenOptions = { headerShown: false } as StackNavigationOptions @@ -23,6 +24,7 @@ const RootNavigator = () => { const { currentAccount } = useAccountStorage() const colors = useColors() const RootStack = createStackNavigator() + const rootNav = useNavigation() useEffect(() => { if (currentAccount) { @@ -36,6 +38,18 @@ const RootNavigator = () => { return currentAccount ? 'ServiceSheetNavigator' : 'OnboardingNavigator' }, [currentAccount]) + useEffect(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const firstRoute = (rootNav as any).getRootState().routes[0].key || '' + if (currentAccount && firstRoute.includes('OnboardingNavigator')) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ;(rootNav as any).reset({ + index: 0, + routes: [{ name: 'ServiceSheetNavigator' }], + }) + } + }, [currentAccount, initialRouteName, rootNav]) + return ( +export type YourWalletsServiceNavigationProp = + StackNavigationProp -const AccountsServiceStack = - createStackNavigator() +const YourWalletsService = () => { + const options = useMemo((): Array => { + return [ + { name: 'YourWalletsPage', Icon: Wallet, component: YourWalletsPage }, + { + name: 'CreateSeedPhrasePage', + Icon: Add, + component: CreateSeedPhrasePage, + }, + { + name: 'AddExistingWalletPage', + Icon: AddExistingWallet, + component: AddExistingWalletPage, + }, + { + name: 'PairLedgerPage', + Icon: Ledger, + IconActive: LedgerSelected, + component: PairLedgerPage, + }, + { + name: 'ConnectKeystonePage', + Icon: Keystone, + IconActive: KeystoneSelected, + component: ConnectKeystonePage, + }, + ] + }, []) -const AccountsService = () => { - const colors = useColors() - const screenOptions: StackNavigationOptions = useMemo( - () => ({ - headerShown: false, - animationEnabled: false, - cardStyle: { backgroundColor: colors.primaryBackground }, - }), - [colors], - ) - return ( - - - - - - - - ) + return } -export default AccountsService +export default YourWalletsService diff --git a/src/app/services/AccountsService/pages/AddExistingWalletPage.tsx b/src/app/services/AccountsService/pages/AddExistingWalletPage.tsx new file mode 100644 index 000000000..40d146e2f --- /dev/null +++ b/src/app/services/AccountsService/pages/AddExistingWalletPage.tsx @@ -0,0 +1,186 @@ +import Box from '@components/Box' +import ScrollBox from '@components/ScrollBox' +import { NavBarHeight } from '@components/ServiceNavBar' +import { useSpacing, useColors } from '@config/theme/themeHooks' +import Text from '@components/Text' +import React, { useCallback, useMemo, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { StyleProp, ViewStyle } from 'react-native' +import SecretPhrase from '@assets/svgs/secretPhrase.svg' +import PrivateKey from '@assets/svgs/privateKey.svg' +import CarotRight from '@assets/svgs/carot-right.svg' +import CommandLine from '@assets/svgs/commandLine.svg' +import TouchableContainer from '@components/TouchableContainer' +import { + createStackNavigator, + StackNavigationOptions, + StackNavigationProp, +} from '@react-navigation/stack' +import { useTheme } from '@shopify/restyle' +import ImportAccountNavigator from '@features/onboarding/import/ImportAccountNavigator' +import { + OnboardingSheetRef, + FlowType, + OnboardingSheetWrapper, +} from '@features/onboarding/OnboardingSheet' + +const AddExistingWalletPage = () => { + const { t } = useTranslation() + const spacing = useSpacing() + const colors = useColors() + const { bottom } = useSafeAreaInsets() + const onboardingSheetRef = useRef(null) + + const onAddExistingWallet = useCallback( + (flowType: FlowType) => () => { + onboardingSheetRef.current?.show(flowType) + }, + [onboardingSheetRef], + ) + + const contentContainerStyle = useMemo( + () => ({ + paddingHorizontal: spacing['2xl'], + flex: 1, + justifyContent: 'center', + gap: spacing.xl, + paddingBottom: bottom + spacing['2xl'] + NavBarHeight, + }), + [spacing, bottom], + ) + + return ( + } + > + + {t('AddExistingWalletPage.title')} + + + {t('AddExistingWalletPage.subtitle')} + + + + + + + {t('AddExistingWalletPage.secretPhrase')} + + + {t('AddExistingWalletPage.twelveOrTwentyFourWords')} + + + + + + + + + {t('AddExistingWalletPage.privateKey')} + + + {t('AddExistingWalletPage.aStringOfCharacters')} + + + + + + + + + {t('AddExistingWalletPage.commandLine')} + + + {t('AddExistingWalletPage.scanCli')} + + + + + + + + ) +} + +export type AddExistingWalletStackParamList = { + AddExistingWalletPage: undefined + ImportAccount: + | undefined + | { + screen: 'AccountImportScreen' + params: { + restoringAccount?: boolean + accountAddress?: string + } + } +} + +export type AddExistingWalletNavigationProp = + StackNavigationProp + +const AddExistingWalletStack = + createStackNavigator() + +const AddExistingWalletNavigator = () => { + const { colors } = useTheme() + const screenOptions = useMemo( + () => + ({ + headerShown: false, + cardStyle: { + backgroundColor: colors.primaryBackground, + }, + } as StackNavigationOptions), + [colors], + ) + return ( + + + + + ) +} + +export default AddExistingWalletNavigator diff --git a/src/app/services/AccountsService/pages/ConnectKeystonePage.tsx b/src/app/services/AccountsService/pages/ConnectKeystonePage.tsx new file mode 100644 index 000000000..5027d6187 --- /dev/null +++ b/src/app/services/AccountsService/pages/ConnectKeystonePage.tsx @@ -0,0 +1,72 @@ +import ButtonPressable from '@components/ButtonPressable' +import ImageBox from '@components/ImageBox' +import ScrollBox from '@components/ScrollBox' +import Text from '@components/Text' +import { useColors, useSpacing } from '@config/theme/themeHooks' +import React, { useCallback, useMemo, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import Scan from '@assets/svgs/scan.svg' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { NavBarHeight } from '@components/ServiceNavBar' +import { StyleProp, ViewStyle } from 'react-native' +import { + OnboardingSheetRef, + OnboardingSheetWrapper, +} from '@features/onboarding/OnboardingSheet' + +const ConnectKeystonePage = () => { + const { t } = useTranslation() + const spacing = useSpacing() + const colors = useColors() + const { bottom } = useSafeAreaInsets() + const onboardingSheetRef = useRef(null) + const contentContainerStyle = useMemo( + () => ({ + paddingHorizontal: spacing['2xl'], + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingBottom: bottom + spacing['2xl'] + NavBarHeight, + }), + [spacing, bottom], + ) + + const openOnboarding = useCallback(() => { + onboardingSheetRef.current?.show('keystone') + }, [onboardingSheetRef]) + + return ( + } + > + + + {t('ConnectKeystonePage.title')} + + + {t('ConnectKeystonePage.subtitle')} + + + } + onPress={openOnboarding} + /> + + + ) +} + +export default ConnectKeystonePage diff --git a/src/app/services/AccountsService/pages/CreateSeedPhrasePage.tsx b/src/app/services/AccountsService/pages/CreateSeedPhrasePage.tsx new file mode 100644 index 000000000..4b6251109 --- /dev/null +++ b/src/app/services/AccountsService/pages/CreateSeedPhrasePage.tsx @@ -0,0 +1,82 @@ +import Box from '@components/Box' +import ButtonPressable from '@components/ButtonPressable' +import ImageBox from '@components/ImageBox' +import ScrollBox from '@components/ScrollBox' +import Text from '@components/Text' +import { useColors, useSpacing } from '@config/theme/themeHooks' +import React, { useCallback, useMemo, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import { StyleProp, ViewStyle } from 'react-native' +import Add from '@assets/svgs/add.svg' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { NavBarHeight } from '@components/ServiceNavBar' +import { + OnboardingSheetRef, + OnboardingSheetWrapper, +} from '@features/onboarding/OnboardingSheet' + +const CreateSeedPhrasePage = () => { + const { t } = useTranslation() + const spacing = useSpacing() + const colors = useColors() + const { bottom } = useSafeAreaInsets() + const onboardingSheetRef = useRef(null) + + const contentContainerStyle = useMemo( + () => ({ + paddingHorizontal: spacing['2xl'], + flex: 1, + justifyContent: 'center', + alignItems: 'center', + gap: spacing.xl, + paddingBottom: bottom + spacing['2xl'] + NavBarHeight, + }), + [spacing, bottom], + ) + + const showBottomSheet = useCallback(() => { + onboardingSheetRef.current?.show('create-account') + }, [onboardingSheetRef]) + + return ( + } + > + + + {t('CreateSeedPhrasePage.title')} + + + {t('CreateSeedPhrasePage.subtitle')} + + + + {t('CreateSeedPhrasePage.disclaimer')} + + + + + } + onPress={showBottomSheet} + /> + + + ) +} + +export default CreateSeedPhrasePage diff --git a/src/app/services/AccountsService/pages/PairLedgerPage.tsx b/src/app/services/AccountsService/pages/PairLedgerPage.tsx new file mode 100644 index 000000000..1b1b8c921 --- /dev/null +++ b/src/app/services/AccountsService/pages/PairLedgerPage.tsx @@ -0,0 +1,77 @@ +import ButtonPressable from '@components/ButtonPressable' +import ImageBox from '@components/ImageBox' +import ScrollBox from '@components/ScrollBox' +import Text from '@components/Text' +import { useColors, useSpacing } from '@config/theme/themeHooks' +import React, { useCallback, useMemo, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import Bluetooth from '@assets/svgs/bluetooth.svg' +import { useSafeAreaInsets } from 'react-native-safe-area-context' +import { NavBarHeight } from '@components/ServiceNavBar' +import { StyleProp, ViewStyle } from 'react-native' +import { + OnboardingSheetRef, + OnboardingSheetWrapper, +} from '@features/onboarding/OnboardingSheet' + +const PairLedgerPage = () => { + const { t } = useTranslation() + const spacing = useSpacing() + const colors = useColors() + const onboardingSheetRef = useRef(null) + const { bottom } = useSafeAreaInsets() + + const contentContainerStyle = useMemo( + () => ({ + paddingHorizontal: spacing['2xl'], + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingBottom: bottom + spacing['2xl'] + NavBarHeight, + }), + [spacing, bottom], + ) + + const openOnboardingSheet = useCallback(() => { + onboardingSheetRef.current?.show('ledger') + }, [onboardingSheetRef]) + + return ( + } + > + + + {t('PairLedgerPage.title')} + + + {t('PairLedgerPage.subtitle')} + + + } + onPress={openOnboardingSheet} + /> + + + ) +} + +export default PairLedgerPage diff --git a/src/app/services/AccountsService/pages/YourWalletsPage.tsx b/src/app/services/AccountsService/pages/YourWalletsPage.tsx index 395af534b..f2e4f9b93 100644 --- a/src/app/services/AccountsService/pages/YourWalletsPage.tsx +++ b/src/app/services/AccountsService/pages/YourWalletsPage.tsx @@ -1,418 +1,74 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react' -import Text from '@components/Text' -import { useTranslation } from 'react-i18next' -import { ReAnimatedBox } from '@components/AnimatedBox' -import { FadeIn } from 'react-native-reanimated' -import Box from '@components/Box' -import { Image, SectionList } from 'react-native' -import { useAccountStorage } from '@config/storage/AccountStorageProvider' -import { NetTypes } from '@helium/address' -import { CSAccount } from '@config/storage/cloudStorage' -import TouchableContainer from '@components/TouchableContainer' -import AccountIcon from '@components/AccountIcon' -import { ellipsizeAddress } from '@utils/accountUtils' -import { useColors, useSpacing } from '@config/theme/themeHooks' -import SmallAdd from '@assets/svgs/smallAdd.svg' -import BigAdd from '@assets/svgs/bigAdd.svg' -import Checkmark from '@assets/svgs/checkmark.svg' -import { useNavigation } from '@react-navigation/native' +import React, { useMemo } from 'react' import { - HELIUM_DERIVATION, - keypairFromSeed, - solanaDerivation, -} from '@hooks/useDerivationAccounts' -import { getSecureAccount } from '@config/storage/secureStorage' -import * as bip39 from 'bip39' -import { useOnboarding } from '@features/onboarding/OnboardingProvider' -import Toast from 'react-native-simple-toast' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import TouchableOpacityBox from '@components/TouchableOpacityBox' -import useLayoutHeight from '@hooks/useLayoutHeight' -import { ServiceSheetNavigationProp } from 'src/app/services/serviceSheetTypes' -import CircleLoader from '@components/CircleLoader' -import ScrollBox from '@components/ScrollBox' -import { AccountsServiceNavigationProp } from '../accountServiceTypes' - -const YourWalletsPage = () => { - const { t } = useTranslation() - const [addingSubAccount, setAddingSubAccount] = useState(false) - const [switchingAccounts, setSwitchingAccounts] = useState< - CSAccount | undefined - >() - const spacing = useSpacing() - const { setOnboardingData, onboardingData } = useOnboarding() - const colors = useColors() - const navigation = useNavigation< - AccountsServiceNavigationProp & ServiceSheetNavigationProp - >() - const { sortedAccounts, currentAccount, setCurrentAccount, accounts } = - useAccountStorage() - const { bottom } = useSafeAreaInsets() - const [footerHeight, setFooterHeight] = useLayoutHeight() - - const handleAddSub = useCallback( - async (acc: CSAccount) => { - setAddingSubAccount(true) - try { - if (!currentAccount) { - throw new Error('No current account') - } - const storage = await getSecureAccount(currentAccount.address) - const seed = bip39.mnemonicToSeedSync( - storage?.mnemonic?.join(' ') || '', - '', - ) - - if (!seed || !acc?.derivationPath) { - throw new Error('Missing seed or derivation path') + StackNavigationOptions, + StackNavigationProp, + createStackNavigator, +} from '@react-navigation/stack' +import { useColors } from '@config/theme/themeHooks' +import AddNewAccountNavigator from '@features/home/addNewAccount/AddNewAccountNavigator' +import AccountAssignScreen from '@features/onboarding/AccountAssignScreen' +import { RouteAccount } from '@features/onboarding/create/createAccountNavTypes' +import ImportAccountNavigator from '@features/onboarding/import/ImportAccountNavigator' +import KeystoneNavigator from '@features/keystone/KeystoneNavigator' +import WalletList from '@features/accounts/WalletList' + +export type AccountsServiceStackParamList = { + WalletList: undefined + AddNewAccountNavigator: undefined + AccountAssignScreen: undefined | RouteAccount + ReImportAccountNavigator: + | undefined + | { + screen: 'AccountImportScreen' + params: { + restoringAccount?: boolean + accountAddress?: string } - const currentPath = acc.derivationPath - const takenAddresses = new Set( - Object.values(accounts || {}).map((a) => a.solanaAddress), - ) - let currentAccountNum = - currentPath === HELIUM_DERIVATION - ? 0 - : Number(currentPath.split('/')[3].replace("'", '')) + 1 - let derivationPath = solanaDerivation(currentAccountNum, 0) - let keypair = await keypairFromSeed(seed, derivationPath) - while ( - currentAccountNum < 100 && - (!keypair || takenAddresses.has(keypair.publicKey.toBase58())) - ) { - currentAccountNum += 1 - derivationPath = solanaDerivation(currentAccountNum, 0) - keypair = await keypairFromSeed(seed, derivationPath) - } - if (currentAccountNum >= 100) { - throw new Error('More than 100 accounts are not supported') - } - if (keypair) { - const words = (await getSecureAccount(acc.address))?.mnemonic - setOnboardingData({ - ...onboardingData, - words, - paths: [ - { - derivationPath, - keypair, - }, - ], - }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - navigation.navigate('AccountAssignScreen', { - words, - }) - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - } catch (e: any) { - Toast.show(e.message || e.toString()) - } finally { - setAddingSubAccount(false) } - }, - [accounts, currentAccount, navigation, onboardingData, setOnboardingData], - ) - - const filteredAccounts = useMemo(() => { - const grouped = sortedAccounts - .filter((a) => a.netType !== NetTypes.TESTNET) - .reduce((acc, account) => { - acc[account.mnemonicHash || 'none'] = [ - ...(acc[account.mnemonicHash || 'none'] || []), - account, - ] - return acc - }, {} as { [key: string]: CSAccount[] }) - - const { none, ...rest } = grouped - const ret = Object.values(rest).map((accs, index) => ({ - title: `Seed Phrase ${index + 1}`, - data: accs, - })) - if (none) { - ret.push({ - title: 'Private Keys', - data: none, - }) + KeystoneNavigator: { + screen: 'KeystoneNavigator' + params: { + screen: string } + } +} - return ret - }, [sortedAccounts]) - - const Header = useCallback(() => { - return ( - - - - {t('accountsService.title')} - - - ) - }, [t]) - - const keyExtractor = useCallback((item) => item.address, []) - - const handleAccountChange = useCallback( - (item: CSAccount) => () => { - setSwitchingAccounts(item) - }, - [setSwitchingAccounts], - ) - - useEffect(() => { - if (!switchingAccounts) return - - // Need to wait for a UI tick to allow the animation to finish - setTimeout(() => { - setCurrentAccount(switchingAccounts) - navigation.replace('WalletService') - }, 0) - }, [switchingAccounts, setCurrentAccount, navigation]) - - const renderItem = useCallback( - ({ - item, - index, - section, - }: { - // eslint-disable-next-line react/no-unused-prop-types - index: number - // eslint-disable-next-line react/no-unused-prop-types - item: CSAccount - // eslint-disable-next-line react/no-unused-prop-types - section: { - title: string - data: CSAccount[] - } - }) => { - const { data } = section - - const isSelected = item.address === currentAccount?.address - const showBottomBorder = - (data[index] && - data[index].mnemonicHash !== currentAccount?.mnemonicHash) || - section.title === 'Private Keys' - const isLast = index === data.length - 1 - const accountAddress = item?.solanaAddress - const borderTopStartRadius = index === 0 ? '2xl' : undefined - const borderTopEndRadius = index === 0 ? '2xl' : undefined - const borderBottomStartRadius = - index === data.length - 1 && showBottomBorder ? '2xl' : undefined - const borderBottomEndRadius = - index === data.length - 1 && showBottomBorder ? '2xl' : undefined - - return ( - - - - - {item.alias} - - - {ellipsizeAddress(accountAddress || '', { - numChars: 4, - })} - - - {isSelected && ( - - )} - - ) - }, - [currentAccount, handleAccountChange, colors], - ) - - const handleNetTypeChange = useCallback( - (nextNetType?: NetTypes.NetType) => { - setOnboardingData((prev) => { - let netType = nextNetType - if (netType === undefined) { - netType = - prev.netType === NetTypes.MAINNET - ? NetTypes.TESTNET - : NetTypes.MAINNET - } - return { ...prev, netType } - }) - }, - [setOnboardingData], - ) - - const handleAddNew = useCallback(() => { - handleNetTypeChange(NetTypes.MAINNET) - navigation.navigate('AddNewAccountNavigator') - }, [handleNetTypeChange, navigation]) - - const renderSectionHeader = useCallback( - ({ section: { title, data } }) => { - const firstSection = filteredAccounts[0].title === title - - return ( - - - {title} - - - ) - }, - [filteredAccounts, currentAccount], - ) - - const renderSectionFooter = useCallback( - ({ section: { data } }) => { - return ( - - ) - }, - [handleAddSub, currentAccount?.mnemonicHash], - ) - - const Footer = useCallback(() => { - return ( - - {/** TODO: Bring back when stickers page is added */} - {/* - - */} - - - - - - ) - }, [bottom, handleAddNew, setFooterHeight]) +export type AccountsServiceNavigationProp = + StackNavigationProp - if (switchingAccounts || addingSubAccount) { - return ( - - - {addingSubAccount - ? t('accountsService.addingSubAccount') - : t('accountsService.switchingAccounts')} - - - {t('accountsService.pleaseBePatient')} - - - - - - ) - } +const AccountsServiceStack = + createStackNavigator() - return ( - - - - -