import React, { useContext, useRef, useState, useCallback, useEffect } from 'react'; import { ActivityIndicator, Alert, FlatList, InteractionManager, Keyboard, KeyboardAvoidingView, LayoutAnimation, Platform, StyleSheet, Switch, Text, View, } from 'react-native'; import { Icon, Badge } from 'react-native-elements'; import { useFocusEffect, useNavigation, useRoute } from '@react-navigation/native'; import { BlueButtonLink, BlueFormMultiInput, BlueLoading, BlueSpacing10, BlueSpacing20, BlueSpacing40, BlueText, BlueTextCentered, } from '../../BlueComponents'; import navigationStyle from '../../components/navigationStyle'; import SquareEnumeratedWords, { SquareEnumeratedWordsContentAlign } from '../../components/SquareEnumeratedWords'; import BottomModal from '../../components/BottomModal'; import { HDSegwitBech32Wallet, MultisigCosigner, MultisigHDWallet } from '../../class'; import loc from '../../loc'; import { BlueStorageContext } from '../../blue_modules/storage-context'; import MultipleStepsListItem, { MultipleStepsListItemButtohType, MultipleStepsListItemDashType, } from '../../components/MultipleStepsListItem'; import Privacy from '../../blue_modules/Privacy'; import Biometric from '../../class/biometrics'; import { SquareButton } from '../../components/SquareButton'; import { encodeUR } from '../../blue_modules/ur'; import QRCodeComponent from '../../components/QRCodeComponent'; import alert from '../../components/Alert'; import { requestCameraAuthorization } from '../../helpers/scan-qr'; import { useTheme } from '../../components/themes'; import Button from '../../components/Button'; const fs = require('../../blue_modules/fs'); const prompt = require('../../helpers/prompt'); const ViewEditMultisigCosigners = () => { const hasLoaded = useRef(false); const { colors } = useTheme(); const { wallets, setWalletsWithNewOrder, isElectrumDisabled, isAdvancedModeEnabled } = useContext(BlueStorageContext); const { navigate, goBack } = useNavigation(); const route = useRoute(); const openScannerButtonRef = useRef(); const { walletId } = route.params; const w = useRef(wallets.find(wallet => wallet.getID() === walletId)); const tempWallet = useRef(new MultisigHDWallet()); const [wallet, setWallet] = useState(); const [isLoading, setIsLoading] = useState(true); const [isSaveButtonDisabled, setIsSaveButtonDisabled] = useState(true); const [currentlyEditingCosignerNum, setCurrentlyEditingCosignerNum] = useState(false); const [isProvideMnemonicsModalVisible, setIsProvideMnemonicsModalVisible] = useState(false); const [isMnemonicsModalVisible, setIsMnemonicsModalVisible] = useState(false); const [isShareModalVisible, setIsShareModalVisible] = useState(false); const [importText, setImportText] = useState(''); const [exportString, setExportString] = useState('{}'); // used in exportCosigner() const [exportStringURv2, setExportStringURv2] = useState(''); // used in QR const [exportFilename, setExportFilename] = useState('bw-cosigner.json'); const [vaultKeyData, setVaultKeyData] = useState({ keyIndex: 1, xpub: '', seed: '', path: '', fp: '', isLoading: false }); // string rendered in modal const [askPassphrase, setAskPassphrase] = useState(false); const [isAdvancedModeEnabledRender, setIsAdvancedModeEnabledRender] = useState(false); const data = useRef(); const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.elevated, }, textDestination: { color: colors.foregroundColor, }, modalContent: { backgroundColor: colors.elevated, }, exportButton: { backgroundColor: colors.buttonDisabledBackgroundColor, }, vaultKeyText: { color: colors.alternativeTextColor, }, vaultKeyCircleSuccess: { backgroundColor: colors.msSuccessBG, }, tipKeys: { color: colors.alternativeTextColor, }, tipLabel: { backgroundColor: colors.inputBackgroundColor, borderColor: colors.inputBackgroundColor, }, tipLabelText: { color: colors.buttonTextColor, }, }); useEffect(() => { isAdvancedModeEnabled().then(setIsAdvancedModeEnabledRender); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const exportCosigner = () => { setIsShareModalVisible(false); setTimeout(() => fs.writeFileAndExport(exportFilename, exportString), 1000); }; const onSave = async () => { setIsLoading(true); const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled(); if (isBiometricsEnabled) { if (!(await Biometric.unlockWithBiometrics())) { setIsLoading(false); return; } } // eslint-disable-next-line prefer-const let newWallets = wallets.filter(newWallet => { return newWallet.getID() !== walletId; }); if (!isElectrumDisabled) { await wallet.fetchBalance(); } newWallets.push(wallet); navigate('WalletsList'); setTimeout(() => { setWalletsWithNewOrder(newWallets); }, 500); }; useFocusEffect( useCallback(() => { // useFocusEffect is called on willAppear (example: when camera dismisses). we want to avoid this. if (hasLoaded.current) return; setIsLoading(true); Privacy.enableBlur(); const task = InteractionManager.runAfterInteractions(async () => { const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled(); if (isBiometricsEnabled) { if (!(await Biometric.unlockWithBiometrics())) { return goBack(); } } if (!w.current) { // lets create fake wallet so renderer wont throw any errors w.current = new MultisigHDWallet(); w.current.setNativeSegwit(); } else { tempWallet.current.setSecret(w.current.getSecret()); data.current = new Array(tempWallet.current.getN()); setWallet(tempWallet.current); } hasLoaded.current = true; setIsLoading(false); }); return () => { Privacy.disableBlur(); task.cancel(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [walletId]), ); const hideMnemonicsModal = () => { Keyboard.dismiss(); setIsMnemonicsModalVisible(false); }; const renderMnemonicsModal = () => { return ( {loc.formatString(loc.multisig.vault_key, { number: vaultKeyData.keyIndex })} {vaultKeyData.xpub.length > 1 && ( <> {loc._.wallet_key} )} {vaultKeyData.seed.length > 1 && ( <> {loc._.seed} )}