diff --git a/src/app/wallet-setup/import-wallet.tsx b/src/app/wallet-setup/import-wallet.tsx index 11b454a..f9055c3 100644 --- a/src/app/wallet-setup/import-wallet.tsx +++ b/src/app/wallet-setup/import-wallet.tsx @@ -17,11 +17,13 @@ import { } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { toast } from 'sonner-native'; +import { useMnemonicClipboardCleanup } from '@/hooks/useMnemonicClipboardCleanup'; export default function ImportWalletScreen() { const router = useDebouncedNavigation(); const insets = useSafeAreaInsets(); const [secretWords, setSecretWords] = useState(Array(12).fill('')); + const { scheduleClearClipboard } = useMnemonicClipboardCleanup(); const handleWordChange = (index: number, text: string) => { const newWords = [...secretWords]; @@ -56,6 +58,7 @@ export default function ImportWalletScreen() { setSecretWords(newWords); toast.success('12 words have been pasted from clipboard'); + scheduleClearClipboard(words.join(' ')); } catch (error) { console.error('Paste error:', error); toast.error('Could not paste from clipboard'); @@ -69,7 +72,7 @@ export default function ImportWalletScreen() { }; const isFormValid = () => { - return secretWords.every(word => word.trim().length > 0); + return secretWords.every((word) => word.trim().length > 0); }; const validateSeedPhrase = (phrase: string): boolean => { diff --git a/src/hooks/useMnemonicClipboardCleanup.ts b/src/hooks/useMnemonicClipboardCleanup.ts new file mode 100644 index 0000000..6ddb33d --- /dev/null +++ b/src/hooks/useMnemonicClipboardCleanup.ts @@ -0,0 +1,45 @@ +import * as Clipboard from 'expo-clipboard'; +import { useEffect, useRef } from 'react'; + +const CLEAR_CLIPBOARD_AFTER_MS = 5 * 60 * 1000; //5 mins + +const normalizePhrase = (s: string) => s.trim().toLowerCase().replace(/\s+/g, ' '); + +const checkIsMnemonic = (s: string) => { + const words = normalizePhrase(s).split(' '); + return words.length === 12 && words.every(Boolean); +}; + +export function useMnemonicClipboardCleanup() { + const timerRef = useRef | null>(null); + const lastMnemonicRef = useRef(null); + + const scheduleClearClipboard = (mnemonic: string) => { + if (timerRef.current) clearTimeout(timerRef.current); + + lastMnemonicRef.current = normalizePhrase(mnemonic); + + timerRef.current = setTimeout(async () => { + try { + const current = await Clipboard.getStringAsync(); + const normalizedCurrent = normalizePhrase(current); + + if ( + lastMnemonicRef.current && + normalizedCurrent === lastMnemonicRef.current && + checkIsMnemonic(normalizedCurrent) + ) { + await Clipboard.setStringAsync(''); + } + } catch {} + }, CLEAR_CLIPBOARD_AFTER_MS); + }; + + useEffect(() => { + return () => { + if (timerRef.current) clearTimeout(timerRef.current); + }; + }, []); + + return { scheduleClearClipboard }; +}