import { DownloadIcon, RotateCwIcon, TriangleAlertIcon, XIcon } from "lucide-react"; import { useQueryClient } from "@tanstack/react-query"; import { useCallback, useState } from "react"; import { isElectron } from "../../env"; import { setDesktopUpdateStateQueryData, useDesktopUpdateState, } from "../../lib/desktopUpdateReactQuery"; import { toastManager } from "../ui/toast"; import { getArm64IntelBuildWarningDescription, getDesktopUpdateActionError, getDesktopUpdateButtonTooltip, getDesktopUpdateInstallConfirmationMessage, isDesktopUpdateButtonDisabled, resolveDesktopUpdateButtonAction, shouldShowArm64IntelBuildWarning, shouldShowDesktopUpdateButton, shouldToastDesktopUpdateActionResult, } from "../desktopUpdate.logic"; import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; export function SidebarUpdatePill() { const queryClient = useQueryClient(); const state = useDesktopUpdateState().data ?? null; const [dismissed, setDismissed] = useState(false); const visible = isElectron && shouldShowDesktopUpdateButton(state) && !dismissed; const tooltip = state ? getDesktopUpdateButtonTooltip(state) : "Update available"; const disabled = isDesktopUpdateButtonDisabled(state); const action = state ? resolveDesktopUpdateButtonAction(state) : "none"; const showArm64Warning = isElectron && shouldShowArm64IntelBuildWarning(state); const arm64Description = state && showArm64Warning ? getArm64IntelBuildWarningDescription(state) : null; const handleAction = useCallback(() => { const bridge = window.desktopBridge; if (!bridge || !state) return; if (disabled || action === "none") return; if (action === "download") { void bridge .downloadUpdate() .then((result) => { setDesktopUpdateStateQueryData(queryClient, result.state); if (result.completed) { toastManager.add({ type: "success", title: "Update downloaded", description: "Restart the app from the update button to install it.", }); } if (!shouldToastDesktopUpdateActionResult(result)) return; const actionError = getDesktopUpdateActionError(result); if (!actionError) return; toastManager.add({ type: "error", title: "Could not download update", description: actionError, }); }) .catch((error) => { toastManager.add({ type: "error", title: "Could not start update download", description: error instanceof Error ? error.message : "An unexpected error occurred.", }); }); return; } if (action === "install") { const confirmed = window.confirm(getDesktopUpdateInstallConfirmationMessage(state)); if (!confirmed) return; void bridge .installUpdate() .then((result) => { setDesktopUpdateStateQueryData(queryClient, result.state); if (!shouldToastDesktopUpdateActionResult(result)) return; const actionError = getDesktopUpdateActionError(result); if (!actionError) return; toastManager.add({ type: "error", title: "Could not install update", description: actionError, }); }) .catch((error) => { toastManager.add({ type: "error", title: "Could not install update", description: error instanceof Error ? error.message : "An unexpected error occurred.", }); }); } }, [action, disabled, queryClient, state]); if (!visible && !showArm64Warning) return null; return (
{showArm64Warning && arm64Description && ( Intel build on Apple Silicon {arm64Description} )} {visible && (
{action === "install" ? ( <> Restart to update ) : state?.status === "downloading" ? ( <> Downloading {typeof state.downloadPercent === "number" ? ` (${Math.floor(state.downloadPercent)}%)` : "…"} ) : ( <> Update available )} } /> {tooltip} {action === "download" && ( setDismissed(true)} > } /> Dismiss until next launch )}
)}
); }