diff --git a/src/games/clairobscur/data/collected-items.ts b/src/games/clairobscur/data/collected-items.ts index 5bc776f..a0a19f3 100644 --- a/src/games/clairobscur/data/collected-items.ts +++ b/src/games/clairobscur/data/collected-items.ts @@ -85,7 +85,10 @@ const useCollect = () => { payload: { itemId: input.itemId, itemName: input.itemName }, idempotencyKey: `${ENTITY}:upsert:${anonUserId}:${input.itemId}`, serverUpdatedAt: toIso(existing?.updatedAt), - summary: { title: `Collected: ${input.itemName}` }, + summary: { + title: `Collected: ${input.itemName}`, + gameId: "clairobscur", + }, }), ]); return record; @@ -123,7 +126,10 @@ const useUncollect = () => { payload: { itemId: input.itemId, itemName: input.itemName }, idempotencyKey: `${ENTITY}:delete:${anonUserId}:${input.itemId}`, serverUpdatedAt: toIso(existing?.updatedAt), - summary: { title: `Uncollected: ${input.itemName}` }, + summary: { + title: `Uncollected: ${input.itemName}`, + gameId: "clairobscur", + }, }), ]); return { ok: true as const }; diff --git a/src/games/remnant2/data/collected-items/use-collected-items.ts b/src/games/remnant2/data/collected-items/use-collected-items.ts index b83cb41..9bdc5ab 100644 --- a/src/games/remnant2/data/collected-items/use-collected-items.ts +++ b/src/games/remnant2/data/collected-items/use-collected-items.ts @@ -85,7 +85,10 @@ const useCollect = () => { payload: { itemId: input.itemId, itemName: input.itemName }, idempotencyKey: `${ENTITY}:upsert:${anonUserId}:${input.itemId}`, serverUpdatedAt: toIso(existing?.updatedAt), - summary: { title: `Collected: ${input.itemName}` }, + summary: { + title: `Collected: ${input.itemName}`, + gameId: "remnant2", + }, }), ]); return record; @@ -123,7 +126,10 @@ const useUncollect = () => { payload: { itemId: input.itemId, itemName: input.itemName }, idempotencyKey: `${ENTITY}:delete:${anonUserId}:${input.itemId}`, serverUpdatedAt: toIso(existing?.updatedAt), - summary: { title: `Uncollected: ${input.itemName}` }, + summary: { + title: `Uncollected: ${input.itemName}`, + gameId: "remnant2", + }, }), ]); return { ok: true as const }; diff --git a/src/games/remnant2/data/created-builds/use-created-builds.ts b/src/games/remnant2/data/created-builds/use-created-builds.ts index 85ff563..a3ff711 100644 --- a/src/games/remnant2/data/created-builds/use-created-builds.ts +++ b/src/games/remnant2/data/created-builds/use-created-builds.ts @@ -106,6 +106,7 @@ const useUpdate = () => { title: input.name ? `Updated build: ${input.name}` : "Updated build", + gameId: "remnant2", }, }), ]); @@ -140,7 +141,7 @@ const useRemove = () => { payload: input, idempotencyKey: `${ENTITY}:delete:${anonUserId}:${input.buildId}`, serverUpdatedAt: toIso(existing?.updatedAt), - summary: { title: "Deleted build" }, + summary: { title: "Deleted build", gameId: "remnant2" }, }), ]); return { ok: true as const }; diff --git a/src/games/slaythespire2/data/collected-items.ts b/src/games/slaythespire2/data/collected-items.ts index 04e10bb..13ebec5 100644 --- a/src/games/slaythespire2/data/collected-items.ts +++ b/src/games/slaythespire2/data/collected-items.ts @@ -85,7 +85,10 @@ const useCollect = () => { payload: { itemId: input.itemId, itemName: input.itemName }, idempotencyKey: `${ENTITY}:upsert:${anonUserId}:${input.itemId}`, serverUpdatedAt: toIso(existing?.updatedAt), - summary: { title: `Collected: ${input.itemName}` }, + summary: { + title: `Collected: ${input.itemName}`, + gameId: "slaythespire2", + }, }), ]); return record; @@ -123,7 +126,10 @@ const useUncollect = () => { payload: { itemId: input.itemId, itemName: input.itemName }, idempotencyKey: `${ENTITY}:delete:${anonUserId}:${input.itemId}`, serverUpdatedAt: toIso(existing?.updatedAt), - summary: { title: `Uncollected: ${input.itemName}` }, + summary: { + title: `Uncollected: ${input.itemName}`, + gameId: "slaythespire2", + }, }), ]); return { ok: true as const }; diff --git a/src/routes/account/profile/$userId/data-sync.tsx b/src/routes/account/profile/$userId/data-sync.tsx index 3cc24e8..af17b7d 100644 --- a/src/routes/account/profile/$userId/data-sync.tsx +++ b/src/routes/account/profile/$userId/data-sync.tsx @@ -13,10 +13,8 @@ import { notifications } from "@mantine/notifications"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { createFileRoute } from "@tanstack/react-router"; import { type PropsWithChildren, useEffect, useState } from "react"; -import { - clearSynced, - deleteOp, -} from "#/features/sync/local-data/queue/pending-ops"; +import { useGameId } from "#/features/game/use-game-id.ts"; +import { deleteOp } from "#/features/sync/local-data/queue/pending-ops"; import type { PendingOp } from "#/features/sync/local-data/queue/types"; import { usePendingOps } from "#/features/sync/local-data/queue/use-pending-ops"; import { @@ -27,8 +25,8 @@ import { buildTabHead, loadProfileTabData, } from "#/features/user/profile-tab-head.ts"; -import { getGameMetadata } from "#/registry/game-public-registry.tsx"; import { useSession } from "#/integrations/better-auth/auth-client"; +import { getGameMetadata } from "#/registry/game-public-registry.tsx"; const PendingList = ({ ops, @@ -243,10 +241,19 @@ function DataSync() { const pending = usePendingOps(); const canSync = !!session?.user?.id && online; + // Scope the tab to the active game. Ops with no `summary.gameId` are + // account-wide (profile, primary avatar) and stay visible under every game. + const gameId = useGameId(); + const visibleOps = (pending.data ?? []).filter( + (op) => op.summary?.gameId == null || op.summary.gameId === gameId, + ); + const pendingOps = visibleOps.filter((op) => op.status !== "synced"); + const syncedOps = visibleOps.filter((op) => op.status === "synced"); + const syncAll = useMutation({ mutationFn: async () => { - if (!pending.data) return null; - return syncOps(pending.data); + if (!visibleOps.length) return null; + return syncOps(visibleOps); }, onSuccess: (report) => { if (!report) return; @@ -269,7 +276,9 @@ function DataSync() { }); const clear = useMutation({ - mutationFn: () => clearSynced(), + mutationFn: async () => { + await Promise.all(syncedOps.map((op) => deleteOp(op.id))); + }, onSuccess: () => queryClient.invalidateQueries({ queryKey: ["sync-queue"] }), }); @@ -321,14 +330,14 @@ function DataSync() { size="xs" variant="default" onClick={() => clear.mutate()} - disabled={clear.isPending} + disabled={clear.isPending || !syncedOps.length} > Clear synced @@ -340,7 +349,7 @@ function DataSync() { ) : null} op.status !== "synced")} + ops={pendingOps} onDelete={(id) => { deleteOp(id).then(() => queryClient.invalidateQueries({ queryKey: ["sync-queue"] }), @@ -357,9 +366,7 @@ function DataSync() { : null } /> - op.status === "synced")} - /> + ); }