fix(player): persist watched at end-of-playback (atomic upsert)
The in-app player marks a video watched when it reaches the end, firing
POST /videos/{id}/state at the same instant as a progress checkpoint. The
progress endpoint deletes the 'new' video_states row near the end, so the
concurrent state UPDATE matched 0 rows and raised StaleDataError -> 500. The
watched status was never persisted (and Feed swallowed the error), so the
video stayed in the unwatched feed even after a refresh.
- set_video_state: atomic INSERT ... ON CONFLICT (uq_user_video) DO UPDATE for
watched/hidden, immune to a concurrent delete; tolerate StaleDataError on the
'new' branch and in the progress endpoint.
- PlayerModal: skip the redundant near-end progress checkpoint when we just
auto-marked watched, removing the self-inflicted race.
- Feed: drop the optimistic override if the server rejects the change, so a
failed request can't leave a card phantom-hidden.
This commit is contained in:
parent
79249104d4
commit
a09e0dda0c
3 changed files with 57 additions and 18 deletions
|
|
@ -147,7 +147,15 @@ export default function Feed({
|
|||
api
|
||||
.setState(id, status)
|
||||
.then(() => qc.invalidateQueries({ queryKey: ["feed"] }))
|
||||
.catch(() => {});
|
||||
.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;
|
||||
})
|
||||
);
|
||||
if (status === "hidden") {
|
||||
const v = loadedRef.current.find((x) => x.id === id);
|
||||
notify({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue