Skip to content

Commit 5239018

Browse files
authored
Merge pull request #15 from aaditagrawal/feat/check-for-updates
feat: add Check for Updates in Settings + version bump to 0.0.3
2 parents 38118fb + 0d96911 commit 5239018

File tree

8 files changed

+173
-13
lines changed

8 files changed

+173
-13
lines changed

apps/desktop/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@t3tools/desktop",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"private": true,
55
"main": "dist-electron/main.js",
66
"scripts": {

apps/desktop/src/main.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state";
5656
const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state";
5757
const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download";
5858
const UPDATE_INSTALL_CHANNEL = "desktop:update-install";
59+
const UPDATE_CHECK_CHANNEL = "desktop:update-check";
5960
const STATE_DIR =
6061
process.env.T3CODE_STATE_DIR?.trim() || Path.join(OS.homedir(), ".t3", "userdata");
6162
const DESKTOP_SCHEME = "t3";
@@ -1185,6 +1186,12 @@ function registerIpcHandlers(): void {
11851186
ipcMain.removeHandler(UPDATE_GET_STATE_CHANNEL);
11861187
ipcMain.handle(UPDATE_GET_STATE_CHANNEL, async () => updateState);
11871188

1189+
ipcMain.removeHandler(UPDATE_CHECK_CHANNEL);
1190+
ipcMain.handle(UPDATE_CHECK_CHANNEL, async () => {
1191+
await checkForUpdates("manual");
1192+
return updateState;
1193+
});
1194+
11881195
ipcMain.removeHandler(UPDATE_DOWNLOAD_CHANNEL);
11891196
ipcMain.handle(UPDATE_DOWNLOAD_CHANNEL, async () => {
11901197
const result = await downloadAvailableUpdate();

apps/desktop/src/preload.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state";
1111
const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state";
1212
const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download";
1313
const UPDATE_INSTALL_CHANNEL = "desktop:update-install";
14+
const UPDATE_CHECK_CHANNEL = "desktop:update-check";
1415
const wsUrl = process.env.T3CODE_DESKTOP_WS_URL ?? null;
1516

1617
contextBridge.exposeInMainWorld("desktopBridge", {
@@ -32,6 +33,7 @@ contextBridge.exposeInMainWorld("desktopBridge", {
3233
};
3334
},
3435
getUpdateState: () => ipcRenderer.invoke(UPDATE_GET_STATE_CHANNEL),
36+
checkForUpdate: () => ipcRenderer.invoke(UPDATE_CHECK_CHANNEL),
3537
downloadUpdate: () => ipcRenderer.invoke(UPDATE_DOWNLOAD_CHANNEL),
3638
installUpdate: () => ipcRenderer.invoke(UPDATE_INSTALL_CHANNEL),
3739
onUpdateState: (listener) => {

apps/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "t3",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"repository": {
55
"type": "git",
66
"url": "https://github.com/pingdotgg/t3code",

apps/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@t3tools/web",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"private": true,
55
"type": "module",
66
"scripts": {

apps/web/src/routes/_chat.settings.tsx

Lines changed: 159 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createFileRoute } from "@tanstack/react-router";
22
import { useQuery } from "@tanstack/react-query";
3-
import { useCallback, useState } from "react";
4-
import { type ProviderKind } from "@t3tools/contracts";
3+
import { useCallback, useEffect, useState } from "react";
4+
import { type DesktopUpdateState, type ProviderKind } from "@t3tools/contracts";
55
import { getModelOptions, normalizeModelSlug } from "@t3tools/shared/model";
66

77
import {
@@ -211,6 +211,82 @@ function SettingsRouteView() {
211211
Partial<Record<ProviderKind, string | null>>
212212
>({});
213213

214+
const [updateState, setUpdateState] = useState<DesktopUpdateState | null>(null);
215+
const [isCheckingUpdate, setIsCheckingUpdate] = useState(false);
216+
217+
const hasDesktopBridge = isElectron && !!window.desktopBridge;
218+
219+
useEffect(() => {
220+
if (!hasDesktopBridge) return;
221+
const bridge = window.desktopBridge!;
222+
void bridge
223+
.getUpdateState()
224+
.then(setUpdateState)
225+
.catch(() => {});
226+
const unsubscribe = bridge.onUpdateState(setUpdateState);
227+
return unsubscribe;
228+
}, [hasDesktopBridge]);
229+
230+
const handleCheckForUpdate = useCallback(async () => {
231+
if (!hasDesktopBridge) return;
232+
setIsCheckingUpdate(true);
233+
try {
234+
const state = await window.desktopBridge!.checkForUpdate();
235+
setUpdateState(state);
236+
} catch {
237+
setUpdateState((prev) =>
238+
prev
239+
? {
240+
...prev,
241+
status: "error",
242+
message: "Failed to check for updates.",
243+
errorContext: "check",
244+
}
245+
: prev,
246+
);
247+
} finally {
248+
setIsCheckingUpdate(false);
249+
}
250+
}, [hasDesktopBridge]);
251+
252+
const handleDownloadUpdate = useCallback(async () => {
253+
if (!hasDesktopBridge) return;
254+
try {
255+
const result = await window.desktopBridge!.downloadUpdate();
256+
setUpdateState(result.state);
257+
} catch (error) {
258+
setUpdateState((prev) =>
259+
prev
260+
? {
261+
...prev,
262+
status: "error",
263+
message: error instanceof Error ? error.message : "Failed to download update.",
264+
errorContext: "download",
265+
}
266+
: prev,
267+
);
268+
}
269+
}, [hasDesktopBridge]);
270+
271+
const handleInstallUpdate = useCallback(async () => {
272+
if (!hasDesktopBridge) return;
273+
try {
274+
const result = await window.desktopBridge!.installUpdate();
275+
setUpdateState(result.state);
276+
} catch (error) {
277+
setUpdateState((prev) =>
278+
prev
279+
? {
280+
...prev,
281+
status: "error",
282+
message: error instanceof Error ? error.message : "Failed to install update.",
283+
errorContext: "install",
284+
}
285+
: prev,
286+
);
287+
}
288+
}, [hasDesktopBridge]);
289+
214290
const codexBinaryPath = settings.codexBinaryPath;
215291
const codexHomePath = settings.codexHomePath;
216292
const accentColor = settings.accentColor;
@@ -995,14 +1071,88 @@ function SettingsRouteView() {
9951071
</p>
9961072
</div>
9971073

998-
<div className="flex items-center justify-between rounded-lg border border-border bg-background px-3 py-2">
999-
<div>
1000-
<p className="text-sm font-medium text-foreground">Version</p>
1001-
<p className="text-xs text-muted-foreground">
1002-
Current version of the application.
1003-
</p>
1074+
<div className="space-y-3">
1075+
<div className="flex items-center justify-between rounded-lg border border-border bg-background px-3 py-2">
1076+
<div>
1077+
<p className="text-sm font-medium text-foreground">Version</p>
1078+
<p className="text-xs text-muted-foreground">
1079+
{updateState?.status === "up-to-date"
1080+
? "You're on the latest version."
1081+
: updateState?.status === "checking"
1082+
? "Checking for updates..."
1083+
: updateState?.status === "available"
1084+
? `Version ${updateState.availableVersion ?? "unknown"} is available.`
1085+
: updateState?.status === "downloading"
1086+
? `Downloading update${typeof updateState.downloadPercent === "number" ? ` (${Math.floor(updateState.downloadPercent)}%)` : ""}...`
1087+
: updateState?.status === "downloaded"
1088+
? `Version ${updateState.downloadedVersion ?? updateState.availableVersion ?? "unknown"} is ready to install.`
1089+
: updateState?.status === "error"
1090+
? (updateState.message ?? "Update check failed.")
1091+
: "Current version of the application."}
1092+
</p>
1093+
{updateState?.checkedAt ? (
1094+
<p className="mt-0.5 text-[11px] text-muted-foreground/70">
1095+
Last checked: {new Date(updateState.checkedAt).toLocaleString()}
1096+
</p>
1097+
) : null}
1098+
</div>
1099+
<div className="ml-3 flex shrink-0 items-center gap-2">
1100+
<code className="text-xs font-medium text-muted-foreground">{APP_VERSION}</code>
1101+
{hasDesktopBridge ? (
1102+
<>
1103+
{updateState?.status === "available" ? (
1104+
<Button size="xs" onClick={handleDownloadUpdate}>
1105+
Download
1106+
</Button>
1107+
) : null}
1108+
{updateState?.status === "downloaded" ? (
1109+
<Button size="xs" onClick={handleInstallUpdate}>
1110+
Restart & Install
1111+
</Button>
1112+
) : null}
1113+
{updateState?.status === "error" &&
1114+
updateState.errorContext === "download" &&
1115+
updateState.availableVersion ? (
1116+
<Button size="xs" variant="outline" onClick={handleDownloadUpdate}>
1117+
Retry Download
1118+
</Button>
1119+
) : null}
1120+
{updateState?.status === "error" &&
1121+
updateState.errorContext === "install" &&
1122+
updateState.downloadedVersion ? (
1123+
<Button size="xs" variant="outline" onClick={handleInstallUpdate}>
1124+
Retry Install
1125+
</Button>
1126+
) : null}
1127+
<Button
1128+
size="xs"
1129+
variant="outline"
1130+
disabled={
1131+
isCheckingUpdate ||
1132+
updateState?.status === "checking" ||
1133+
updateState?.status === "downloading"
1134+
}
1135+
onClick={handleCheckForUpdate}
1136+
>
1137+
{isCheckingUpdate || updateState?.status === "checking"
1138+
? "Checking..."
1139+
: "Check for Updates"}
1140+
</Button>
1141+
</>
1142+
) : null}
1143+
</div>
10041144
</div>
1005-
<code className="text-xs font-medium text-muted-foreground">{APP_VERSION}</code>
1145+
{updateState?.status === "downloading" &&
1146+
typeof updateState.downloadPercent === "number" ? (
1147+
<div className="px-1">
1148+
<div className="h-1.5 w-full overflow-hidden rounded-full bg-muted">
1149+
<div
1150+
className="h-full rounded-full bg-primary transition-all duration-300"
1151+
style={{ width: `${updateState.downloadPercent}%` }}
1152+
/>
1153+
</div>
1154+
</div>
1155+
) : null}
10061156
</div>
10071157
</section>
10081158
</div>

packages/contracts/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@t3tools/contracts",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"private": true,
55
"files": [
66
"dist"

packages/contracts/src/ipc.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export interface DesktopBridge {
117117
openExternal: (url: string) => Promise<boolean>;
118118
onMenuAction: (listener: (action: string) => void) => () => void;
119119
getUpdateState: () => Promise<DesktopUpdateState>;
120+
checkForUpdate: () => Promise<DesktopUpdateState>;
120121
downloadUpdate: () => Promise<DesktopUpdateActionResult>;
121122
installUpdate: () => Promise<DesktopUpdateActionResult>;
122123
onUpdateState: (listener: (state: DesktopUpdateState) => void) => () => void;

0 commit comments

Comments
 (0)