diff --git a/package.json b/package.json
index 8949d3c..b6e11aa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "tallynote",
- "version": "1.1.38",
+ "version": "1.1.39",
"private": true,
"type": "module",
"packageManager": "pnpm@9.0.6",
diff --git a/shared/contracts.ts b/shared/contracts.ts
index 19143e0..da4a56a 100644
--- a/shared/contracts.ts
+++ b/shared/contracts.ts
@@ -107,7 +107,7 @@ export const updateApplySchema = z.object({
export const updateDownloadSchema = z.object({
version: z.string().trim().regex(/^v?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/),
- confirm: z.literal(true),
+ confirm: z.boolean().default(true).optional(),
}).strict();
export type ApiError = {
diff --git a/web-next/src/main.tsx b/web-next/src/main.tsx
index b5e5b9c..8845c93 100644
--- a/web-next/src/main.tsx
+++ b/web-next/src/main.tsx
@@ -34,7 +34,7 @@ function App() {
const notify = useCallback((message: string, kind: "success" | "error" | "info" = "info") => {
// The placement container owns the responsive right inset. Keeping the
// item offset at zero avoids pushing narrow-screen notices off canvas.
- const options = { content: message, duration: 4200, placement: "top-right" as const, offset: [0, 76] as [number, number], zIndex: 5000 };
+ const options = { content: message, duration: 4200, placement: "top-right" as const, offset: [24, 76] as [number, number], zIndex: 6000 };
const show = kind === "success" ? NotificationPlugin.success : kind === "error" ? NotificationPlugin.error : NotificationPlugin.info;
void show(options);
}, []);
diff --git a/web-next/src/pages/update/UpdatePage.tsx b/web-next/src/pages/update/UpdatePage.tsx
index 5f44d81..aafd90a 100644
--- a/web-next/src/pages/update/UpdatePage.tsx
+++ b/web-next/src/pages/update/UpdatePage.tsx
@@ -245,6 +245,7 @@ export default function UpdatePage({
const [loading, setLoading] = useState(true);
const [checking, setChecking] = useState(false);
const [error, setError] = useState("");
+ const [modalError, setModalError] = useState("");
const [pollError, setPollError] = useState("");
const [actionBusy, setActionBusy] = useState(false);
const [reloadReady, setReloadReady] = useState(false);
@@ -457,16 +458,20 @@ export default function UpdatePage({
if (!latest) return;
setActionBusy(true);
setError("");
+ setModalError("");
try {
const response = await api<{ job: UpdateJob }>("/api/update/download", {
method: "POST",
- body: JSON.stringify({ version: latest.version }),
+ body: JSON.stringify({ version: latest.version, confirm: true }),
});
setLiveInfo((current) => (current ? { ...current, job: response.job } : current));
setConfirmReadyToDownload(false);
notify?.("已提交下载更新包请求,后台下载中", "info");
} catch (e) {
- setError((e as Error).message);
+ const msg = (e as Error).message;
+ setError(msg);
+ setModalError(msg);
+ notify?.(msg, "error");
} finally {
setActionBusy(false);
}
@@ -477,15 +482,19 @@ export default function UpdatePage({
if (!latest) return;
setActionBusy(true);
setError("");
+ setModalError("");
try {
const response = await api<{ job: UpdateJob }>("/api/update/apply", {
method: "POST",
- body: JSON.stringify({ version: latest.version, confirm: true }),
+ body: JSON.stringify({ version: latest.version, confirm: true, ...(job?.id ? { jobId: job.id } : {}) }),
});
setLiveInfo((current) => (current ? { ...current, job: response.job } : current));
notify?.("已提交升级请求,正在备份并平滑重启…", "info");
} catch (e) {
- setError((e as Error).message);
+ const msg = (e as Error).message;
+ setError(msg);
+ setModalError(msg);
+ notify?.(msg, "error");
} finally {
setActionBusy(false);
}
@@ -751,7 +760,7 @@ export default function UpdatePage({
size="large"
onClick={() => {
setConfirmReadyToDownload(true);
- setShowUpgradeModal(true);
+ setModalError(""); setShowUpgradeModal(true);
}}
disabled={actionBusy}
icon={