import { useCallback, useEffect, useState } from 'react'; export type RuntimeAppLockReason = 'startup' | 'idle' | 'manual' | 'background' | null; export interface RuntimeAppLockState { initialized: boolean; locked: boolean; reason: RuntimeAppLockReason; version: number; lastLockedAt: number | null; lastUnlockedAt: number | null; lastActivityAt: number | null; } export function normalizeRuntimeAppLockState(input: unknown): RuntimeAppLockState { const record = input && typeof input === 'object' ? input as Record : null; const reason = record?.reason; return { initialized: record?.initialized === true, locked: record?.locked === true, reason: reason === 'startup' || reason === 'idle' || reason === 'manual' || reason === 'background' ? reason : null, version: typeof record?.version === 'number' && Number.isFinite(record.version) ? record.version : 0, lastLockedAt: typeof record?.lastLockedAt === 'number' && Number.isFinite(record.lastLockedAt) ? record.lastLockedAt : null, lastUnlockedAt: typeof record?.lastUnlockedAt === 'number' && Number.isFinite(record.lastUnlockedAt) ? record.lastUnlockedAt : null, lastActivityAt: typeof record?.lastActivityAt === 'number' && Number.isFinite(record.lastActivityAt) ? record.lastActivityAt : null, }; } const DEFAULT_RUNTIME_APP_LOCK_STATE: RuntimeAppLockState = normalizeRuntimeAppLockState(null); export function selectPreferredRuntimeAppLockState( current: RuntimeAppLockState, incoming: RuntimeAppLockState, ): RuntimeAppLockState { if (incoming.version > current.version) return incoming; if (incoming.version < current.version) return current; if (incoming.initialized && !current.initialized) return incoming; if (!incoming.initialized && current.initialized) return current; return incoming; } export function useAppLockRuntime( bridge: Pick< NonNullable, 'getAppLockRuntimeState' | 'onAppLockRuntimeStateChanged' > | null | undefined, ) { // 浏览器远程访问环境下(无 Electron bridge),直接 unlocked const BROWSER_ONLY = typeof window !== 'undefined' && !!(window as any).__REMOTE_TOKEN__; const [runtimeState, setRuntimeState] = useState( BROWSER_ONLY ? { initialized: true, locked: false, reason: null, version: 0, lastLockedAt: null, lastUnlockedAt: null, lastActivityAt: null } : DEFAULT_RUNTIME_APP_LOCK_STATE, ); const refreshRuntimeState = useCallback(async () => { // 浏览器远程访问:直接返回 unlocked if (BROWSER_ONLY) { const unlocked: RuntimeAppLockState = { initialized: true, locked: false, reason: null, version: 0, lastLockedAt: null, lastUnlockedAt: null, lastActivityAt: null, }; setRuntimeState((current) => selectPreferredRuntimeAppLockState(current, unlocked)); return unlocked; } const nextState = await bridge?.getAppLockRuntimeState?.(); const normalized = normalizeRuntimeAppLockState(nextState); setRuntimeState((current) => selectPreferredRuntimeAppLockState(current, normalized)); return normalized; }, [bridge, BROWSER_ONLY]); useEffect(() => { let cancelled = false; const unsubscribe = BROWSER_ONLY ? () => {} : (bridge?.onAppLockRuntimeStateChanged?.((nextState) => { if (cancelled) return; const normalized = normalizeRuntimeAppLockState(nextState); setRuntimeState((current) => selectPreferredRuntimeAppLockState(current, normalized)); }) ?? (() => {})); void refreshRuntimeState().catch(() => { if (cancelled) return; setRuntimeState((current) => selectPreferredRuntimeAppLockState( current, DEFAULT_RUNTIME_APP_LOCK_STATE, )); }); return () => { cancelled = true; unsubscribe(); }; }, [bridge, refreshRuntimeState, BROWSER_ONLY]); return { runtimeState, setRuntimeState, refreshRuntimeState, }; }