Add a sort toolbar to the Playlists page: Title A-Z/Z-A, shortest/longest first, and a 'group by channel' toggle that groups items by channel (A-Z) and applies the chosen sort within each group. Applying a sort reorders the items and persists via the existing reorder API. The item order is now an undoable value: drag, sort and grouping all go through it, so every change is reversible via undo/redo buttons and Ctrl/Cmd+Z / Ctrl+Y (Ctrl+Shift+Z). Built on two reusable pieces, not playlist-specific: - useUndoable<T> — a generic past/present/future snapshot hook (set/undo/redo/reset + canUndo/canRedo), ref-backed so callbacks are stable and never see a stale snapshot; onApply runs the side effect (here: persist order). - UndoToolbar — undo/redo buttons + keyboard shortcuts, plain props so any undo source can use it. Undo history is reset only when the item set actually changes (different playlist or add/remove), so a refetch confirming our own reorder doesn't wipe it; membership changes (remove) clear history since they aren't reorder-undoable. Trilingual.
45 lines
2.1 KiB
JSON
45 lines
2.1 KiB
JSON
{
|
||
"title": "Playlists",
|
||
"watchLater": "Watch later",
|
||
"syncYoutube": "Sync from YouTube",
|
||
"syncedToast": "Synced {{count}} playlists from YouTube",
|
||
"syncFailed": "Couldn't sync from YouTube",
|
||
"ytReadonly": "Synced from YouTube — edits sync back",
|
||
"noneYet": "No playlists yet",
|
||
"newPlaylist": "New playlist…",
|
||
"itemCount_one": "{{count}} video",
|
||
"itemCount_other": "{{count}} videos",
|
||
"pickOne": "Pick a playlist on the left.",
|
||
"loading": "Loading…",
|
||
"empty": "This playlist is empty — add videos from the feed.",
|
||
"playAll": "Play all",
|
||
"delete": "Delete",
|
||
"rename": "Rename",
|
||
"confirmDelete": "Delete the playlist “{{name}}”? This can't be undone.",
|
||
"addToPlaylist": "Add to playlist",
|
||
"exportToYoutube": "Export to YouTube",
|
||
"pushToYoutube": "Sync to YouTube",
|
||
"unsynced": "Unsynced changes",
|
||
"pushTitle": "Sync to YouTube",
|
||
"pushConfirm": "Sync",
|
||
"pushPlanCreate": "Create a new YouTube playlist with {{count}} videos? (~{{units}} quota units; {{left}} left today.)",
|
||
"pushPlanUpdate": "Update on YouTube — add {{insert}}, remove {{del}}, reorder {{reorder}}? (~{{units}} quota units; {{left}} left today.)",
|
||
"pushDiverged": "{{count}} video(s) currently on YouTube will be removed.",
|
||
"pushNoQuota": "Not enough YouTube quota left today ({{left}}) for this sync (~{{units}} needed). Try again tomorrow.",
|
||
"pushUpToDate": "Already in sync with YouTube.",
|
||
"pushDone": "Synced to YouTube ✓",
|
||
"pushPartial": "Synced, but {{count}} item(s) were skipped.",
|
||
"pushFailed": "Couldn't sync to YouTube.",
|
||
"deleteOnYoutubeTitle": "Delete on YouTube too?",
|
||
"deleteOnYoutubeMsg": "Also delete “{{name}}” from your YouTube account? Choose “Here only” to keep it on YouTube.",
|
||
"deleteOnYoutubeConfirm": "Delete on YouTube",
|
||
"deleteHereOnly": "Here only",
|
||
"deleteYtFailed": "Couldn't delete on YouTube; removed here only.",
|
||
"sortLabel": "Sort",
|
||
"sortManual": "Custom order",
|
||
"sortTitleAsc": "Title A–Z",
|
||
"sortTitleDesc": "Title Z–A",
|
||
"sortDurAsc": "Shortest first",
|
||
"sortDurDesc": "Longest first",
|
||
"groupByChannel": "Group by channel"
|
||
}
|