improve(updates): close update center after component updates

This commit is contained in:
zipg
2026-09-20 00:50:25 +08:00
committed by GitHub
parent f9b984da68
commit fff07eff00
4 changed files with 52 additions and 2 deletions
+14 -1
View File
@@ -28,7 +28,7 @@ import { canDownloadAndInstallUpdate, useAppUpdater } from "@/composables/useApp
import { useMcpUpdateBadge } from "@/composables/useMcpUpdateBadge";
import { useComponentUpdates, type ComponentUpdateCategory } from "@/composables/useComponentUpdates";
import { driverStoreUpdateBadgeCount, showMcpUpdateBadge } from "@/lib/updates/updateBadges";
import { markPendingComponentUpdatesAfterAppUpdate, resolveUpdateAllAction, runPendingComponentUpdatePlan, takePendingComponentUpdatesAfterAppRestart, type PendingComponentUpdatePlan } from "@/lib/updates/componentUpdateOrchestration";
import { markPendingComponentUpdatesAfterAppUpdate, resolveUpdateAllAction, runPendingComponentUpdatePlan, shouldCloseUpdateCenterAfterComponentUpdate, takePendingComponentUpdatesAfterAppRestart, type PendingComponentUpdatePlan } from "@/lib/updates/componentUpdateOrchestration";
import { isUpdatePreviewMockEnabled } from "@/lib/updates/updatePreviewMock";
import { useExportTracker } from "@/composables/useExportTracker";
import { useFileDrop } from "@/composables/useFileDrop";
@@ -1215,9 +1215,22 @@ function handleToolbarUpdateClick() {
function reportComponentUpdateResult(result: Awaited<ReturnType<typeof componentUpdates.installCategory>>) {
const updatedComponents = [result.drivers > 0 ? t("settings.updateDrivers") : "", result.jdbc ? t("settings.updateJdbc") : "", result.mcp ? t("settings.updateMcp") : "", result.plugins > 0 ? t("settings.updatePlugins") : ""].filter(Boolean);
// Only a clean refresh is authoritative; a failed registry check must not clear the toolbar count.
if (result.failed.length === 0) agentDriverUpdateCount.value = componentUpdates.driverUpdateCount.value;
if (updatedComponents.length) toast(t("updates.componentsAutoUpdated", { components: updatedComponents.join(t("updates.componentListSeparator")) }));
if (result.skippedDrivers > 0) toast(t("updates.componentsAutoUpdateSkipped"), 6000);
if (result.failed.length) toast(t("updates.componentsAutoUpdateFailed", { count: result.failed.length }), 6000);
if (
shouldCloseUpdateCenterAfterComponentUpdate({
failedCount: result.failed.length,
skippedDriverCount: result.skippedDrivers,
hasAppUpdate: hasUpdateAvailable.value,
remainingComponentUpdateCount: componentUpdates.totalUpdateCount.value,
})
) {
showUpdateDialog.value = false;
}
}
async function rememberComponentUpdatesForRestartedApp(plan: PendingComponentUpdatePlan = { kind: "auto" }) {
@@ -87,6 +87,16 @@ describe("useComponentUpdates", () => {
expect(updates.totalUpdateCount.value).toBe(4);
});
it("clears the driver count after a successful update and refresh", async () => {
mocks.listInstalledAgents.mockResolvedValueOnce([{ db_type: "mysql", update_available: true }]).mockResolvedValueOnce([{ db_type: "mysql", update_available: false }]);
const updates = useComponentUpdates({ isDesktop: true });
await updates.installCategories(["drivers"]);
expect(mocks.upgradeAllAgents).toHaveBeenCalledOnce();
expect(updates.driverUpdateCount.value).toBe(0);
});
it("installs every enabled component category after an app update", async () => {
const updates = useComponentUpdates({ isDesktop: true });
@@ -1,7 +1,7 @@
// @vitest-environment happy-dom
import { beforeEach, describe, expect, it, vi } from "vitest";
import { clearPendingComponentUpdatesAfterAppUpdate, markPendingComponentUpdatesAfterAppUpdate, resolveUpdateAllAction, runPendingComponentUpdatePlan, takePendingComponentUpdatesAfterAppRestart } from "@/lib/updates/componentUpdateOrchestration";
import { clearPendingComponentUpdatesAfterAppUpdate, markPendingComponentUpdatesAfterAppUpdate, resolveUpdateAllAction, runPendingComponentUpdatePlan, shouldCloseUpdateCenterAfterComponentUpdate, takePendingComponentUpdatesAfterAppRestart } from "@/lib/updates/componentUpdateOrchestration";
describe("component update orchestration", () => {
beforeEach(() => {
@@ -88,6 +88,29 @@ describe("component update orchestration", () => {
expect(resolveUpdateAllAction({ hasAppUpdate: false, appUpdateCanInstall: true, appUpdatePrepared: false, hasComponentUpdates: false })).toBe("none");
});
it("closes the update center after a successful component-only update with nothing left to install", () => {
expect(
shouldCloseUpdateCenterAfterComponentUpdate({
failedCount: 0,
skippedDriverCount: 0,
hasAppUpdate: false,
remainingComponentUpdateCount: 0,
}),
).toBe(true);
});
it("keeps the update center open when an update failed or a driver update was skipped", () => {
const base = { hasAppUpdate: false, remainingComponentUpdateCount: 0 };
expect(shouldCloseUpdateCenterAfterComponentUpdate({ ...base, failedCount: 1, skippedDriverCount: 0 })).toBe(false);
expect(shouldCloseUpdateCenterAfterComponentUpdate({ ...base, failedCount: 0, skippedDriverCount: 1 })).toBe(false);
});
it("keeps the update center open while the app or another component still needs an update", () => {
const base = { failedCount: 0, skippedDriverCount: 0 };
expect(shouldCloseUpdateCenterAfterComponentUpdate({ ...base, hasAppUpdate: true, remainingComponentUpdateCount: 0 })).toBe(false);
expect(shouldCloseUpdateCenterAfterComponentUpdate({ ...base, hasAppUpdate: false, remainingComponentUpdateCount: 1 })).toBe(false);
});
it("can explicitly clear pending state", () => {
markPendingComponentUpdatesAfterAppUpdate("0.6.16", "0.6.17");
clearPendingComponentUpdatesAfterAppUpdate();
@@ -96,6 +96,10 @@ export function resolveUpdateAllAction(options: { hasAppUpdate: boolean; appUpda
return options.appUpdatePrepared ? "defer-components" : "download-app";
}
export function shouldCloseUpdateCenterAfterComponentUpdate(options: { failedCount: number; skippedDriverCount: number; hasAppUpdate: boolean; remainingComponentUpdateCount: number }): boolean {
return options.failedCount === 0 && options.skippedDriverCount === 0 && !options.hasAppUpdate && options.remainingComponentUpdateCount === 0;
}
export function runPendingComponentUpdatePlan<T>(pending: PendingComponentUpdates, updates: { installCategories: (categories: ComponentUpdateCategory[]) => Promise<T>; autoUpdateEnabledComponents: () => Promise<T> }): Promise<T> {
return pending.plan.kind === "manual" ? updates.installCategories(pending.plan.categories) : updates.autoUpdateEnabledComponents();
}