import React, { createContext, useContext, useState, useEffect, useCallback, useRef, } from "react"; import type { UpdateInfo, UpdateHandle } from "../lib/updater"; import { checkForUpdate } from "../lib/updater"; interface UpdateContextValue { // 更新状态 hasUpdate: boolean; updateInfo: UpdateInfo | null; updateHandle: UpdateHandle | null; isChecking: boolean; error: string | null; // 提示状态 isDismissed: boolean; dismissUpdate: () => void; // 操作方法 checkUpdate: () => Promise; resetDismiss: () => void; } const UpdateContext = createContext(undefined); export function UpdateProvider({ children }: { children: React.ReactNode }) { const DISMISSED_VERSION_KEY = "ccswitch:update:dismissedVersion"; const LEGACY_DISMISSED_KEY = "dismissedUpdateVersion"; // 兼容旧键 const [hasUpdate, setHasUpdate] = useState(false); const [updateInfo, setUpdateInfo] = useState(null); const [updateHandle, setUpdateHandle] = useState(null); const [isChecking, setIsChecking] = useState(false); const [error, setError] = useState(null); const [isDismissed, setIsDismissed] = useState(false); // 从 localStorage 读取已关闭的版本 useEffect(() => { const current = updateInfo?.availableVersion; if (!current) return; // 读取新键;若不存在,尝试迁移旧键 let dismissedVersion = localStorage.getItem(DISMISSED_VERSION_KEY); if (!dismissedVersion) { const legacy = localStorage.getItem(LEGACY_DISMISSED_KEY); if (legacy) { localStorage.setItem(DISMISSED_VERSION_KEY, legacy); localStorage.removeItem(LEGACY_DISMISSED_KEY); dismissedVersion = legacy; } } setIsDismissed(dismissedVersion === current); }, [updateInfo?.availableVersion]); const isCheckingRef = useRef(false); const checkUpdate = useCallback(async () => { if (isCheckingRef.current) return false; isCheckingRef.current = true; setIsChecking(true); setError(null); try { const result = await checkForUpdate({ timeout: 30000 }); if (result.status === "available") { setHasUpdate(true); setUpdateInfo(result.info); setUpdateHandle(result.update); // 检查是否已经关闭过这个版本的提醒 let dismissedVersion = localStorage.getItem(DISMISSED_VERSION_KEY); if (!dismissedVersion) { const legacy = localStorage.getItem(LEGACY_DISMISSED_KEY); if (legacy) { localStorage.setItem(DISMISSED_VERSION_KEY, legacy); localStorage.removeItem(LEGACY_DISMISSED_KEY); dismissedVersion = legacy; } } setIsDismissed(dismissedVersion === result.info.availableVersion); return true; // 有更新 } else { setHasUpdate(false); setUpdateInfo(null); setUpdateHandle(null); setIsDismissed(false); return false; // 已是最新 } } catch (err) { console.error("检查更新失败:", err); setError(err instanceof Error ? err.message : "检查更新失败"); setHasUpdate(false); throw err; // 抛出错误让调用方处理 } finally { setIsChecking(false); isCheckingRef.current = false; } }, []); const dismissUpdate = useCallback(() => { setIsDismissed(true); if (updateInfo?.availableVersion) { localStorage.setItem(DISMISSED_VERSION_KEY, updateInfo.availableVersion); // 清理旧键 localStorage.removeItem(LEGACY_DISMISSED_KEY); } }, [updateInfo?.availableVersion]); const resetDismiss = useCallback(() => { setIsDismissed(false); localStorage.removeItem(DISMISSED_VERSION_KEY); localStorage.removeItem(LEGACY_DISMISSED_KEY); }, []); // 应用启动时自动检查更新 useEffect(() => { // 延迟1秒后检查,避免影响启动体验 const timer = setTimeout(() => { checkUpdate().catch(console.error); }, 1000); return () => clearTimeout(timer); }, [checkUpdate]); const value: UpdateContextValue = { hasUpdate, updateInfo, updateHandle, isChecking, error, isDismissed, dismissUpdate, checkUpdate, resetDismiss, }; return ( {children} ); } export function useUpdate() { const context = useContext(UpdateContext); if (!context) { throw new Error("useUpdate must be used within UpdateProvider"); } return context; }