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.
|
||
|---|---|---|
| .. | ||
| src | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.js | ||
| tailwind.config.js | ||
| tsconfig.json | ||
| vite.config.ts | ||