fix(web): only announce a state change after the server confirms it
The 'Marked watched' / 'Hidden' notices (and resolving a stale notice on unwatch/unhide) fired synchronously, before the api.setState call resolved. When the write failed — e.g. the API was unreachable — the optimistic card override was rolled back in .catch, but the notice had already been emitted and lingered, falsely claiming a change that never persisted. Move all three branches into the .then() so the notice is produced only once the server has actually applied the change; on failure the card reverts and nothing is announced (the connection-lost status already explains why).
This commit is contained in:
parent
928bbbb4b8
commit
3f2fb339d3
1 changed files with 36 additions and 30 deletions
|
|
@ -144,18 +144,14 @@ export default function Feed({
|
||||||
(id: string, status: string) => {
|
(id: string, status: string) => {
|
||||||
setOverrides((o) => ({ ...o, [id]: status }));
|
setOverrides((o) => ({ ...o, [id]: status }));
|
||||||
// Refetch once the server has the change so other views (e.g. Hidden) are in sync.
|
// Refetch once the server has the change so other views (e.g. Hidden) are in sync.
|
||||||
|
// Announce the change only AFTER the server confirms it: a "Marked watched"/"Hidden"
|
||||||
|
// notice (or resolving a stale one) is a claim of success, so firing it optimistically
|
||||||
|
// would falsely report a change that the .catch below is about to roll back (e.g. when
|
||||||
|
// the API is unreachable).
|
||||||
api
|
api
|
||||||
.setState(id, status)
|
.setState(id, status)
|
||||||
.then(() => qc.invalidateQueries({ queryKey: ["feed"] }))
|
.then(() => {
|
||||||
.catch(() =>
|
qc.invalidateQueries({ queryKey: ["feed"] });
|
||||||
// Server rejected the change — drop the optimistic override so the card
|
|
||||||
// reverts to the real (unchanged) state instead of staying phantom-hidden.
|
|
||||||
setOverrides((o) => {
|
|
||||||
const next = { ...o };
|
|
||||||
delete next[id];
|
|
||||||
return next;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
if (status === "hidden") {
|
if (status === "hidden") {
|
||||||
const v = loadedRef.current.find((x) => x.id === id);
|
const v = loadedRef.current.find((x) => x.id === id);
|
||||||
notify({
|
notify({
|
||||||
|
|
@ -181,10 +177,20 @@ export default function Feed({
|
||||||
meta: { kind: "video-watched", videoId: id, title: v?.title ?? "this video" },
|
meta: { kind: "video-watched", videoId: id, title: v?.title ?? "this video" },
|
||||||
});
|
});
|
||||||
} else if (status === "new") {
|
} else if (status === "new") {
|
||||||
// Unhide / unwatch (from a card, the toast's Undo, or the center): quietly resolve any
|
// Unhide / unwatch (from a card, the toast's Undo, or the center): quietly resolve
|
||||||
// stale hide/watch notice for this video so it doesn't linger with a dead action.
|
// any stale hide/watch notice for this video so it doesn't linger with a dead action.
|
||||||
resolveVideo(id);
|
resolveVideo(id);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(() =>
|
||||||
|
// Server rejected the change — drop the optimistic override so the card
|
||||||
|
// reverts to the real (unchanged) state instead of staying phantom-hidden.
|
||||||
|
setOverrides((o) => {
|
||||||
|
const next = { ...o };
|
||||||
|
delete next[id];
|
||||||
|
return next;
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[qc]
|
[qc]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue