import React, { useContext, useEffect, useState, useRef, useMemo } from 'react'; import { FlatList, StyleSheet, TextInput, View } from 'react-native'; import { useNavigation, useRoute } from '@react-navigation/native'; import { BlueFormLabel, BlueSpacing20, BlueTextCentered } from '../../BlueComponents'; import navigationStyle from '../../components/navigationStyle'; import WalletToImport from '../../components/WalletToImport'; import loc from '../../loc'; import { BlueStorageContext } from '../../blue_modules/storage-context'; import { HDLegacyP2PKHWallet, HDSegwitP2SHWallet, HDSegwitBech32Wallet } from '../../class'; import { validateBip32 } from '../../class/wallet-import'; import debounce from '../../blue_modules/debounce'; import { useTheme } from '../../components/themes'; import Button from '../../components/Button'; import SafeArea from '../../components/SafeArea'; const WRONG_PATH = 'WRONG_PATH'; const WALLET_FOUND = 'WALLET_FOUND'; const WALLET_NOTFOUND = 'WALLET_NOTFOUND'; const WALLET_UNKNOWN = 'WALLET_UNKNOWN'; const ListEmptyComponent = () => {loc.wallets.import_wrong_path}; const ImportCustomDerivationPath = () => { const navigation = useNavigation(); const { colors } = useTheme(); const route = useRoute(); const importText = route.params.importText; const password = route.params.password; const { addAndSaveWallet } = useContext(BlueStorageContext); const [path, setPath] = useState("m/84'/0'/0'"); const [wallets, setWallets] = useState({}); const [used, setUsed] = useState({}); const [selected, setSelected] = useState(); const importing = useRef(false); const debouncedSavePath = useRef( debounce(async newPath => { if (!validateBip32(newPath)) { setWallets(ws => ({ ...ws, [newPath]: WRONG_PATH })); return; } // create wallets const newWallets = {}; for (const Wallet of [HDLegacyP2PKHWallet, HDSegwitP2SHWallet, HDSegwitBech32Wallet]) { const wallet = new Wallet(); wallet.setSecret(importText); wallet.setPassphrase(password); wallet.setDerivationPath(newPath); newWallets[Wallet.type] = wallet; } setWallets(ws => ({ ...ws, [newPath]: newWallets })); // discover was they ever used const res = {}; const promises = Object.values(newWallets).map(w => w.wasEverUsed().then(v => (res[w.type] = v ? WALLET_FOUND : WALLET_NOTFOUND))); try { await Promise.all(promises); // wait for all promises to be resolved } catch (e) { Object.values(newWallets).forEach(w => (res[w.type] = WALLET_UNKNOWN)); } setUsed(u => ({ ...u, [newPath]: res })); }, 500), ); useEffect(() => { if (path in wallets) return; debouncedSavePath.current(path); }, [path, wallets]); const items = useMemo(() => { if (wallets[path] === WRONG_PATH) return []; return [ [HDLegacyP2PKHWallet.type, HDLegacyP2PKHWallet.typeReadable, used[path]?.[HDLegacyP2PKHWallet.type]], [HDSegwitP2SHWallet.type, HDSegwitP2SHWallet.typeReadable, used[path]?.[HDSegwitP2SHWallet.type]], [HDSegwitBech32Wallet.type, HDSegwitBech32Wallet.typeReadable, used[path]?.[HDSegwitBech32Wallet.type]], ]; }, [path, used, wallets]); const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.elevated, }, center: { backgroundColor: colors.elevated, }, pathInput: { borderColor: colors.formBorder, borderBottomColor: colors.formBorder, backgroundColor: colors.inputBackgroundColor, }, }); const saveWallet = type => { if (importing.current) return; importing.current = true; const wallet = wallets[path][type]; addAndSaveWallet(wallet); navigation.getParent().pop(); }; const renderItem = ({ item }) => { const [type, title, found] = item; let subtitle; switch (found) { case WALLET_FOUND: subtitle = loc.wallets.import_derivation_found; break; case WALLET_NOTFOUND: subtitle = loc.wallets.import_derivation_found_not; break; case WALLET_UNKNOWN: subtitle = loc.wallets.import_derivation_unknown; break; default: subtitle = loc.wallets.import_derivation_loading; } return setSelected(type)} />; }; return ( {loc.wallets.import_derivation_subtitle} path + w[0]} renderItem={renderItem} contentContainerStyle={styles.flatListContainer} ListEmptyComponent={ListEmptyComponent} />