mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-06-22 22:23:49 +08:00
fix(sync): guard stale installed refreshes
This commit is contained in:
+18
-9
@@ -1541,7 +1541,9 @@ const loadDownloadedHistory = async (): Promise<void> => {
|
||||
}
|
||||
};
|
||||
|
||||
const refreshInstalledSyncCandidates = async (): Promise<void> => {
|
||||
const refreshInstalledSyncCandidates = async (
|
||||
isCurrentRequest: () => boolean,
|
||||
): Promise<boolean> => {
|
||||
const origins: Array<"spark" | "apm"> = [];
|
||||
if (isOriginEnabled(storeFilter.value, "spark") && sparkAvailable.value) {
|
||||
origins.push("spark");
|
||||
@@ -1572,11 +1574,16 @@ const refreshInstalledSyncCandidates = async (): Promise<void> => {
|
||||
}),
|
||||
);
|
||||
|
||||
if (!isCurrentRequest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
syncCandidateApps.value = mergeInstalledApps(
|
||||
syncCandidateApps.value,
|
||||
refreshedApps,
|
||||
origins,
|
||||
);
|
||||
return true;
|
||||
};
|
||||
|
||||
const syncInstalledAppsToAccount = async (): Promise<void> => {
|
||||
@@ -1588,13 +1595,12 @@ const syncInstalledAppsToAccount = async (): Promise<void> => {
|
||||
syncRequestGeneration.value = generation;
|
||||
syncLoading.value = true;
|
||||
try {
|
||||
await refreshInstalledSyncCandidates();
|
||||
if (
|
||||
syncRequestGeneration.value !== generation ||
|
||||
currentUser.value?.id !== userId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const refreshed = await refreshInstalledSyncCandidates(
|
||||
() =>
|
||||
syncRequestGeneration.value === generation &&
|
||||
currentUser.value?.id === userId,
|
||||
);
|
||||
if (!refreshed) return;
|
||||
const items = buildSyncItems(syncCandidateApps.value);
|
||||
await uploadSyncedAppList({
|
||||
clientArch: window.apm_store.arch || "amd64",
|
||||
@@ -1641,7 +1647,10 @@ const openRestoreFromAccount = async (): Promise<void> => {
|
||||
restoreError.value = "";
|
||||
restoreItems.value = [];
|
||||
try {
|
||||
await refreshInstalledSyncCandidates();
|
||||
const refreshed = await refreshInstalledSyncCandidates(() =>
|
||||
isCurrentRestoreRequest(generation, userId),
|
||||
);
|
||||
if (!refreshed) return;
|
||||
const result = await fetchSyncedAppList();
|
||||
if (!isCurrentRestoreRequest(generation, userId)) return;
|
||||
restoreItems.value = result?.items || [];
|
||||
|
||||
@@ -583,4 +583,83 @@ describe("App account placeholders", () => {
|
||||
});
|
||||
syncUpload.resolve(syncedList([]));
|
||||
});
|
||||
|
||||
it("does not upload stale sync candidates after logout", async () => {
|
||||
const slowInstalled = createDeferred<{
|
||||
success: true;
|
||||
apps: Array<{
|
||||
pkgname: string;
|
||||
name: string;
|
||||
version: string;
|
||||
arch: string;
|
||||
flags: string;
|
||||
origin: "apm";
|
||||
}>;
|
||||
}>();
|
||||
let listInstalledCalls = 0;
|
||||
invoke.mockImplementation(async (channel: string) => {
|
||||
if (channel === "get-store-filter") return "apm";
|
||||
if (channel === "check-spark-available") return false;
|
||||
if (channel === "check-apm-available") return true;
|
||||
if (channel === "get-app-version") return "5.0.0";
|
||||
if (channel === "get-system-info") return { distro: "deepin 25" };
|
||||
if (channel === "list-installed") {
|
||||
listInstalledCalls += 1;
|
||||
if (listInstalledCalls === 1) return slowInstalled.promise;
|
||||
return { success: true, apps: [] };
|
||||
}
|
||||
return [];
|
||||
});
|
||||
vi.mocked(uploadSyncedAppList).mockResolvedValue(syncedList([]));
|
||||
render(App);
|
||||
|
||||
await fireEvent.click(await screen.findByRole("button", { name: /Momen/ }));
|
||||
await fireEvent.click(screen.getByText("用户管理"));
|
||||
await fireEvent.click(
|
||||
await screen.findByRole("button", { name: "立即同步" }),
|
||||
);
|
||||
await fireEvent.click(
|
||||
await screen.findByRole("button", { name: /^Momen$/ }),
|
||||
);
|
||||
if (!screen.queryByText("退出登录")) {
|
||||
await fireEvent.click(
|
||||
await screen.findByRole("button", { name: /^Momen$/ }),
|
||||
);
|
||||
}
|
||||
await fireEvent.click(screen.getByText("退出登录"));
|
||||
|
||||
slowInstalled.resolve({
|
||||
success: true,
|
||||
apps: [
|
||||
{
|
||||
pkgname: "wps",
|
||||
name: "WPS",
|
||||
version: "1.0.0",
|
||||
arch: "amd64",
|
||||
flags: "installed",
|
||||
origin: "apm",
|
||||
},
|
||||
],
|
||||
});
|
||||
await slowInstalled.promise;
|
||||
await Promise.resolve();
|
||||
|
||||
setSecondUserSession();
|
||||
const secondUserButton = await screen.findByRole("button", {
|
||||
name: /^Second User$/,
|
||||
});
|
||||
if (!screen.queryByText("用户管理")) {
|
||||
await fireEvent.click(secondUserButton);
|
||||
}
|
||||
await fireEvent.click(await screen.findByText("用户管理"));
|
||||
await fireEvent.click(
|
||||
await screen.findByRole("button", { name: "立即同步" }),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(uploadSyncedAppList).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ items: [] }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -254,11 +254,21 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const handleSyncClick = () => {
|
||||
emit(props.loggedIn ? "sync-to-account" : "request-login");
|
||||
if (props.loggedIn) {
|
||||
emit("sync-to-account");
|
||||
return;
|
||||
}
|
||||
|
||||
emit("request-login");
|
||||
};
|
||||
|
||||
const handleRestoreClick = () => {
|
||||
emit(props.loggedIn ? "restore-from-account" : "request-login");
|
||||
if (props.loggedIn) {
|
||||
emit("restore-from-account");
|
||||
return;
|
||||
}
|
||||
|
||||
emit("request-login");
|
||||
};
|
||||
|
||||
const onOverlayWheel = (e: WheelEvent) => {
|
||||
|
||||
Reference in New Issue
Block a user