/** * Centralised copy-to-clipboard with a single, module-scope auto-clear * timer. * * Why module scope: tying the timer to a React component (via `useRef`) * means it gets cancelled when the screen unmounts — which happens * routinely (auto-lock on tab-switch, navigation to another item). The * user's just-completed copy then either gets wiped immediately * (component cleanup runs `setStringAsync("")`) or sits indefinitely. * * Behaviour: * * `copyToClipboard(value)` writes the value, returns `true` on * success. * * On success, schedules a 30-second auto-wipe. * * Subsequent copies cancel the prior timer and reschedule, so the * wipe is always 30 seconds from the *last* copy regardless of * which screen issued it. * * Limitation: we don't read the clipboard back (would require the * clipboard-read permission), so if the user copies from another app * between our copy and the auto-clear firing, we'll still wipe their * value at the 30s mark. Acceptable trade-off for a password manager. */ import * as Clipboard from "expo-clipboard"; const AUTO_CLEAR_MS = 30_000; let clearTimer: ReturnType | null = null; /** Write `value` to the clipboard. Returns `true` if the write succeeded. */ export async function copyToClipboard(value: string): Promise { let ok: boolean; try { ok = await Clipboard.setStringAsync(value); } catch { ok = false; } if (!ok) { return false; } if (clearTimer !== null) { clearTimeout(clearTimer); } clearTimer = setTimeout(() => { clearTimer = null; void Clipboard.setStringAsync("").catch(() => { // Best-effort. }); }, AUTO_CLEAR_MS); return true; } /** * Wipe the clipboard now (e.g. on explicit logout). Cancels the pending * auto-clear so we don't fire a redundant wipe later. */ export async function clearClipboardNow(): Promise { if (clearTimer !== null) { clearTimeout(clearTimer); clearTimer = null; } try { await Clipboard.setStringAsync(""); } catch { // Best-effort. } }