1import type { DesktopBridge, DesktopUpdateState } from "@t3tools/contracts";
2import { ensureLocalApi } from "../localApi";
3import { stackedThreadToast, toastManager } from "./ui/toast";
4import {
5 getDesktopUpdateActionError,
6 getDesktopUpdateInstallConfirmationMessage,
7 resolveDesktopUpdateButtonAction,
8 shouldToastDesktopUpdateActionResult,
9} from "./desktopUpdate.logic";
10import { showDesktopUpdateDownloadedToast } from "./desktopUpdate.toast";
11import type { ReportLocalUpdateError } from "./sidebar/useUpdateEverything";
12import { prepareUiRefresh } from "~/uiRefresh";
13
14export const toastUpdateError: ReportLocalUpdateError = (title, description) => {
15 toastManager.add(stackedThreadToast({ type: "error", title, description }));
16};
17
18/** Downloads updates; activation waits for confirmation or a safe idle refresh. */
19export async function downloadLocalUpdate(
20 bridge: Pick<DesktopBridge, "downloadUpdate" | "openExternal">,
21 {
22 notifyDownloaded,
23 reportError = toastUpdateError,
24 }: { readonly notifyDownloaded: boolean; readonly reportError?: ReportLocalUpdateError },
25): Promise<boolean> {
26 try {
27 const result = await bridge.downloadUpdate();
28 if (result.completed && notifyDownloaded) {
29 showDesktopUpdateDownloadedToast(bridge, result.state);
30 }
31 const actionError = shouldToastDesktopUpdateActionResult(result)
32 ? getDesktopUpdateActionError(result)
33 : null;
34 if (actionError) reportError("Could not download update", actionError);
35 return result.completed;
36 } catch (error) {
37 reportError(
38 "Could not start update download",
39 error instanceof Error ? error.message : "An unexpected error occurred.",
40 );
41 return false;
42 }
43}
44
45/** Applies a prepared UI in place or starts an app relaunch after the caller confirms. */
46export async function installLocalUpdate(
47 bridge: Pick<DesktopBridge, "installUpdate" | "openExternal">,
48 reportError: ReportLocalUpdateError = toastUpdateError,
49 options: { readonly automatic?: boolean } = {},
50): Promise<boolean> {
51 try {
52 if (options.automatic && !prepareUiRefresh()) return false;
53 const result = await bridge.installUpdate();
54 if (!result.accepted) {
55 showDesktopUpdateDownloadedToast(bridge, result.state);
56 return false;
57 }
58 const actionError = shouldToastDesktopUpdateActionResult(result)
59 ? getDesktopUpdateActionError(result)
60 : null;
61 if (!actionError) {
62 if (result.completed && result.state.uiVersion) {
63 const preserved = prepareUiRefresh();
64 if (!options.automatic || preserved) window.location.reload();
65 }
66 return true;
67 }
68 reportError("Could not install update", actionError);
69 return false;
70 } catch (error) {
71 reportError(
72 "Could not install update",
73 error instanceof Error ? error.message : "An unexpected error occurred.",
74 );
75 return false;
76 }
77}
78
79export async function performDesktopUpdate(
80 bridge: DesktopBridge,
81 state: DesktopUpdateState,
82): Promise<void> {
83 const action = resolveDesktopUpdateButtonAction(state);
84 try {
85 if (action === "download") {
86 await downloadLocalUpdate(bridge, { notifyDownloaded: true });
87 } else if (action === "install") {
88 if (
89 state.installKind === "ui" ||
90 (await ensureLocalApi().dialogs.confirm(getDesktopUpdateInstallConfirmationMessage(state)))
91 ) {
92 await installLocalUpdate(bridge);
93 }
94 } else {
95 const result = await bridge.checkForUpdate();
96 if (!result.checked)
97 toastUpdateError(
98 "Could not check for updates",
99 result.state.message ?? "Automatic updates are not available in this build.",
100 );
101 }
102 } catch (error) {
103 toastUpdateError("Could not update app", error instanceof Error ? error.message : "Try again.");
104 }
105}