import { useCallback, useMemo } from 'react'; import { useSearchParams } from 'react-router'; // Coin control selection, held in `?coins=txid:vout,txid:vout`. // // The Coins section picks the inputs and the Send section spends them — two panels that must agree on a // set. The URL is how they agree (docs/navigation-audit.md): no channel, no shared store, and the // selection survives a reload and can be handed to someone as a link. // // Outpoints are public transaction data, so there is nothing sensitive about putting them in an address // bar — unlike anything else this app handles. const PARAM = 'coins'; const OUTPOINT = /^[0-9a-f]{64}:\d+$/i; export function useCoinSelection() { const [params, setParams] = useSearchParams(); const raw = params.get(PARAM) ?? ''; const selected = useMemo(() => raw.split(',').filter((o) => OUTPOINT.test(o)), [raw]); const write = useCallback( (next: string[]) => { setParams( (prev) => { const updated = new URLSearchParams(prev); if (next.length === 0) updated.delete(PARAM); else updated.set(PARAM, next.join(',')); return updated; }, { replace: true }, ); }, [setParams], ); const toggle = useCallback( (outpoint: string) => write(selected.includes(outpoint) ? selected.filter((o) => o !== outpoint) : [...selected, outpoint]), [selected, write], ); const clear = useCallback(() => write([]), [write]); return { selected, toggle, clear, set: write }; }