export type GoogleDriveBackupReadinessStatus = 'idle' | 'ready' | 'needs-access' | 'error'; export type GoogleDriveBackupReadiness = { status: GoogleDriveBackupReadinessStatus; isChecking: boolean; missingScopes: readonly string[]; accessToken?: string; error?: unknown; }; export type UseGoogleDriveBackupReadinessReturn = GoogleDriveBackupReadiness & { check: () => Promise; requestAccess: () => Promise; reset: () => void; }; /** * Pre-flight readiness check for backing up MPC keyshares to Google Drive. * * Recommended pattern: call `check()` when the user opens the "Backup to * Google Drive" CTA. If `status` is `'needs-access'`, prompt the user and * call `requestAccess()` to re-prompt the Google consent screen. Then * proceed with `useWalletBackup().backupWallet(...)`. * * Status values: * - `'idle'`: never checked / after `reset()`. * - `'ready'`: stored scopes include both required Drive scopes; backup * should succeed (barring transient errors). * - `'needs-access'`: backup likely won't succeed without user intervention. * Covers (a) missing scopes — `missingScopes` lists the gap, (b) no Google * account linked — `missingScopes` is populated with the full required set * so callers can render copy directly, and (c) legacy tokens captured * before scope tracking shipped (`scopes: []` from the API) — indicated * by an empty `missingScopes` array. Call `requestAccess()` to recover. * - `'error'`: the underlying fetch or relink rejected. `error` holds * the cause. * * `isChecking` is true while a `check()` or `requestAccess()` is in flight. * * `missingScopes` lists the scopes the user still needs to grant. When no * Google account is linked, this is the full required set; when scopes are * known to be partially missing, only the missing ones; for legacy * "unknown scopes" tokens, the array is empty. * * @example * ```tsx * function BackupButton({ wallet }) { * const { status, missingScopes, isChecking, check, requestAccess } = * useGoogleDriveBackupReadiness(); * const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup(); * * // Layer 2: post-flight fallback — needed for legacy users where the stored * // `scopes` is empty so pre-flight returned 'unknown'. Because `lastBackupError` * // is React state it won't update until the next render; read it in a * // useEffect rather than synchronously after `await backupWallet(...)`. * useEffect(() => { * if (lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError)) { * clearBackupError(); * requestAccess(); // re-prompt consent, then caller can retry backupWallet * } * }, [lastBackupError]); * * const onBackup = async () => { * // Layer 1: pre-flight readiness — saves an MPC reshare ceremony when * // we can already tell the upload will fail. * let r = await check(); * if (r.status === 'needs-access') { * r = await requestAccess(); // popup re-consent * if (r.status !== 'ready') return; // user cancelled or relink failed * } * * await backupWallet(wallet, 'GoogleDrive'); * // If the backup failed due to missing scopes, the useEffect above will * // fire on the next render with the updated lastBackupError. * }; * * return ( * <> * {status === 'needs-access' && ( *

We need access to: {missingScopes.join(', ')}

* )} * * * ); * } * ``` */ export declare const useGoogleDriveBackupReadiness: () => UseGoogleDriveBackupReadinessReturn;