import React, { useCallback, useEffect, useMemo, useState } from 'react'; import Clipboard from '@react-native-clipboard/clipboard'; import { RouteProp, useFocusEffect, useNavigation, useRoute } from '@react-navigation/native'; import { Icon } from '@rneui/themed'; import { ActivityIndicator, InteractionManager, LayoutChangeEvent, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'; import { disallowScreenshot } from 'react-native-screen-capture'; import { validateMnemonic } from '../../blue_modules/bip39'; import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback'; import { BlueText } from '../../BlueComponents'; import { LightningCustodianWallet, WatchOnlyWallet } from '../../class'; import HandOffComponent from '../../components/HandOffComponent'; import QRCodeComponent from '../../components/QRCodeComponent'; import SeedWords from '../../components/SeedWords'; import { useTheme } from '../../components/themes'; import { HandOffActivityType } from '../../components/types'; import { useSettings } from '../../hooks/context/useSettings'; import { isDesktop } from '../../blue_modules/environment'; import { useStorage } from '../../hooks/context/useStorage'; import useAppState from '../../hooks/useAppState'; import loc from '../../loc'; import { WalletExportStackParamList } from '../../navigation/WalletExportStack'; type RouteProps = RouteProp; const HORIZONTAL_PADDING = 20; const CopyBox: React.FC<{ text: string; onPress: () => void }> = ({ text, onPress }) => { const { colors } = useTheme(); const stylesHook = StyleSheet.create({ copyRoot: { backgroundColor: colors.lightBorder }, }); return ( {text} ); }; const DoNotDisclose: React.FC = () => { const { colors } = useTheme(); return ( {loc.wallets.warning_do_not_disclose} ); }; const WalletExport: React.FC = () => { const { wallets, saveToDisk } = useStorage(); const { walletID } = useRoute().params; const [isLoading, setIsLoading] = useState(true); const navigation = useNavigation(); const { isPrivacyBlurEnabled } = useSettings(); const { colors } = useTheme(); const wallet = wallets.find(w => w.getID() === walletID)!; const [qrCodeSize, setQRCodeSize] = useState(90); const { currentAppState, previousAppState } = useAppState(); const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.elevated }, }); const secrets: string[] = useMemo(() => { try { const secret = wallet.getSecret(); return typeof secret === 'string' ? [secret] : Array.isArray(secret) ? secret : []; } catch (error) { console.error('Failed to get wallet secret:', error); return []; } }, [wallet]); const secretIsMnemonic: boolean = useMemo(() => { return validateMnemonic(wallet.getSecret()); }, [wallet]); useEffect(() => { if (!isLoading && previousAppState === 'active' && currentAppState !== 'active') { const timer = setTimeout(() => navigation.goBack(), 500); return () => clearTimeout(timer); } }, [currentAppState, previousAppState, navigation, isLoading]); useFocusEffect( useCallback(() => { if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); const task = InteractionManager.runAfterInteractions(async () => { if (!wallet.getUserHasSavedExport()) { wallet.setUserHasSavedExport(true); saveToDisk(); } setIsLoading(false); }); return () => { if (!isDesktop) disallowScreenshot(false); task.cancel(); }; }, [isPrivacyBlurEnabled, wallet, saveToDisk]), ); const onLayout = useCallback((e: LayoutChangeEvent) => { const { height, width } = e.nativeEvent.layout; setQRCodeSize(height > width ? width - HORIZONTAL_PADDING * 2 : width / 1.8); }, []); const handleCopy = useCallback(() => { Clipboard.setString(wallet.getSecret()); triggerHapticFeedback(HapticFeedbackTypes.Selection); }, [wallet]); const Scroll = useCallback( // eslint-disable-next-line react/no-unused-prop-types ({ children }: { children: React.ReactNode | React.ReactNodeArray }) => ( {children} ), [isLoading, onLayout, stylesHook.root], ); if (isLoading) { return ( ); } // for SLIP39 if (secrets.length !== 1) { return ( {loc.wallets.write_down_header} {loc.wallets.write_down} {secrets.map((secret, index) => ( {loc.formatString(loc.wallets.share_number, { number: index + 1 })} ))} {loc.formatString(loc.wallets.wallet_type_this, { type: wallet.typeReadable })} ); } const secret = secrets[0]; return ( {wallet.type !== WatchOnlyWallet.type && } {loc.wallets.scan_import} {/* Do not allow to copy mnemonic */} {secretIsMnemonic ? ( <> {loc.wallets.write_down_header} {loc.wallets.write_down} ) : ( <> {wallet.type === LightningCustodianWallet.type ? loc.wallets.copy_ln_url : loc.wallets.copy_ln_public} )} {wallet.type === WatchOnlyWallet.type && ( )} {loc.formatString(loc.wallets.wallet_type_this, { type: wallet.typeReadable })} ); }; const styles = StyleSheet.create({ scrollViewContent: { justifyContent: 'center', flexGrow: 1, gap: 32, paddingHorizontal: HORIZONTAL_PADDING, paddingTop: 10, paddingBottom: 20, }, warningBox: { alignItems: 'center', padding: 12, borderRadius: 10, alignSelf: 'stretch', flexDirection: 'row', gap: 8, }, warning: { fontSize: 20, color: 'white', }, scanText: { textAlign: 'center', fontSize: 20, }, writeText: { textAlign: 'center', fontSize: 17, }, manualText: { textAlign: 'center', fontSize: 20, marginBottom: 10, }, typeText: { textAlign: 'center', fontSize: 17, color: 'grey', }, copyRoot: { padding: 10, borderRadius: 8, flexDirection: 'row', }, copyLeft: { flexShrink: 1, }, copyRight: { justifyContent: 'center', marginHorizontal: 8, }, copyText: { fontSize: 17, }, }); export default WalletExport;