Merge improvement/playlist-header-polish: tidy playlist detail header
This commit is contained in:
commit
f6e662ff14
4 changed files with 51 additions and 31 deletions
|
|
@ -20,6 +20,7 @@ import {
|
|||
ArrowDown,
|
||||
ArrowUp,
|
||||
Check,
|
||||
ExternalLink,
|
||||
GripVertical,
|
||||
ListPlus,
|
||||
Pencil,
|
||||
|
|
@ -320,9 +321,8 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
|||
}, [sortedPlaylists]);
|
||||
|
||||
const builtin = detail?.kind === "watch_later";
|
||||
// YouTube-mirrored playlists can be edited locally and synced back (the read-sync skips
|
||||
// a dirty mirror so it won't clobber unpushed edits); the chip just marks their origin.
|
||||
const mirrored = detail?.source === "youtube";
|
||||
// YouTube-mirrored playlists can be edited locally and synced back (the read-sync skips a
|
||||
// dirty mirror so it won't clobber unpushed edits); the YT-link button marks their origin.
|
||||
const editable = !builtin;
|
||||
const linked = editable && !!detail?.yt_playlist_id;
|
||||
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"
|
||||
/>
|
||||
) : (
|
||||
<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>
|
||||
{editable && (
|
||||
<button
|
||||
|
|
@ -660,17 +660,30 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
|||
setRenaming(true);
|
||||
}}
|
||||
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" />
|
||||
</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 className="text-xs text-muted mt-0.5">
|
||||
{t("playlists.itemCount", { count: items.length })}
|
||||
</div>
|
||||
<div className="flex gap-2 mt-3 flex-wrap">
|
||||
<div className="flex gap-1.5 mt-3 flex-wrap items-center">
|
||||
<button
|
||||
onClick={() => items.length && setPlayingIndex(0)}
|
||||
disabled={!items.length}
|
||||
|
|
@ -683,7 +696,8 @@ export default function Playlists({ canWrite }: { canWrite: boolean }) {
|
|||
onClick={pushToYoutube}
|
||||
disabled={pushing}
|
||||
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
|
||||
? "border-accent text-accent hover:bg-accent/10"
|
||||
: "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" />
|
||||
)}
|
||||
{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>
|
||||
)}
|
||||
{editable && (
|
||||
<button
|
||||
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>
|
||||
)}
|
||||
{linked && detail.dirty && (
|
||||
<>
|
||||
<button
|
||||
onClick={revertToYoutube}
|
||||
disabled={reverting}
|
||||
title={t("playlists.revert")}
|
||||
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"
|
||||
>
|
||||
<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
|
||||
title={t("playlists.unsyncedHint")}
|
||||
className="inline-flex items-center gap-1.5 text-[11px] text-accent ml-1"
|
||||
>
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-accent" />
|
||||
{t("playlists.unsynced")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
"addToPlaylist": "Zur Wiedergabeliste hinzufügen",
|
||||
"exportToYoutube": "Zu YouTube exportieren",
|
||||
"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",
|
||||
"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?",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
"addToPlaylist": "Add to playlist",
|
||||
"exportToYoutube": "Export 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",
|
||||
"revertTitle": "Discard local changes?",
|
||||
"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",
|
||||
"exportToYoutube": "Exportálás 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",
|
||||
"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?",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue