Merge pull request #7284 from BlueWallet/import-ts2

feat: convert ImportCustomDerivationPath and ImportSpeed screens into Typescript
This commit is contained in:
GLaDOS 2024-11-10 14:54:44 +00:00 committed by GitHub
commit 0211ff3140
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 104 additions and 71 deletions

View File

@ -67,7 +67,11 @@ const AddWalletStack = () => {
title: loc.wallets.add_title,
})(theme)}
/>
<Stack.Screen name="ImportCustomDerivationPath" component={ImportCustomDerivationPathComponent} />
<Stack.Screen
name="ImportCustomDerivationPath"
component={ImportCustomDerivationPathComponent}
options={navigationStyle({ statusBarStyle: 'light', title: loc.wallets.import_derivation_title })(theme)}
/>
<Stack.Screen
name="ImportWallet"
component={ImportWalletComponent}

View File

@ -4,9 +4,9 @@ import { LazyLoadingIndicator } from './LazyLoadingIndicator';
// Define lazy imports
const WalletsAdd = lazy(() => import('../screen/wallets/Add'));
const ImportCustomDerivationPath = lazy(() => import('../screen/wallets/importCustomDerivationPath'));
const ImportCustomDerivationPath = lazy(() => import('../screen/wallets/ImportCustomDerivationPath'));
const ImportWalletDiscovery = lazy(() => import('../screen/wallets/ImportWalletDiscovery'));
const ImportSpeed = lazy(() => import('../screen/wallets/importSpeed'));
const ImportSpeed = lazy(() => import('../screen/wallets/ImportSpeed'));
const ImportWallet = lazy(() => import('../screen/wallets/import'));
const PleaseBackup = lazy(() => import('../screen/wallets/PleaseBackup'));
const PleaseBackupLNDHub = lazy(() => import('../screen/wallets/pleaseBackupLNDHub'));

View File

@ -140,9 +140,7 @@ const LnurlPay: React.FC = () => {
}
const bolt11payload = await _LN.requestBolt11FromLnurlPayService(amountSats, comment);
// @ts-ignore fixme after lnurl.js converted to ts
await wallet.payInvoice(bolt11payload.pr);
// @ts-ignore fixme after lnurl.js converted to ts
const decoded = wallet.decodeInvoice(bolt11payload.pr);
setPayButtonDisabled(false);

View File

@ -1,36 +1,48 @@
import { useNavigation, useRoute } from '@react-navigation/native';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { FlatList, StyleSheet, TextInput, View } from 'react-native';
import debounce from '../../blue_modules/debounce';
import { BlueFormLabel, BlueSpacing20, BlueTextCentered } from '../../BlueComponents';
import { HDLegacyP2PKHWallet, HDSegwitBech32Wallet, HDSegwitP2SHWallet } from '../../class';
import { validateBip32 } from '../../class/wallet-import';
import { TWallet } from '../../class/wallets/types';
import Button from '../../components/Button';
import navigationStyle from '../../components/navigationStyle';
import SafeArea from '../../components/SafeArea';
import { useTheme } from '../../components/themes';
import WalletToImport from '../../components/WalletToImport';
import loc from '../../loc';
import { useStorage } from '../../hooks/context/useStorage';
import loc from '../../loc';
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
type RouteProps = RouteProp<AddWalletStackParamList, 'ImportCustomDerivationPath'>;
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'ImportCustomDerivationPath'>;
const ListEmptyComponent: React.FC = () => <BlueTextCentered>{loc.wallets.import_wrong_path}</BlueTextCentered>;
const WRONG_PATH = 'WRONG_PATH';
const WALLET_FOUND = 'WALLET_FOUND';
const WALLET_NOTFOUND = 'WALLET_NOTFOUND';
const WALLET_UNKNOWN = 'WALLET_UNKNOWN';
enum STATUS {
WALLET_FOUND = 'WALLET_FOUND',
WALLET_NOTFOUND = 'WALLET_NOTFOUND',
WALLET_UNKNOWN = 'WALLET_UNKNOWN',
}
type TWalletsByType = { [type: string]: TWallet };
type TWalletsByPath = { [path: string]: TWalletsByType | 'WRONG_PATH' };
const ListEmptyComponent = () => <BlueTextCentered>{loc.wallets.import_wrong_path}</BlueTextCentered>;
type TUsedByType = { [type: string]: STATUS };
type TUsedByPath = { [path: string]: TUsedByType };
const ImportCustomDerivationPath = () => {
const navigation = useNavigation();
type TItem = [type: string, typeReadable: string, STATUS | undefined];
const ImportCustomDerivationPath: React.FC = () => {
const navigation = useNavigation<NavigationProp>();
const { colors } = useTheme();
const route = useRoute();
const importText = route.params.importText;
const password = route.params.password;
const { importText, password } = useRoute<RouteProps>().params;
const { addAndSaveWallet } = useStorage();
const [path, setPath] = useState("m/84'/0'/0'");
const [wallets, setWallets] = useState({});
const [used, setUsed] = useState({});
const [selected, setSelected] = useState();
const [path, setPath] = useState<string>("m/84'/0'/0'");
const [wallets, setWallets] = useState<TWalletsByPath>({});
const [used, setUsed] = useState<TUsedByPath>({});
const [selected, setSelected] = useState<string>('');
const importing = useRef(false);
const debouncedSavePath = useRef(
@ -41,25 +53,32 @@ const ImportCustomDerivationPath = () => {
}
// create wallets
const newWallets = {};
const newWallets: { [type: string]: TWallet } = {};
for (const Wallet of [HDLegacyP2PKHWallet, HDSegwitP2SHWallet, HDSegwitBech32Wallet]) {
const wallet = new Wallet();
wallet.setSecret(importText);
wallet.setPassphrase(password);
if (password) {
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)));
const promises = Object.values(newWallets).map(w => {
return w.wasEverUsed().then(v => {
const status = v ? STATUS.WALLET_FOUND : STATUS.WALLET_NOTFOUND;
setUsed(u => ({ ...u, [newPath]: { ...u[newPath], [w.type]: status } }));
});
});
try {
await Promise.all(promises); // wait for all promises to be resolved
await Promise.all(promises);
} catch (e) {
Object.values(newWallets).forEach(w => (res[w.type] = WALLET_UNKNOWN));
Object.values(newWallets).forEach(w => {
setUsed(u => ({ ...u, [newPath]: { ...u[newPath], [w.type]: STATUS.WALLET_UNKNOWN } }));
});
}
setUsed(u => ({ ...u, [newPath]: res }));
}, 500),
);
useEffect(() => {
@ -67,7 +86,7 @@ const ImportCustomDerivationPath = () => {
debouncedSavePath.current(path);
}, [path, wallets]);
const items = useMemo(() => {
const items: TItem[] = useMemo(() => {
if (wallets[path] === WRONG_PATH) return [];
return [
[HDLegacyP2PKHWallet.type, HDLegacyP2PKHWallet.typeReadable, used[path]?.[HDLegacyP2PKHWallet.type]],
@ -90,25 +109,26 @@ const ImportCustomDerivationPath = () => {
},
});
const saveWallet = type => {
const saveWallet = (type: string) => {
if (importing.current) return;
importing.current = true;
const wallet = wallets[path][type];
addAndSaveWallet(wallet);
if (wallets[path] === WRONG_PATH) return;
addAndSaveWallet(wallets[path][type]);
// @ts-ignore: Navigation
navigation.getParent().pop();
};
const renderItem = ({ item }) => {
const renderItem = ({ item }: { item: TItem }) => {
const [type, title, found] = item;
let subtitle;
switch (found) {
case WALLET_FOUND:
case STATUS.WALLET_FOUND:
subtitle = loc.wallets.import_derivation_found;
break;
case WALLET_NOTFOUND:
case STATUS.WALLET_NOTFOUND:
subtitle = loc.wallets.import_derivation_found_not;
break;
case WALLET_UNKNOWN:
case STATUS.WALLET_UNKNOWN:
subtitle = loc.wallets.import_derivation_unknown;
break;
default:
@ -118,6 +138,8 @@ const ImportCustomDerivationPath = () => {
return <WalletToImport key={type} title={title} subtitle={subtitle} active={selected === type} onPress={() => setSelected(type)} />;
};
const disabled = wallets[path] === WRONG_PATH || wallets[path]?.[selected] === undefined;
return (
<SafeArea style={[styles.root, stylesHook.root]}>
<BlueSpacing20 />
@ -141,12 +163,7 @@ const ImportCustomDerivationPath = () => {
<View style={[styles.center, stylesHook.center]}>
<View style={styles.buttonContainer}>
<Button
disabled={wallets[path]?.[selected] === undefined}
title={loc.wallets.import_do_import}
testID="ImportButton"
onPress={() => saveWallet(selected)}
/>
<Button disabled={disabled} title={loc.wallets.import_do_import} testID="ImportButton" onPress={() => saveWallet(selected)} />
</View>
</View>
</SafeArea>
@ -183,10 +200,4 @@ const styles = StyleSheet.create({
},
});
ImportCustomDerivationPath.navigationOptions = navigationStyle({}, opts => ({
...opts,
title: loc.wallets.import_derivation_title,
statusBarStyle: 'light',
}));
export default ImportCustomDerivationPath;

View File

@ -1,6 +1,7 @@
import { useNavigation } from '@react-navigation/native';
import React, { useState } from 'react';
import { ActivityIndicator, StyleSheet, TextInput, View } from 'react-native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { BlueFormLabel, BlueFormMultiInput, BlueSpacing20 } from '../../BlueComponents';
import { HDSegwitBech32Wallet, WatchOnlyWallet } from '../../class';
import presentAlert from '../../components/Alert';
@ -8,14 +9,17 @@ import Button from '../../components/Button';
import SafeArea from '../../components/SafeArea';
import { useTheme } from '../../components/themes';
import { useStorage } from '../../hooks/context/useStorage';
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'ImportSpeed'>;
const WalletsImportWallet = () => {
const navigation = useNavigation();
const navigation = useNavigation<NavigationProp>();
const { colors } = useTheme();
const [loading, setLoading] = useState(false);
const [importText, setImportText] = useState();
const [walletType, setWalletType] = useState();
const [passphrase, setPassphrase] = useState();
const [loading, setLoading] = useState<boolean>(false);
const [importText, setImportText] = useState<string>('');
const [walletType, setWalletType] = useState<string>('');
const [passphrase, setPassphrase] = useState<string>('');
const { addAndSaveWallet } = useStorage();
const styles = StyleSheet.create({
@ -58,13 +62,21 @@ const WalletsImportWallet = () => {
break;
}
if (!WalletClass) {
throw new Error('Invalid wallet type');
}
const wallet = new WalletClass();
wallet.setSecret(importText);
if (passphrase) wallet.setPassphrase(passphrase);
// check wallet is type of HDSegwitBech32Wallet
if (passphrase && wallet instanceof HDSegwitBech32Wallet) {
wallet.setPassphrase(passphrase);
}
await wallet.fetchBalance();
// @ts-ignore: navigation
navigation.getParent().pop();
addAndSaveWallet(wallet);
} catch (e) {
} catch (e: any) {
presentAlert({ message: e.message });
} finally {
setLoading(false);

View File

@ -1,18 +1,23 @@
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import React, { useCallback, useEffect } from 'react';
import { useNavigation, useRoute } from '@react-navigation/native';
import { BackHandler, I18nManager, ScrollView, StyleSheet, Text, View } from 'react-native';
import { disallowScreenshot } from 'react-native-screen-capture';
import Button from '../../components/Button';
import { useTheme } from '../../components/themes';
import { disallowScreenshot } from 'react-native-screen-capture';
import loc from '../../loc';
import { useStorage } from '../../hooks/context/useStorage';
import { useSettings } from '../../hooks/context/useSettings';
import { useStorage } from '../../hooks/context/useStorage';
import loc from '../../loc';
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
type RouteProps = RouteProp<AddWalletStackParamList, 'PleaseBackup'>;
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'PleaseBackup'>;
const PleaseBackup: React.FC = () => {
const { wallets } = useStorage();
const { walletID } = useRoute().params as { walletID: string };
const { walletID } = useRoute<RouteProps>().params;
const wallet = wallets.find(w => w.getID() === walletID);
const navigation = useNavigation();
const navigation = useNavigation<NavigationProp>();
const { isPrivacyBlurEnabled } = useSettings();
const { colors } = useTheme();

View File

@ -1,6 +1,8 @@
import { useNavigation, useRoute } from '@react-navigation/native';
import BN from 'bignumber.js';
import React, { useEffect, useReducer, useState } from 'react';
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { Icon } from '@rneui/themed';
import BN from 'bignumber.js';
import {
Alert,
Dimensions,
@ -13,15 +15,18 @@ import {
View,
useWindowDimensions,
} from 'react-native';
import { Icon } from '@rneui/themed';
import { BlueSpacing20 } from '../../BlueComponents';
import { randomBytes } from '../../class/rng';
import { FButton, FContainer } from '../../components/FloatButtons';
import SafeArea from '../../components/SafeArea';
import { Tabs } from '../../components/Tabs';
import { BlueCurrentTheme, useTheme } from '../../components/themes';
import loc from '../../loc';
import { randomBytes } from '../../class/rng';
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
type RouteProps = RouteProp<AddWalletStackParamList, 'ProvideEntropy'>;
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'ProvideEntropy'>;
export enum EActionType {
push = 'push',
@ -252,11 +257,10 @@ const D20Tab = ({ active }: { active: boolean }) => {
return <Icon name="dice-d20" type="font-awesome-5" color={active ? colors.buttonAlternativeTextColor : colors.buttonBackgroundColor} />;
};
const Entropy = () => {
const ProvideEntropy = () => {
const [entropy, dispatch] = useReducer(eReducer, initialState);
// @ts-ignore: navigation is not typed yet
const { onGenerated, words } = useRoute().params;
const navigation = useNavigation();
const { onGenerated, words } = useRoute<RouteProps>().params;
const navigation = useNavigation<NavigationProp>();
const [tab, setTab] = useState(1);
const [show, setShow] = useState(false);
const { colors } = useTheme();
@ -319,7 +323,6 @@ const Entropy = () => {
buf = Buffer.concat([buf, random], bufLength);
}
// @ts-ignore: navigation is not typed yet
navigation.pop();
onGenerated(buf);
},
@ -432,4 +435,4 @@ const styles = StyleSheet.create({
},
});
export default Entropy;
export default ProvideEntropy;