BlueWallet/screen/wallets/addMultisigStep2.js

817 lines
27 KiB
JavaScript
Raw Normal View History

import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2024-07-23 13:44:04 -04:00
import { useFocusEffect, useRoute } from '@react-navigation/native';
import {
ActivityIndicator,
FlatList,
2021-03-17 21:37:48 -04:00
I18nManager,
Keyboard,
LayoutAnimation,
Platform,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
2024-06-12 12:46:44 -04:00
import { Icon } from '@rneui/themed';
2024-05-20 10:54:13 +01:00
import A from '../../blue_modules/analytics';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
import { encodeUR } from '../../blue_modules/ur';
import { BlueButtonLink, BlueFormMultiInput, BlueSpacing10, BlueSpacing20, BlueTextCentered } from '../../BlueComponents';
import { HDSegwitBech32Wallet, MultisigCosigner, MultisigHDWallet } from '../../class';
2024-05-20 10:54:13 +01:00
import presentAlert from '../../components/Alert';
2020-11-17 11:43:38 +03:00
import BottomModal from '../../components/BottomModal';
2024-05-20 10:54:13 +01:00
import Button from '../../components/Button';
import MultipleStepsListItem, {
MultipleStepsListItemButtohType,
MultipleStepsListItemDashType,
} from '../../components/MultipleStepsListItem';
2021-08-26 21:31:36 -04:00
import QRCodeComponent from '../../components/QRCodeComponent';
2024-05-20 10:54:13 +01:00
import { useTheme } from '../../components/themes';
2023-09-17 18:28:54 +02:00
import confirm from '../../helpers/confirm';
2024-05-20 10:54:13 +01:00
import prompt from '../../helpers/prompt';
import usePrivacy from '../../hooks/usePrivacy';
2024-05-20 10:54:13 +01:00
import loc from '../../loc';
2024-05-31 13:18:01 -04:00
import { useStorage } from '../../hooks/context/useStorage';
2024-07-14 22:54:30 -04:00
import { scanQrHelper } from '../../helpers/scan-qr';
2024-07-23 13:44:04 -04:00
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
import ToolTipMenu from '../../components/TooltipMenu';
import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
const staticCache = {};
const WalletsAddMultisigStep2 = () => {
const { addWallet, saveToDisk, isElectrumDisabled, sleep, currentSharedCosigner, setSharedCosigner } = useStorage();
const { colors } = useTheme();
2024-07-23 13:44:04 -04:00
const { navigate, navigateToWalletsList } = useExtendedNavigation();
const { m, n, format, walletLabel } = useRoute().params;
const { name } = useRoute();
const [cosigners, setCosigners] = useState([]); // array of cosigners user provided. if format [cosigner, fp, path]
const [isLoading, setIsLoading] = useState(false);
2024-06-30 13:17:55 -04:00
const mnemonicsModalRef = useRef(null);
const provideMnemonicsModalRef = useRef(null);
const renderCosignersXpubModalRef = useRef(null);
2021-07-09 11:52:09 +01:00
const [cosignerXpub, setCosignerXpub] = useState(''); // string used in exportCosigner()
const [cosignerXpubURv2, setCosignerXpubURv2] = useState(''); // string displayed in renderCosignersXpubModal()
2023-11-12 15:41:51 +01:00
const [cosignerXpubFilename, setCosignerXpubFilename] = useState('bw-cosigner.bwcosigner');
const [vaultKeyData, setVaultKeyData] = useState({ keyIndex: 1, xpub: '', seed: '', isLoading: false }); // string rendered in modal
const [importText, setImportText] = useState('');
2022-01-09 17:38:56 +03:00
const [askPassphrase, setAskPassphrase] = useState(false);
const openScannerButton = useRef();
const data = useRef(new Array(n));
const { enableBlur, disableBlur } = usePrivacy();
2021-09-13 13:43:26 -04:00
useFocusEffect(
useCallback(() => {
enableBlur();
return () => {
disableBlur();
};
}, [disableBlur, enableBlur]),
);
2023-09-17 18:28:54 +02:00
useEffect(() => {
console.log(currentSharedCosigner);
if (currentSharedCosigner) {
(async function () {
if (await confirm(loc.multisig.shared_key_detected, loc.multisig.shared_key_detected_question)) {
setImportText(currentSharedCosigner);
2024-06-30 13:17:55 -04:00
provideMnemonicsModalRef.current.present();
2023-09-17 18:28:54 +02:00
setSharedCosigner('');
}
})();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentSharedCosigner]);
2024-07-23 13:44:04 -04:00
const handleOnHelpPress = async () => {
await dismissAllModals();
2024-07-23 11:04:26 +01:00
navigate('WalletsAddMultisigHelp');
2020-12-07 17:48:56 +01:00
};
2024-07-23 13:44:04 -04:00
const dismissAllModals = async () => {
try {
await mnemonicsModalRef.current?.dismiss();
await provideMnemonicsModalRef.current?.dismiss();
await renderCosignersXpubModalRef.current?.dismiss();
} catch (e) {
// in rare occasions trying to dismiss non visible modals can error out
console.debug('dismissAllModals error', e);
}
2024-07-02 23:25:15 -04:00
};
const stylesHook = StyleSheet.create({
root: {
backgroundColor: colors.elevated,
},
askPassphrase: {
backgroundColor: colors.lightButton,
},
textDestination: {
color: colors.foregroundColor,
},
vaultKeyText: {
color: colors.alternativeTextColor,
},
vaultKeyCircleSuccess: {
backgroundColor: colors.msSuccessBG,
},
word: {
backgroundColor: colors.inputBackgroundColor,
},
wordText: {
color: colors.labelText,
},
2020-12-08 23:01:05 +01:00
helpButton: {
backgroundColor: colors.buttonDisabledBackgroundColor,
},
2020-12-09 10:08:02 -05:00
helpButtonText: {
2020-12-08 23:01:05 +01:00
color: colors.foregroundColor,
},
});
2022-01-09 17:38:56 +03:00
const onCreate = async () => {
setIsLoading(true);
2022-01-09 17:38:56 +03:00
await sleep(100);
try {
await _onCreate(); // this can fail with "Duplicate fingerprint" error or other
} catch (e) {
setIsLoading(false);
presentAlert({ message: e.message });
2022-01-09 17:38:56 +03:00
console.log('create MS wallet error', e);
}
2020-12-07 16:35:28 +00:00
};
const _onCreate = async () => {
const w = new MultisigHDWallet();
w.setM(m);
switch (format) {
case MultisigHDWallet.FORMAT_P2WSH:
w.setNativeSegwit();
w.setDerivationPath(MultisigHDWallet.PATH_NATIVE_SEGWIT);
break;
case MultisigHDWallet.FORMAT_P2SH_P2WSH:
case MultisigHDWallet.FORMAT_P2SH_P2WSH_ALT:
w.setWrappedSegwit();
w.setDerivationPath(MultisigHDWallet.PATH_WRAPPED_SEGWIT);
break;
case MultisigHDWallet.FORMAT_P2SH:
w.setLegacy();
w.setDerivationPath(MultisigHDWallet.PATH_LEGACY);
break;
default:
2024-07-23 11:04:26 +01:00
console.error('Unexpected format:', format);
throw new Error('This should never happen');
}
for (const cc of cosigners) {
2022-01-09 17:38:56 +03:00
const fp = cc[1] || getFpCacheForMnemonics(cc[0], cc[3]);
w.addCosigner(cc[0], fp, cc[2], cc[3]);
}
w.setLabel(walletLabel);
2021-08-24 00:33:32 -04:00
if (!isElectrumDisabled) {
await w.fetchBalance();
}
addWallet(w);
await saveToDisk();
A(A.ENUM.CREATED_WALLET);
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
2024-07-23 13:44:04 -04:00
navigateToWalletsList();
};
const generateNewKey = () => {
const w = new HDSegwitBech32Wallet();
w.generate().then(() => {
const cosignersCopy = [...cosigners];
cosignersCopy.push([w.getSecret(), false, false]);
2020-12-04 23:18:31 -05:00
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setCosigners(cosignersCopy);
setVaultKeyData({ keyIndex: cosignersCopy.length, seed: w.getSecret(), xpub: w.getXpub(), isLoading: false });
setIsLoading(true);
2024-06-30 13:17:55 -04:00
mnemonicsModalRef.current.present();
setTimeout(() => {
// filling cache
setXpubCacheForMnemonics(w.getSecret());
setFpCacheForMnemonics(w.getSecret());
setIsLoading(false);
}, 500);
});
};
const getPath = () => {
let path = '';
switch (format) {
case MultisigHDWallet.FORMAT_P2WSH:
path = MultisigHDWallet.PATH_NATIVE_SEGWIT;
break;
case MultisigHDWallet.FORMAT_P2SH_P2WSH:
case MultisigHDWallet.FORMAT_P2SH_P2WSH_ALT:
path = MultisigHDWallet.PATH_WRAPPED_SEGWIT;
break;
case MultisigHDWallet.FORMAT_P2SH:
path = MultisigHDWallet.PATH_LEGACY;
break;
default:
2024-07-23 11:04:26 +01:00
console.error('Unexpected format:', format);
throw new Error('This should never happen');
}
return path;
};
const viewKey = cosigner => {
if (MultisigHDWallet.isXpubValid(cosigner[0])) {
setCosignerXpub(MultisigCosigner.exportToJson(cosigner[1], cosigner[0], cosigner[2]));
2021-07-09 11:52:09 +01:00
setCosignerXpubURv2(encodeUR(MultisigCosigner.exportToJson(cosigner[1], cosigner[0], cosigner[2]))[0]);
2023-11-12 15:41:51 +01:00
setCosignerXpubFilename('bw-cosigner-' + cosigner[1] + '.bwcosigner');
2024-06-30 13:17:55 -04:00
renderCosignersXpubModalRef.current.present();
} else {
const path = getPath();
const xpub = getXpubCacheForMnemonics(cosigner[0], cosigner[3]);
2022-01-09 17:38:56 +03:00
const fp = getFpCacheForMnemonics(cosigner[0], cosigner[3]);
setCosignerXpub(MultisigCosigner.exportToJson(fp, xpub, path));
2021-07-09 11:52:09 +01:00
setCosignerXpubURv2(encodeUR(MultisigCosigner.exportToJson(fp, xpub, path))[0]);
2023-11-12 15:41:51 +01:00
setCosignerXpubFilename('bw-cosigner-' + fp + '.bwcosigner');
2024-06-30 13:17:55 -04:00
renderCosignersXpubModalRef.current.present();
}
};
const getXpubCacheForMnemonics = (seed, passphrase) => {
const path = getPath();
return staticCache[seed + path + passphrase] || setXpubCacheForMnemonics(seed, passphrase);
};
const setXpubCacheForMnemonics = (seed, passphrase) => {
const path = getPath();
const w = new MultisigHDWallet();
w.setDerivationPath(path);
staticCache[seed + path + passphrase] = w.convertXpubToMultisignatureXpub(MultisigHDWallet.seedToXpub(seed, path, passphrase));
return staticCache[seed + path + passphrase];
};
2022-01-09 17:38:56 +03:00
const getFpCacheForMnemonics = (seed, passphrase) => {
return staticCache[seed + (passphrase ?? '')] || setFpCacheForMnemonics(seed, passphrase);
};
2022-01-09 17:38:56 +03:00
const setFpCacheForMnemonics = (seed, passphrase) => {
staticCache[seed + (passphrase ?? '')] = MultisigHDWallet.mnemonicToFingerprint(seed, passphrase);
return staticCache[seed + (passphrase ?? '')];
};
const iHaveMnemonics = () => {
2024-06-30 13:17:55 -04:00
provideMnemonicsModalRef.current.present();
};
2023-09-17 18:28:54 +02:00
const tryUsingXpub = async (xpub, fp, path) => {
if (!MultisigHDWallet.isXpubForMultisig(xpub)) {
2024-06-30 13:17:55 -04:00
provideMnemonicsModalRef.current.dismiss();
setIsLoading(false);
setImportText('');
2022-01-09 17:38:56 +03:00
setAskPassphrase(false);
presentAlert({ message: loc.multisig.not_a_multisignature_xpub });
return;
}
2023-09-17 18:28:54 +02:00
if (fp) {
// do nothing, it's already set
} else {
try {
fp = await prompt(loc.multisig.input_fp, loc.multisig.input_fp_explain, true, 'plain-text');
fp = (fp + '').toUpperCase();
if (!MultisigHDWallet.isFpValid(fp)) fp = '00000000';
} catch (e) {
return setIsLoading(false);
}
2021-12-27 12:21:35 -06:00
}
2023-09-17 18:28:54 +02:00
if (path) {
// do nothing, it's already set
} else {
try {
path = await prompt(
loc.multisig.input_path,
loc.formatString(loc.multisig.input_path_explain, { default: getPath() }),
true,
'plain-text',
);
if (!MultisigHDWallet.isPathValid(path)) path = getPath();
} catch {
return setIsLoading(false);
}
2021-12-27 12:21:35 -06:00
}
2024-06-30 13:17:55 -04:00
provideMnemonicsModalRef.current.dismiss();
setIsLoading(false);
setImportText('');
2022-01-09 17:38:56 +03:00
setAskPassphrase(false);
const cosignersCopy = [...cosigners];
cosignersCopy.push([xpub, fp, path]);
2020-12-04 23:18:31 -05:00
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setCosigners(cosignersCopy);
};
2022-01-09 17:38:56 +03:00
const useMnemonicPhrase = async () => {
setIsLoading(true);
if (MultisigHDWallet.isXpubValid(importText)) {
return tryUsingXpub(importText);
}
2023-09-17 18:28:54 +02:00
try {
const jsonText = JSON.parse(importText);
let fp;
let path;
if (jsonText.xpub) {
if (jsonText.xfp) {
fp = jsonText.xfp;
}
if (jsonText.path) {
path = jsonText.path;
}
return tryUsingXpub(jsonText.xpub, fp, path);
}
} catch {}
const hd = new HDSegwitBech32Wallet();
hd.setSecret(importText);
if (!hd.validateMnemonic()) {
setIsLoading(false);
return presentAlert({ message: loc.multisig.invalid_mnemonics });
}
2022-01-09 17:38:56 +03:00
let passphrase;
if (askPassphrase) {
try {
passphrase = await prompt(loc.wallets.import_passphrase_title, loc.wallets.import_passphrase_message);
} catch (e) {
if (e.message === 'Cancel Pressed') {
setIsLoading(false);
return;
}
throw e;
}
}
const cosignersCopy = [...cosigners];
2022-01-09 17:38:56 +03:00
cosignersCopy.push([hd.getSecret(), false, false, passphrase]);
2020-12-04 23:18:31 -05:00
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setCosigners(cosignersCopy);
2020-12-04 23:18:31 -05:00
2024-06-30 13:17:55 -04:00
provideMnemonicsModalRef.current.dismiss();
setIsLoading(false);
setImportText('');
2022-01-09 17:38:56 +03:00
setAskPassphrase(false);
};
const isValidMnemonicSeed = mnemonicSeed => {
const hd = new HDSegwitBech32Wallet();
hd.setSecret(mnemonicSeed);
return hd.validateMnemonic();
};
const onBarScanned = ret => {
if (!ret.data) ret = { data: ret };
try {
let retData = JSON.parse(ret.data);
if (Array.isArray(retData) && retData.length === 1) {
// UR:CRYPTO-ACCOUNT now parses as an array of accounts, even if it is just one,
// so in case of cosigner data its gona be an array of 1 cosigner account. lets pop it for
// the code that expects it
retData = retData.pop();
ret.data = JSON.stringify(retData);
}
} catch (_) {}
if (ret.data.toUpperCase().startsWith('UR')) {
presentAlert({ message: 'BC-UR not decoded. This should never happen' });
} else if (isValidMnemonicSeed(ret.data)) {
setImportText(ret.data);
2024-07-14 11:56:46 -04:00
setTimeout(() => {
provideMnemonicsModalRef.current.present().then(() => {});
}, 100);
} else {
if (MultisigHDWallet.isXpubValid(ret.data) && !MultisigHDWallet.isXpubForMultisig(ret.data)) {
return presentAlert({ message: loc.multisig.not_a_multisignature_xpub });
}
if (MultisigHDWallet.isXpubValid(ret.data)) {
return tryUsingXpub(ret.data);
}
let cosigner = new MultisigCosigner(ret.data);
if (!cosigner.isValid()) return presentAlert({ message: loc.multisig.invalid_cosigner });
2024-06-30 13:17:55 -04:00
provideMnemonicsModalRef.current.dismiss();
if (cosigner.howManyCosignersWeHave() > 1) {
// lets look for the correct cosigner. thats probably gona be the one with specific corresponding path,
// for example m/48'/0'/0'/2' if user chose to setup native segwit in BW
for (const cc of cosigner.getAllCosigners()) {
switch (format) {
case MultisigHDWallet.FORMAT_P2WSH:
if (cc.getPath().startsWith('m/48') && cc.getPath().endsWith("/2'")) {
// found it
cosigner = cc;
}
break;
case MultisigHDWallet.FORMAT_P2SH_P2WSH:
case MultisigHDWallet.FORMAT_P2SH_P2WSH_ALT:
if (cc.getPath().startsWith('m/48') && cc.getPath().endsWith("/1'")) {
// found it
cosigner = cc;
}
break;
case MultisigHDWallet.FORMAT_P2SH:
if (cc.getPath().startsWith('m/45')) {
// found it
cosigner = cc;
}
break;
default:
2024-07-23 11:04:26 +01:00
console.error('Unexpected format:', format);
throw new Error('This should never happen');
}
}
}
for (const existingCosigner of cosigners) {
if (existingCosigner[0] === cosigner.getXpub()) return presentAlert({ message: loc.multisig.this_cosigner_is_already_imported });
}
// now, validating that cosigner is in correct format:
let correctFormat = false;
switch (format) {
case MultisigHDWallet.FORMAT_P2WSH:
if (cosigner.getPath().startsWith('m/48') && cosigner.getPath().endsWith("/2'")) {
correctFormat = true;
}
break;
case MultisigHDWallet.FORMAT_P2SH_P2WSH:
case MultisigHDWallet.FORMAT_P2SH_P2WSH_ALT:
if (cosigner.getPath().startsWith('m/48') && cosigner.getPath().endsWith("/1'")) {
correctFormat = true;
}
break;
case MultisigHDWallet.FORMAT_P2SH:
if (cosigner.getPath().startsWith('m/45')) {
correctFormat = true;
}
break;
default:
2024-07-23 11:04:26 +01:00
console.error('Unexpected format:', format);
throw new Error('This should never happen');
}
if (!correctFormat) return presentAlert({ message: loc.formatString(loc.multisig.invalid_cosigner_format, { format }) });
const cosignersCopy = [...cosigners];
cosignersCopy.push([cosigner.getXpub(), cosigner.getFp(), cosigner.getPath()]);
2020-12-04 23:18:31 -05:00
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setCosigners(cosignersCopy);
}
};
2024-07-14 11:56:46 -04:00
const scanOrOpenFile = async () => {
await provideMnemonicsModalRef.current.dismiss();
2024-07-23 13:44:04 -04:00
const scanned = await scanQrHelper(name, true, undefined);
2024-07-14 22:54:30 -04:00
onBarScanned({ data: scanned });
};
2021-04-11 16:53:05 -04:00
const dashType = ({ index, lastIndex, isChecked, isFocus }) => {
if (isChecked) {
if (index === lastIndex) {
return MultipleStepsListItemDashType.top;
} else {
return MultipleStepsListItemDashType.topAndBottom;
}
} else {
if (index === lastIndex) {
return isFocus ? MultipleStepsListItemDashType.topAndBottom : MultipleStepsListItemDashType.top;
} else {
return MultipleStepsListItemDashType.topAndBottom;
}
}
};
const _renderKeyItem = el => {
const renderProvideKeyButtons = el.index === cosigners.length;
const isChecked = el.index < cosigners.length;
return (
<View>
<MultipleStepsListItem
2023-08-27 22:19:08 +03:00
circledText={String(el.index + 1)}
leftText={loc.formatString(loc.multisig.vault_key, { number: el.index + 1 })}
2021-04-11 16:53:05 -04:00
dashes={dashType({ index: el.index, lastIndex: data.current.length - 1, isChecked, isFocus: renderProvideKeyButtons })}
checked={isChecked}
rightButton={{
disabled: vaultKeyData.isLoading,
text: loc.multisig.share,
onPress: () => {
viewKey(cosigners[el.index]);
},
}}
/>
{renderProvideKeyButtons && (
<>
<MultipleStepsListItem
showActivityIndicator={vaultKeyData.keyIndex === el.index && vaultKeyData.isLoading}
button={{
buttonType: MultipleStepsListItemButtohType.full,
onPress: () => {
setVaultKeyData({ keyIndex: el.index, xpub: '', seed: '', isLoading: true });
generateNewKey();
},
text: loc.multisig.create_new_key,
disabled: vaultKeyData.isLoading,
}}
dashes={MultipleStepsListItemDashType.topAndBottom}
checked={isChecked}
/>
<MultipleStepsListItem
button={{
testID: 'VaultCosignerImport' + String(el.index + 1),
onPress: iHaveMnemonics,
buttonType: MultipleStepsListItemButtohType.full,
text: loc.wallets.import_do_import,
disabled: vaultKeyData.isLoading,
}}
2021-04-11 16:53:05 -04:00
dashes={el.index === data.current.length - 1 ? MultipleStepsListItemDashType.top : MultipleStepsListItemDashType.topAndBottom}
checked={isChecked}
/>
</>
)}
</View>
);
};
const renderSecret = entries => {
const component = [];
const entriesObject = entries.entries();
for (const [index, secret] of entriesObject) {
if (entries.length > 1) {
const text = `${index + 1}. ${secret} `;
component.push(
<View style={[styles.word, stylesHook.word]} key={`${secret}${index}`}>
<Text style={[styles.wordText, stylesHook.wordText]} textBreakStrategy="simple">
{text}
</Text>
</View>,
);
} else {
const text = `${secret} `;
component.push(
<View style={[styles.word, stylesHook.word]} key={`${secret}${index}`}>
<Text style={[styles.wordText, stylesHook.wordText]} textBreakStrategy="simple">
{text}
</Text>
</View>,
);
}
}
return component;
};
const renderMnemonicsModal = () => {
return (
2024-07-06 17:55:03 -04:00
<BottomModal
ref={mnemonicsModalRef}
isGrabberVisible={false}
dismissible={false}
showCloseButton={!isLoading}
footerDefaultMargins
2024-07-14 10:03:29 -04:00
backgroundColor={colors.modal}
2024-07-06 17:55:03 -04:00
footer={
2024-07-14 11:56:46 -04:00
<View style={styles.modalFooterBottomPadding}>
{isLoading ? (
<ActivityIndicator />
) : (
<Button title={loc.send.success_done} onPress={() => mnemonicsModalRef.current.dismiss()} />
)}
</View>
2024-07-06 17:55:03 -04:00
}
>
<View style={styles.itemKeyUnprovidedWrapper}>
<View style={[styles.vaultKeyCircleSuccess, stylesHook.vaultKeyCircleSuccess]}>
<Icon size={24} name="check" type="ionicons" color={colors.msSuccessCheck} />
</View>
<View style={styles.vaultKeyTextWrapper}>
<Text style={[styles.vaultKeyText, stylesHook.vaultKeyText]}>
{loc.formatString(loc.multisig.vault_key, { number: vaultKeyData.keyIndex })}
</Text>
</View>
</View>
2024-07-06 17:55:03 -04:00
<BlueSpacing20 />
<Text style={[styles.headerText, stylesHook.textDestination]}>{loc.multisig.wallet_key_created}</Text>
<BlueSpacing20 />
<Text style={[styles.textDestination, stylesHook.textDestination]}>{loc._.seed}</Text>
<BlueSpacing10 />
<View style={styles.secretContainer}>{renderSecret(vaultKeyData.seed.split(' '))}</View>
<BlueSpacing20 />
2020-11-17 11:43:38 +03:00
</BottomModal>
);
};
const toolTipActions = useMemo(() => {
const passphrase = CommonToolTipActions.Passphrase;
passphrase.menuState = askPassphrase;
return [passphrase];
}, [askPassphrase]);
const renderProvideMnemonicsModal = () => {
return (
2024-07-06 17:55:03 -04:00
<BottomModal
footerDefaultMargins
footer={
2024-07-14 11:56:46 -04:00
<View style={styles.modalFooterBottomPadding}>
{isLoading ? (
<ActivityIndicator />
) : (
<>
<Button
testID="DoImportKeyButton"
disabled={importText.trim().length === 0}
title={loc.wallets.import_do_import}
onPress={useMnemonicPhrase}
/>
<BlueButtonLink
testID="ScanOrOpenFile"
ref={openScannerButton}
disabled={isLoading}
onPress={scanOrOpenFile}
title={loc.wallets.import_scan_qr}
/>
</>
)}
</View>
2024-07-06 17:55:03 -04:00
}
ref={provideMnemonicsModalRef}
2024-07-14 10:03:29 -04:00
backgroundColor={colors.modal}
2024-07-06 17:55:03 -04:00
isGrabberVisible={false}
2024-07-23 21:49:59 -04:00
onDismiss={() => {
Keyboard.dismiss();
setImportText('');
setAskPassphrase(false);
}}
2024-07-06 17:55:03 -04:00
>
<>
<ToolTipMenu
isButton
isMenuPrimaryAction
onPressMenuItem={_id => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setAskPassphrase(!askPassphrase);
}}
actions={toolTipActions}
style={[styles.askPassprase, stylesHook.askPassphrase]}
>
<Icon size={22} name="more-horiz" type="material" color={colors.foregroundColor} />
</ToolTipMenu>
<BlueTextCentered>{loc.multisig.type_your_mnemonics}</BlueTextCentered>
<BlueSpacing20 />
2024-09-15 14:59:02 -04:00
<View style={styles.multiLineTextInput}>
<BlueFormMultiInput value={importText} onChangeText={setImportText} />
<BlueSpacing20 />
</View>
</>
2020-11-17 11:43:38 +03:00
</BottomModal>
);
};
2020-11-11 11:59:03 -05:00
const hideCosignersXpubModal = () => {
Keyboard.dismiss();
2024-06-30 13:17:55 -04:00
renderCosignersXpubModalRef.current.dismiss();
2020-11-11 11:59:03 -05:00
};
const renderCosignersXpubModal = () => {
return (
2024-07-06 17:55:03 -04:00
<BottomModal
onClose={hideCosignersXpubModal}
ref={renderCosignersXpubModalRef}
2024-07-14 10:04:25 -04:00
backgroundColor={colors.modal}
2024-08-24 15:20:53 -04:00
shareContent={{ fileContent: cosignerXpub, fileName: cosignerXpubFilename }}
2024-07-06 17:55:03 -04:00
footerDefaultMargins
2024-07-14 10:04:25 -04:00
contentContainerStyle={[styles.modalContent, styles.alignItemsCenter]}
2024-08-24 15:20:53 -04:00
footer={<View style={styles.modalFooterBottomPadding}>{isLoading ? <ActivityIndicator /> : null}</View>}
2024-07-06 17:55:03 -04:00
>
<Text style={[styles.headerText, stylesHook.textDestination]}>
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
</Text>
<BlueSpacing20 />
<QRCodeComponent value={cosignerXpubURv2} size={260} />
<BlueSpacing20 />
2020-11-17 11:43:38 +03:00
</BottomModal>
);
};
2020-12-07 17:48:56 +01:00
const renderHelp = () => {
return (
2020-12-08 23:01:05 +01:00
<View style={styles.helpButtonWrapper}>
<TouchableOpacity accessibilityRole="button" style={[styles.helpButton, stylesHook.helpButton]} onPress={handleOnHelpPress}>
2020-12-08 23:01:05 +01:00
<Icon size={20} name="help" type="octaicon" color={colors.foregroundColor} />
2020-12-09 15:55:23 +01:00
<Text style={[styles.helpButtonText, stylesHook.helpButtonText]}>{loc.multisig.ms_help}</Text>
2020-12-08 23:01:05 +01:00
</TouchableOpacity>
</View>
2020-12-07 17:48:56 +01:00
);
};
2021-02-07 22:39:19 -05:00
const footer = (
2020-11-06 14:31:08 +01:00
<View style={styles.buttonBottom}>
{isLoading ? (
<ActivityIndicator />
) : (
<Button testID="CreateButton" title={loc.multisig.create} onPress={onCreate} disabled={cosigners.length !== n} />
)}
2020-11-05 12:38:04 +01:00
</View>
);
return (
2020-11-07 22:01:25 -05:00
<View style={[styles.root, stylesHook.root]}>
2020-12-07 17:48:56 +01:00
{renderHelp()}
<View style={styles.wrapBox}>
2020-12-09 13:13:20 +01:00
<FlatList data={data.current} renderItem={_renderKeyItem} keyExtractor={(_item, index) => `${index}`} />
</View>
2020-11-07 22:01:25 -05:00
{renderMnemonicsModal()}
2020-11-07 22:01:25 -05:00
{renderProvideMnemonicsModal()}
2020-11-07 22:01:25 -05:00
{renderCosignersXpubModal()}
2020-11-06 14:31:08 +01:00
{footer}
2020-11-07 22:01:25 -05:00
</View>
);
};
const styles = StyleSheet.create({
root: {
flex: 1,
paddingHorizontal: 20,
2020-11-06 14:31:08 +01:00
},
2020-12-09 13:13:20 +01:00
wrapBox: {
2020-12-09 10:08:02 -05:00
flex: 1,
2020-12-09 13:13:20 +01:00
marginVertical: 24,
},
2020-11-06 14:31:08 +01:00
buttonBottom: {
marginHorizontal: 20,
flex: 0.12,
2021-02-07 22:39:19 -05:00
marginBottom: 40,
2020-11-06 14:31:08 +01:00
justifyContent: 'flex-end',
},
itemKeyUnprovidedWrapper: { flexDirection: 'row' },
vaultKeyText: { fontSize: 18, fontWeight: 'bold' },
vaultKeyTextWrapper: { justifyContent: 'center', alignItems: 'center', paddingLeft: 16 },
textDestination: { fontWeight: '600' },
modalContent: {
paddingHorizontal: 22,
paddingVertical: 32,
2024-09-15 14:59:02 -04:00
alignItems: 'center',
justifyContent: 'center',
2024-07-23 13:44:04 -04:00
minHeight: 450,
},
2024-08-24 15:20:53 -04:00
multiLineTextInput: {
minHeight: 200,
},
2024-07-14 11:56:46 -04:00
modalFooterBottomPadding: { paddingBottom: 26 },
vaultKeyCircleSuccess: {
width: 42,
height: 42,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
},
word: {
width: 'auto',
marginRight: 8,
marginBottom: 8,
paddingTop: 6,
paddingBottom: 6,
paddingLeft: 8,
paddingRight: 8,
borderRadius: 4,
},
2024-09-15 14:59:02 -04:00
askPassprase: { top: -44, left: 0, justifyContent: 'center', width: 33, height: 33, borderRadius: 33 / 2 },
secretContainer: {
2021-03-17 21:37:48 -04:00
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
justifyContent: 'flex-start',
flexWrap: 'wrap',
},
wordText: {
fontWeight: 'bold',
},
headerText: { fontSize: 15, color: '#13244D' },
alignItemsCenter: { alignItems: 'center' },
2020-12-08 23:01:05 +01:00
helpButtonWrapper: {
alignItems: 'flex-end',
2021-03-18 22:30:01 -04:00
flexDirection: I18nManager.isRTL ? 'row' : 'row-reverse',
2020-12-08 23:01:05 +01:00
},
helpButton: {
paddingHorizontal: 16,
paddingVertical: 10,
borderRadius: 50,
2020-12-09 10:08:02 -05:00
flexDirection: 'row',
2020-12-08 23:01:05 +01:00
},
2020-12-09 15:55:23 +01:00
helpButtonText: {
2020-12-08 23:01:05 +01:00
fontSize: 16,
fontWeight: 'bold',
marginLeft: 8,
},
});
export default WalletsAddMultisigStep2;