import { useCallback, useEffect, useMemo, useSyncExternalStore } from "react"; import { checkDesktopAppUpdate, formatVersionWithPrefix, installDesktopAppUpdate, shouldShowDesktopUpdateSection, type DesktopAppUpdateCheckResult, type DesktopAppUpdateCheckIntent, type DesktopAppUpdateInstallResult, } from "@/desktop/updates/desktop-updates"; import { useDesktopSettings } from "@/desktop/settings/desktop-settings"; import { useDesktopIpcErrorReporter } from "@/desktop/hooks/desktop-ipc-error"; import { PENDING_RECHECK_MS, createDesktopAppUpdater, formatStatusText, type DesktopAppUpdateStatus, } from "@/desktop/updates/desktop-app-updater"; import { formatMessageTimestamp } from "@/utils/time"; export type { DesktopAppUpdateStatus }; export interface UseDesktopAppUpdaterReturn { isDesktopApp: boolean; status: DesktopAppUpdateStatus; statusText: string; availableUpdate: DesktopAppUpdateCheckResult | null; errorMessage: string | null; lastCheckedAt: number | null; isChecking: boolean; isInstalling: boolean; checkForUpdates: (options?: { intent?: DesktopAppUpdateCheckIntent; silent?: boolean; }) => Promise; installUpdate: () => Promise; } export function useDesktopAppUpdater(): UseDesktopAppUpdaterReturn { const isDesktopApp = shouldShowDesktopUpdateSection(); const { settings: desktopSettings } = useDesktopSettings(); const releaseChannel = desktopSettings.releaseChannel; const reportError = useDesktopIpcErrorReporter(); const updater = useMemo( () => createDesktopAppUpdater({ port: { checkDesktopAppUpdate, installDesktopAppUpdate, }, now: () => Date.now(), reportInstallError: reportError, }), [reportError], ); const snapshot = useSyncExternalStore( updater.subscribe, updater.getSnapshot, updater.getSnapshot, ); const checkForUpdates = useCallback( async (options: { intent?: DesktopAppUpdateCheckIntent; silent?: boolean } = {}) => { if (!isDesktopApp) { return null; } return updater.checkForUpdates({ releaseChannel, intent: options.intent ?? "manual", silent: options.silent, }); }, [isDesktopApp, releaseChannel, updater], ); const installUpdate = useCallback(async () => { if (!isDesktopApp) { return null; } return updater.installUpdate({ releaseChannel }); }, [isDesktopApp, releaseChannel, updater]); useEffect(() => { if (!isDesktopApp) { return; } void checkForUpdates({ intent: "automatic", silent: true }); }, [checkForUpdates, isDesktopApp]); useEffect(() => { if (!isDesktopApp || snapshot.status !== "pending") { return undefined; } const intervalId = setInterval(() => { void checkForUpdates({ intent: "automatic", silent: true }); }, PENDING_RECHECK_MS); return () => { clearInterval(intervalId); }; }, [checkForUpdates, isDesktopApp, snapshot.status]); return { isDesktopApp, status: snapshot.status, statusText: formatStatusText({ status: snapshot.status, availableUpdate: snapshot.availableUpdate, installMessage: snapshot.installMessage, lastCheckedAt: snapshot.lastCheckedAt, formatVersion: formatVersionWithPrefix, formatLastCheckedAt: (timestamp) => formatMessageTimestamp(new Date(timestamp)), }), availableUpdate: snapshot.availableUpdate, errorMessage: snapshot.errorMessage, lastCheckedAt: snapshot.lastCheckedAt, isChecking: snapshot.isChecking, isInstalling: snapshot.isInstalling, checkForUpdates, installUpdate, }; }