Skip to content

Commit fc6b326

Browse files
authored
fix: show clickable desktop update notices in header (#10622)
* fix: show clickable desktop update notices in header * fix: recover stalled desktop update checks * Delete dashboard/tests/desktopUpdateCheck.test.mjs
1 parent e5e21b7 commit fc6b326

3 files changed

Lines changed: 64 additions & 13 deletions

File tree

‎dashboard/src/layouts/full/vertical-header/VerticalHeader.vue‎

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,16 @@ const desktopUpdateCurrentVersion = ref("-");
134134
const desktopUpdateLatestVersion = ref("-");
135135
const desktopUpdateStatus = ref("");
136136
const desktopDownloadProgress = ref<AstrBotDesktopAppUpdateProgress | null>(null);
137+
const appHasNewVersion = computed(() =>
138+
isDesktopReleaseMode.value
139+
? desktopUpdateHasNewVersion.value
140+
: hasNewVersion.value,
141+
);
142+
const hasAvailableUpdate = computed(
143+
() =>
144+
appHasNewVersion.value ||
145+
(!isDesktopReleaseMode.value && dashboardHasNewVersion.value),
146+
);
137147
const isChatPath = computed(
138148
() => route.path === "/chat" || route.path.startsWith("/chat/"),
139149
);
@@ -297,10 +307,11 @@ function cancelDesktopUpdate() {
297307
desktopUpdateDialog.value = false;
298308
}
299309
300-
async function openDesktopUpdateDialog() {
310+
async function checkDesktopUpdate(showDialog = false) {
301311
if (desktopUpdateInstalling.value) return;
312+
if (showDialog) desktopUpdateDialog.value = true;
313+
if (desktopUpdateChecking.value) return;
302314
desktopDownloadProgress.value = null;
303-
desktopUpdateDialog.value = true;
304315
desktopUpdateChecking.value = true;
305316
desktopUpdateInstalling.value = false;
306317
desktopUpdateHasNewVersion.value = false;
@@ -310,16 +321,29 @@ async function openDesktopUpdateDialog() {
310321
311322
const bridge = getAppUpdaterBridge();
312323
if (!bridge) {
324+
console.warn("[desktop-update] App updater bridge is unavailable.");
313325
desktopUpdateChecking.value = false;
314326
desktopUpdateStatus.value = t(
315327
"core.header.updateDialog.desktopApp.checkFailed",
316328
);
317329
return;
318330
}
319331
332+
let checkTimeout: ReturnType<typeof setTimeout> | undefined;
320333
try {
321-
const result = await bridge.checkForAppUpdate();
334+
const result = await Promise.race([
335+
bridge.checkForAppUpdate(),
336+
new Promise<never>((_, reject) => {
337+
checkTimeout = setTimeout(() => {
338+
reject(new Error("Desktop app update check timed out."));
339+
}, 30_000);
340+
}),
341+
]);
322342
if (!result?.ok) {
343+
console.warn(
344+
"[desktop-update] Failed to check for app updates.",
345+
result?.reason || "Unknown error",
346+
);
323347
desktopUpdateCurrentVersion.value = result?.currentVersion || "-";
324348
desktopUpdateLatestVersion.value =
325349
result?.latestVersion || result?.currentVersion || "-";
@@ -336,11 +360,12 @@ async function openDesktopUpdateDialog() {
336360
? t("core.header.updateDialog.desktopApp.hasNewVersion")
337361
: t("core.header.updateDialog.desktopApp.isLatest");
338362
} catch (error) {
339-
console.error(error);
363+
console.error("[desktop-update] Failed to check for app updates.", error);
340364
desktopUpdateStatus.value = t(
341365
"core.header.updateDialog.desktopApp.checkFailed",
342366
);
343367
} finally {
368+
if (checkTimeout !== undefined) clearTimeout(checkTimeout);
344369
desktopUpdateChecking.value = false;
345370
}
346371
}
@@ -391,7 +416,7 @@ async function confirmDesktopUpdate() {
391416
392417
function handleUpdateClick() {
393418
if (isDesktopReleaseMode.value) {
394-
void openDesktopUpdateDialog();
419+
void checkDesktopUpdate(true);
395420
return;
396421
}
397422
checkUpdate();
@@ -1043,6 +1068,7 @@ onMounted(async () => {
10431068
isDesktopReleaseMode.value = runtimeInfo.isDesktopRuntime;
10441069
if (isDesktopReleaseMode.value) {
10451070
dashboardHasNewVersion.value = false;
1071+
void checkDesktopUpdate();
10461072
}
10471073
10481074
// The toolbar band doubles as the window drag region on desktop (no native title bar).
@@ -1089,15 +1115,20 @@ onMounted(async () => {
10891115

10901116
<v-spacer />
10911117

1092-
<!-- 版本提示信息 - 在手机上隐藏 -->
1093-
<div v-if="!isChatPath" class="mr-4 hidden-xs">
1094-
<small v-if="hasNewVersion">
1118+
<!-- Desktop keeps the update notice visible in both Bot and Chat modes. -->
1119+
<button
1120+
v-if="hasAvailableUpdate && (!isChatPath || isDesktopReleaseMode)"
1121+
type="button"
1122+
class="update-notice mr-4 hidden-xs text-no-wrap"
1123+
@click="handleUpdateClick"
1124+
>
1125+
<small v-if="appHasNewVersion">
10951126
{{ t("core.header.version.hasNewVersion") }}
10961127
</small>
10971128
<small v-else-if="dashboardHasNewVersion && !isDesktopReleaseMode">
10981129
{{ t("core.header.version.dashboardHasNewVersion") }}
10991130
</small>
1100-
</div>
1131+
</button>
11011132

11021133
<div class="header-actions" :class="{ 'chat-header-actions': isChatPath }">
11031134
<v-btn
@@ -1155,7 +1186,9 @@ onMounted(async () => {
11551186
icon
11561187
:ripple="false"
11571188
>
1158-
<v-icon>mdi-dots-vertical</v-icon>
1189+
<v-badge :model-value="hasAvailableUpdate" color="primary" dot floating>
1190+
<v-icon>mdi-dots-vertical</v-icon>
1191+
</v-badge>
11591192
<span class="header-toolbar-label">{{ t("core.navigation.options") }}</span>
11601193
</v-btn>
11611194
</template>
@@ -1324,9 +1357,7 @@ onMounted(async () => {
13241357
}}</v-list-item-title>
13251358
<template
13261359
v-slot:append
1327-
v-if="
1328-
hasNewVersion || (dashboardHasNewVersion && !isDesktopReleaseMode)
1329-
"
1360+
v-if="hasAvailableUpdate"
13301361
>
13311362
<v-chip size="x-small" color="primary" variant="tonal" class="ml-2"
13321363
>!</v-chip
@@ -2499,6 +2530,14 @@ onMounted(async () => {
24992530
user-select: none;
25002531
}
25012532
2533+
.top-header .update-notice {
2534+
cursor: pointer;
2535+
}
2536+
2537+
.top-header .update-notice:hover {
2538+
text-decoration: underline;
2539+
}
2540+
25022541
/* Mount point for page-contextual toolbar content (see the template note). */
25032542
.app-header-context {
25042543
display: flex;

‎docs/en/deploy/astrbot/desktop.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ Repository: [AstrBotDevs/AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot
2626
2. Download the installer for your operating system (e.g. `.exe`, `.dmg`, `.rpm`, `.deb`).
2727
3. Launch the desktop client after installation and follow the setup wizard to complete initialization.
2828

29+
## Check for Updates
30+
31+
The desktop client automatically checks for app updates when the main interface loads. When an update is available, the header displays “AstrBot has a new version!” in both Bot and Chat modes. A dot also appears on the options menu in the top-right corner, and the update entry inside the menu is marked.
32+
33+
Click “AstrBot has a new version!” in the header to open the desktop app update dialog. You can also open the options menu and select the update entry to check manually and confirm installation. Automatic checks do not download or install updates. If a check fails, use the same entry to retry.
34+
2935
## Difference from Launcher Deployment
3036

3137
- Desktop client: focuses on an out-of-the-box GUI experience.

‎docs/zh/deploy/astrbot/desktop.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
2. 下载与你系统对应的安装包(如 `.exe`、`.dmg`、`.rpm`、`.deb`)。
2727
3. 安装完成后启动桌面客户端,按向导完成初始化。
2828

29+
## 检查更新
30+
31+
桌面客户端进入主界面后会自动检查客户端新版本。发现更新时,Bot 模式和聊天模式的顶部栏都会显示「AstrBot 有新版本!」文字提示。右上角的功能菜单也会显示提示圆点,菜单内的「更新」入口会显示标记。
32+
33+
点击顶部的「AstrBot 有新版本!」文字,即可打开桌面客户端更新弹窗。也可以点击右上角功能菜单中的「更新」,手动检查版本并确认安装。自动检查不会直接下载或安装更新;检查失败时,也可以通过此入口重试。
34+
2935
## 与启动器部署的区别
3036

3137
- 桌面客户端:更偏向开箱即用的 GUI 体验。

0 commit comments

Comments
 (0)