fix(api): a gone row and a refused playlist action are said in the reader's language, and a failure is reported once
Deep-review round 3 of bug hunt 2026-09.
- get_or_404 / owned_or_404 answer a structured {code: "not_found", reason: <what>}
(errors.notFound.* in HU+EN, generic fallback). The structured-404 fix of round 2 had gone into the
ad-hoc `_own_playlist` (CLAUDE.md's "Not" pattern) while the shared helpers stayed string-only, so
views, tags, pins, Plex playlists, invites and users still 404'd in English. `_own_playlist` now
goes through owned_or_404; admin's hand-rolled user lookups use get_or_404.
- A move into a list that is gone names the TARGET (reason "move_target") — "this playlist doesn't
exist any more" was read as the list on screen, which still exists.
- Every refusal in the playlists router is a {code: "playlist", reason} (playlists.errors.* HU+EN)
instead of a bare English string shown verbatim in the Hungarian UI.
- `req` / `uploadReq` mark the errors they already put on screen (the error dialog, the connection
notice, the 401 sign-out), and notifyActionError skips those. The watched toggles, the saved views
and a playlist delete toasted a fault the dialog was already showing; Playlists' two hand-written
status filters now go through the same check.
This commit is contained in:
@@ -40,7 +40,7 @@ import {
|
||||
X,
|
||||
Youtube,
|
||||
} from "lucide-react";
|
||||
import { api, HttpError, type Playlist, type PlaylistDetail, type Video } from "../lib/api";
|
||||
import { api, type Playlist, type PlaylistDetail, type Video } from "../lib/api";
|
||||
import { notify } from "../lib/notifications";
|
||||
import { notifyActionError } from "../lib/actionErrors";
|
||||
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
|
||||
@@ -576,12 +576,10 @@ export default function Playlists() {
|
||||
refreshAll(listId);
|
||||
return true;
|
||||
} catch (e) {
|
||||
// `req` raises the global dialog for 4xx-validation and 5xx, but leaves 403/404 to the caller
|
||||
// (the list was deleted or de-owned meanwhile) — so say it here, as moveItemTo does, or a
|
||||
// confirmed removal fails in silence (inside the full-screen player there is not even a row
|
||||
// snapping back to show it).
|
||||
if (e instanceof HttpError && (e.status === 404 || e.status === 403))
|
||||
notifyActionError(e, t("playlists.removeFailed"));
|
||||
// Said here, as moveItemTo does, or a confirmed removal fails in silence (inside the
|
||||
// full-screen player there is not even a row snapping back to show it) — unless `req` already
|
||||
// said it (the error dialog, the connection notice), which `notifyActionError` checks.
|
||||
notifyActionError(e, t("playlists.removeFailed"));
|
||||
refreshAll(listId); // …and resync, which restores any row the optimistic removal took out
|
||||
return false;
|
||||
}
|
||||
@@ -613,11 +611,9 @@ export default function Playlists() {
|
||||
// every move failure a user can actually reach is a 404 (the 400 `same_playlist` case is already
|
||||
// impossible — the submenu never offers the current list). So the row used to snap back with no
|
||||
// explanation and the shipped moveErrors strings were unreachable (E10 review round 1).
|
||||
// ONLY the statuses `req` deliberately leaves to the caller. It already opens the blocking
|
||||
// error dialog for 400/409/422 and 5xx and raises the connectivity notice for a bad gateway,
|
||||
// so toasting unconditionally reported a transport fault twice (E10 review round 2).
|
||||
if (e instanceof HttpError && (e.status === 404 || e.status === 403))
|
||||
notifyActionError(e, t("playlists.moveFailed"));
|
||||
// A fault `req` already reported (the error dialog, the connection notice) is not toasted
|
||||
// again — `notifyActionError` checks (E10 review round 2 found it reported twice).
|
||||
notifyActionError(e, t("playlists.moveFailed"));
|
||||
}
|
||||
// BOTH sides changed, and so did this video's membership. `qk.playlist(selectedId)` is an exact
|
||||
// key, so it cannot cover the target — invalidate the whole `["playlist"]` prefix, exactly as
|
||||
|
||||
Reference in New Issue
Block a user