Merge improvement/playlist-header-polish: tidy playlist detail header
This commit is contained in:
commit
4ec1a0488c
4 changed files with 51 additions and 31 deletions
|
|
@ -20,6 +20,7 @@ import {
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
ArrowUp,
|
ArrowUp,
|
||||||
Check,
|
Check,
|
||||||
|
ExternalLink,
|
||||||
GripVertical,
|
GripVertical,
|
||||||
ListPlus,
|
ListPlus,
|
||||||
Pencil,
|
Pencil,
|
||||||
|
|
@ -320,9 +321,8 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
||||||
}, [sortedPlaylists]);
|
}, [sortedPlaylists]);
|
||||||
|
|
||||||
const builtin = detail?.kind === "watch_later";
|
const builtin = detail?.kind === "watch_later";
|
||||||
// YouTube-mirrored playlists can be edited locally and synced back (the read-sync skips
|
// YouTube-mirrored playlists can be edited locally and synced back (the read-sync skips a
|
||||||
// a dirty mirror so it won't clobber unpushed edits); the chip just marks their origin.
|
// dirty mirror so it won't clobber unpushed edits); the YT-link button marks their origin.
|
||||||
const mirrored = detail?.source === "youtube";
|
|
||||||
const editable = !builtin;
|
const editable = !builtin;
|
||||||
const linked = editable && !!detail?.yt_playlist_id;
|
const linked = editable && !!detail?.yt_playlist_id;
|
||||||
const [syncing, setSyncing] = useState(false);
|
const [syncing, setSyncing] = useState(false);
|
||||||
|
|
@ -651,7 +651,7 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
||||||
className="text-lg font-semibold bg-card border border-border rounded-md px-2 py-0.5 outline-none focus:border-accent"
|
className="text-lg font-semibold bg-card border border-border rounded-md px-2 py-0.5 outline-none focus:border-accent"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
<h2 className="text-lg font-semibold truncate">{plName(detail)}</h2>
|
<h2 className="text-lg font-semibold truncate">{plName(detail)}</h2>
|
||||||
{editable && (
|
{editable && (
|
||||||
<button
|
<button
|
||||||
|
|
@ -660,17 +660,30 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
||||||
setRenaming(true);
|
setRenaming(true);
|
||||||
}}
|
}}
|
||||||
title={t("playlists.rename")}
|
title={t("playlists.rename")}
|
||||||
className="text-muted hover:text-fg"
|
aria-label={t("playlists.rename")}
|
||||||
|
className="shrink-0 text-muted hover:text-fg"
|
||||||
>
|
>
|
||||||
<Pencil className="w-3.5 h-3.5" />
|
<Pencil className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{detail.yt_playlist_id && (
|
||||||
|
<a
|
||||||
|
href={`https://www.youtube.com/playlist?list=${detail.yt_playlist_id}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
title={t("playlists.openOnYoutube")}
|
||||||
|
aria-label={t("playlists.openOnYoutube")}
|
||||||
|
className="shrink-0 text-muted hover:text-accent"
|
||||||
|
>
|
||||||
|
<ExternalLink className="w-3.5 h-3.5" />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="text-xs text-muted mt-0.5">
|
<div className="text-xs text-muted mt-0.5">
|
||||||
{t("playlists.itemCount", { count: items.length })}
|
{t("playlists.itemCount", { count: items.length })}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 mt-3 flex-wrap">
|
<div className="flex gap-1.5 mt-3 flex-wrap items-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => items.length && setPlayingIndex(0)}
|
onClick={() => items.length && setPlayingIndex(0)}
|
||||||
disabled={!items.length}
|
disabled={!items.length}
|
||||||
|
|
@ -683,7 +696,8 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
||||||
onClick={pushToYoutube}
|
onClick={pushToYoutube}
|
||||||
disabled={pushing}
|
disabled={pushing}
|
||||||
title={linked ? t("playlists.pushToYoutube") : t("playlists.exportToYoutube")}
|
title={linked ? t("playlists.pushToYoutube") : t("playlists.exportToYoutube")}
|
||||||
className={`inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-lg border transition disabled:opacity-40 ${
|
aria-label={linked ? t("playlists.pushToYoutube") : t("playlists.exportToYoutube")}
|
||||||
|
className={`inline-flex items-center justify-center p-2 rounded-lg border transition disabled:opacity-40 ${
|
||||||
detail.dirty
|
detail.dirty
|
||||||
? "border-accent text-accent hover:bg-accent/10"
|
? "border-accent text-accent hover:bg-accent/10"
|
||||||
: "border-border text-muted hover:text-fg hover:border-accent"
|
: "border-border text-muted hover:text-fg hover:border-accent"
|
||||||
|
|
@ -696,36 +710,36 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
||||||
) : (
|
) : (
|
||||||
<Upload className="w-3.5 h-3.5" />
|
<Upload className="w-3.5 h-3.5" />
|
||||||
)}
|
)}
|
||||||
{linked ? t("playlists.pushToYoutube") : t("playlists.exportToYoutube")}
|
</button>
|
||||||
|
)}
|
||||||
|
{linked && detail.dirty && (
|
||||||
|
<button
|
||||||
|
onClick={revertToYoutube}
|
||||||
|
disabled={reverting}
|
||||||
|
title={t("playlists.revert")}
|
||||||
|
aria-label={t("playlists.revert")}
|
||||||
|
className="inline-flex items-center justify-center p-2 rounded-lg border border-border text-muted hover:text-fg hover:border-accent transition disabled:opacity-40"
|
||||||
|
>
|
||||||
|
<RotateCcw className={`w-3.5 h-3.5 ${reverting ? "animate-spin" : ""}`} />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{editable && (
|
{editable && (
|
||||||
<button
|
<button
|
||||||
onClick={deletePlaylist}
|
onClick={deletePlaylist}
|
||||||
className="inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-lg border border-border text-muted hover:text-fg hover:border-accent transition"
|
title={t("playlists.delete")}
|
||||||
|
aria-label={t("playlists.delete")}
|
||||||
|
className="inline-flex items-center justify-center p-2 rounded-lg border border-border text-muted hover:text-red-400 hover:border-red-400/50 transition"
|
||||||
>
|
>
|
||||||
<Trash2 className="w-3.5 h-3.5" /> {t("playlists.delete")}
|
<Trash2 className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{linked && detail.dirty && (
|
{linked && detail.dirty && (
|
||||||
<>
|
<span
|
||||||
<button
|
title={t("playlists.unsyncedHint")}
|
||||||
onClick={revertToYoutube}
|
className="inline-flex items-center gap-1.5 text-[11px] text-accent ml-1"
|
||||||
disabled={reverting}
|
>
|
||||||
title={t("playlists.revert")}
|
<span className="w-1.5 h-1.5 rounded-full bg-accent" />
|
||||||
className="inline-flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-lg border border-border text-muted hover:text-fg hover:border-accent transition disabled:opacity-40"
|
{t("playlists.unsynced")}
|
||||||
>
|
|
||||||
<RotateCcw className={`w-3.5 h-3.5 ${reverting ? "animate-spin" : ""}`} />
|
|
||||||
{t("playlists.revert")}
|
|
||||||
</button>
|
|
||||||
<span className="inline-flex items-center gap-1.5 text-[11px] px-2.5 py-1.5 rounded-lg text-accent">
|
|
||||||
{t("playlists.unsynced")}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{mirrored && (
|
|
||||||
<span className="inline-flex items-center gap-1.5 text-[11px] px-2.5 py-1.5 rounded-lg text-muted">
|
|
||||||
<Youtube className="w-3.5 h-3.5" /> {t("playlists.ytReadonly")}
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
"addToPlaylist": "Zur Wiedergabeliste hinzufügen",
|
"addToPlaylist": "Zur Wiedergabeliste hinzufügen",
|
||||||
"exportToYoutube": "Zu YouTube exportieren",
|
"exportToYoutube": "Zu YouTube exportieren",
|
||||||
"pushToYoutube": "Mit YouTube synchronisieren",
|
"pushToYoutube": "Mit YouTube synchronisieren",
|
||||||
"unsynced": "Nicht synchronisierte Änderungen",
|
"unsynced": "Nicht synchronisiert",
|
||||||
|
"unsyncedHint": "Du hast lokale Änderungen, die noch nicht mit YouTube synchronisiert sind.",
|
||||||
|
"openOnYoutube": "Auf YouTube öffnen",
|
||||||
"revert": "Von YouTube zurücksetzen",
|
"revert": "Von YouTube zurücksetzen",
|
||||||
"revertTitle": "Lokale Änderungen verwerfen?",
|
"revertTitle": "Lokale Änderungen verwerfen?",
|
||||||
"revertMsg": "Dies lädt die Wiedergabeliste von YouTube neu und verwirft deine nicht synchronisierten lokalen Änderungen (Reihenfolge, Hinzufügungen, Entfernungen, Umbenennung). Fortfahren?",
|
"revertMsg": "Dies lädt die Wiedergabeliste von YouTube neu und verwirft deine nicht synchronisierten lokalen Änderungen (Reihenfolge, Hinzufügungen, Entfernungen, Umbenennung). Fortfahren?",
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
"addToPlaylist": "Add to playlist",
|
"addToPlaylist": "Add to playlist",
|
||||||
"exportToYoutube": "Export to YouTube",
|
"exportToYoutube": "Export to YouTube",
|
||||||
"pushToYoutube": "Sync to YouTube",
|
"pushToYoutube": "Sync to YouTube",
|
||||||
"unsynced": "Unsynced changes",
|
"unsynced": "Unsynced",
|
||||||
|
"unsyncedHint": "You have local changes not yet synced to YouTube.",
|
||||||
|
"openOnYoutube": "Open on YouTube",
|
||||||
"revert": "Reset to YouTube",
|
"revert": "Reset to YouTube",
|
||||||
"revertTitle": "Discard local changes?",
|
"revertTitle": "Discard local changes?",
|
||||||
"revertMsg": "This reloads the playlist from YouTube and discards your unsynced local changes (order, additions, removals, rename). Continue?",
|
"revertMsg": "This reloads the playlist from YouTube and discards your unsynced local changes (order, additions, removals, rename). Continue?",
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
"addToPlaylist": "Hozzáadás listához",
|
"addToPlaylist": "Hozzáadás listához",
|
||||||
"exportToYoutube": "Exportálás YouTube-ra",
|
"exportToYoutube": "Exportálás YouTube-ra",
|
||||||
"pushToYoutube": "Szinkron YouTube-ra",
|
"pushToYoutube": "Szinkron YouTube-ra",
|
||||||
"unsynced": "Nem szinkronizált változások",
|
"unsynced": "Nincs szinkronizálva",
|
||||||
|
"unsyncedHint": "Vannak helyi módosításaid, amelyek még nincsenek a YouTube-ra szinkronizálva.",
|
||||||
|
"openOnYoutube": "Megnyitás YouTube-on",
|
||||||
"revert": "Visszaállítás YouTube-ról",
|
"revert": "Visszaállítás YouTube-ról",
|
||||||
"revertTitle": "Eldobod a helyi módosításokat?",
|
"revertTitle": "Eldobod a helyi módosításokat?",
|
||||||
"revertMsg": "Ez újratölti a listát a YouTube-ról, és eldobja a nem szinkronizált helyi módosításaidat (sorrend, hozzáadás, eltávolítás, átnevezés). Folytatod?",
|
"revertMsg": "Ez újratölti a listát a YouTube-ról, és eldobja a nem szinkronizált helyi módosításaidat (sorrend, hozzáadás, eltávolítás, átnevezés). Folytatod?",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue