import { useEffect, useState } from 'react'; import { BiometricAuth, BiometryError, BiometryErrorType } from '@aparajita/capacitor-biometric-auth'; interface Props { onUnlocked: () => void; /** User can't (or won't) use biometrics — fall back to server login. */ onFallback: () => void; } /** * App-lock gate shown on launch when biometric unlock is enabled. The * server session cookie persists in the WebView's cookie store between * launches, so this is what stands between "phone unlocked" and "Bill * Tracker dashboard visible". */ export default function BiometricLock({ onUnlocked, onFallback }: Props) { const [error, setError] = useState(''); const [checking, setChecking] = useState(true); async function attempt() { setError(''); setChecking(true); try { await BiometricAuth.authenticate({ reason: 'Unlock Bill Tracker', cancelTitle: 'Cancel', allowDeviceCredential: true, androidTitle: 'Unlock Bill Tracker', }); onUnlocked(); } catch (err) { if (err instanceof BiometryError && err.code === BiometryErrorType.userCancel) { setError(''); } else { setError('Authentication failed. Try again.'); } } finally { setChecking(false); } } useEffect(() => { attempt(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return (
{checking ? (
) : ( <>

Locked

{error || 'Authenticate to continue.'}

)}
); }