mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-06-22 14:13: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"> = [];
|
const origins: Array<"spark" | "apm"> = [];
|
||||||
if (isOriginEnabled(storeFilter.value, "spark") && sparkAvailable.value) {
|
if (isOriginEnabled(storeFilter.value, "spark") && sparkAvailable.value) {
|
||||||
origins.push("spark");
|
origins.push("spark");
|
||||||
@@ -1572,11 +1574,16 @@ const refreshInstalledSyncCandidates = async (): Promise<void> => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!isCurrentRequest()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
syncCandidateApps.value = mergeInstalledApps(
|
syncCandidateApps.value = mergeInstalledApps(
|
||||||
syncCandidateApps.value,
|
syncCandidateApps.value,
|
||||||
refreshedApps,
|
refreshedApps,
|
||||||
origins,
|
origins,
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const syncInstalledAppsToAccount = async (): Promise<void> => {
|
const syncInstalledAppsToAccount = async (): Promise<void> => {
|
||||||
@@ -1588,13 +1595,12 @@ const syncInstalledAppsToAccount = async (): Promise<void> => {
|
|||||||
syncRequestGeneration.value = generation;
|
syncRequestGeneration.value = generation;
|
||||||
syncLoading.value = true;
|
syncLoading.value = true;
|
||||||
try {
|
try {
|
||||||
await refreshInstalledSyncCandidates();
|
const refreshed = await refreshInstalledSyncCandidates(
|
||||||
if (
|
() =>
|
||||||
syncRequestGeneration.value !== generation ||
|
syncRequestGeneration.value === generation &&
|
||||||
currentUser.value?.id !== userId
|
currentUser.value?.id === userId,
|
||||||
) {
|
);
|
||||||
return;
|
if (!refreshed) return;
|
||||||
}
|
|
||||||
const items = buildSyncItems(syncCandidateApps.value);
|
const items = buildSyncItems(syncCandidateApps.value);
|
||||||
await uploadSyncedAppList({
|
await uploadSyncedAppList({
|
||||||
clientArch: window.apm_store.arch || "amd64",
|
clientArch: window.apm_store.arch || "amd64",
|
||||||
@@ -1641,7 +1647,10 @@ const openRestoreFromAccount = async (): Promise<void> => {
|
|||||||
restoreError.value = "";
|
restoreError.value = "";
|
||||||
restoreItems.value = [];
|
restoreItems.value = [];
|
||||||
try {
|
try {
|
||||||
await refreshInstalledSyncCandidates();
|
const refreshed = await refreshInstalledSyncCandidates(() =>
|
||||||
|
isCurrentRestoreRequest(generation, userId),
|
||||||
|
);
|
||||||
|
if (!refreshed) return;
|
||||||
const result = await fetchSyncedAppList();
|
const result = await fetchSyncedAppList();
|
||||||
if (!isCurrentRestoreRequest(generation, userId)) return;
|
if (!isCurrentRestoreRequest(generation, userId)) return;
|
||||||
restoreItems.value = result?.items || [];
|
restoreItems.value = result?.items || [];
|
||||||
|
|||||||
@@ -583,4 +583,83 @@ describe("App account placeholders", () => {
|
|||||||
});
|
});
|
||||||
syncUpload.resolve(syncedList([]));
|
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 = () => {
|
const handleSyncClick = () => {
|
||||||
emit(props.loggedIn ? "sync-to-account" : "request-login");
|
if (props.loggedIn) {
|
||||||
|
emit("sync-to-account");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit("request-login");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRestoreClick = () => {
|
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) => {
|
const onOverlayWheel = (e: WheelEvent) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user