diff --git a/frontend/src/components/Playlists.tsx b/frontend/src/components/Playlists.tsx index cbf846f..c157186 100644 --- a/frontend/src/components/Playlists.tsx +++ b/frontend/src/components/Playlists.tsx @@ -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); - await api.removeFromPlaylist(selectedId, videoId); - refreshAll(); + 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() {