fix(playlists-ui): surface YouTube 403s + rollback remove; drop dup name/import
BUGS: - push/sync/revert (PF1/PF2/PF4) swallowed every failure into a generic warning, so a missing-write-scope 403 gave no hint and no "Connect your YouTube account" affordance (unlike Channels). Route all three through notifyYouTubeActionError (no wizard handle on this screen, so message-only for the 403). - removeItem (PF5) reset the order optimistically then awaited the delete with no catch: a failed call left the row gone locally (and an unhandled rejection). Now resync via refreshAll on either path (api.req surfaces the error dialog). CLEANUP: - Drop the dead `Check` lucide import (PC6; only AddToPlaylist uses it). - Use the module plName(detail) helper instead of the inline watch_later-vs-name ternary that shadowed it in push/revert (PC7). tsc green, localdev boots healthy.
This commit is contained in:
parent
ecb2e0b749
commit
c0e941bc0c
1 changed files with 18 additions and 14 deletions
|
|
@ -19,7 +19,6 @@ import { CSS } from "@dnd-kit/utilities";
|
|||
import {
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
Check,
|
||||
ExternalLink,
|
||||
GripVertical,
|
||||
ListPlus,
|
||||
|
|
@ -37,6 +36,7 @@ import {
|
|||
import { api, type Playlist, type Video } from "../lib/api";
|
||||
import { formatDuration } from "../lib/format";
|
||||
import { notify } from "../lib/notifications";
|
||||
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
|
||||
import { getAccountRaw, LS, readAccountMerged, setAccountRaw, writeAccount } from "../lib/storage";
|
||||
import { useUndoable } from "../lib/useUndoable";
|
||||
const PlayerModal = lazy(() => import("./PlayerModal"));
|
||||
|
|
@ -347,14 +347,13 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
|||
danger: true,
|
||||
});
|
||||
if (!ok) return;
|
||||
const plName = detail.kind === "watch_later" ? t("playlists.watchLater") : detail.name;
|
||||
setReverting(true);
|
||||
try {
|
||||
await api.revertPlaylist(selectedId);
|
||||
refreshAll();
|
||||
notify({ level: "success", message: t("playlists.revertDone", { name: plName }) });
|
||||
} catch {
|
||||
notify({ level: "warning", message: t("playlists.revertFailed") });
|
||||
notify({ level: "success", message: t("playlists.revertDone", { name: plName(detail) }) });
|
||||
} catch (e) {
|
||||
notifyYouTubeActionError(e, t("playlists.revertFailed"));
|
||||
} finally {
|
||||
setReverting(false);
|
||||
}
|
||||
|
|
@ -362,12 +361,11 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
|||
|
||||
async function pushToYoutube() {
|
||||
if (selectedId == null || !detail || pushing) return;
|
||||
const plName = detail.kind === "watch_later" ? t("playlists.watchLater") : detail.name;
|
||||
setPushing(true);
|
||||
try {
|
||||
const plan = await api.playlistPushPlan(selectedId);
|
||||
if (plan.action === "update" && !plan.to_insert && !plan.to_delete && !plan.to_reorder) {
|
||||
notify({ level: "info", message: t("playlists.pushUpToDate", { name: plName }) });
|
||||
notify({ level: "info", message: t("playlists.pushUpToDate", { name: plName(detail) }) });
|
||||
return;
|
||||
}
|
||||
if (!plan.affordable) {
|
||||
|
|
@ -406,10 +404,10 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
|||
if (r.failures.length) {
|
||||
notify({ level: "warning", message: t("playlists.pushPartial", { count: r.failures.length }) });
|
||||
} else {
|
||||
notify({ level: "success", message: t("playlists.pushDone", { name: plName }) });
|
||||
notify({ level: "success", message: t("playlists.pushDone", { name: plName(detail) }) });
|
||||
}
|
||||
} catch {
|
||||
notify({ level: "warning", message: t("playlists.pushFailed") });
|
||||
} catch (e) {
|
||||
notifyYouTubeActionError(e, t("playlists.pushFailed"));
|
||||
} finally {
|
||||
setPushing(false);
|
||||
}
|
||||
|
|
@ -423,8 +421,8 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
|||
qc.invalidateQueries({ queryKey: ["playlists"] });
|
||||
qc.invalidateQueries({ queryKey: ["playlist"] });
|
||||
notify({ level: "success", message: t("playlists.syncedToast", { count: r.synced }) });
|
||||
} catch {
|
||||
notify({ level: "warning", message: t("playlists.syncFailed") });
|
||||
} catch (e) {
|
||||
notifyYouTubeActionError(e, t("playlists.syncFailed"));
|
||||
} finally {
|
||||
setSyncing(false);
|
||||
}
|
||||
|
|
@ -463,8 +461,14 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
|||
const next = items.filter((v) => v.id !== videoId);
|
||||
order.reset(next);
|
||||
lastSetRef.current = idsKey(next);
|
||||
try {
|
||||
await api.removeFromPlaylist(selectedId, videoId);
|
||||
refreshAll();
|
||||
} catch {
|
||||
// The optimistic removal above didn't stick — resync from the server to restore the row.
|
||||
// api.req already surfaced the error to the user via the global error dialog.
|
||||
refreshAll();
|
||||
}
|
||||
}
|
||||
|
||||
async function saveRename() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue