From afa6974a078ad7c814d27e67f0c3462be6700231 Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Fri, 23 Oct 2026 02:06:15 +0200 Subject: [PATCH] feat: call the unfiled tracks To sort, and say WaveFlow keeps the list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfiled read as a folder that could be renamed, or as one holding uploads. It is the list of every track in no folder, kept by WaveFlow: it is now named To sort (À ranger), drawn with a tray rather than a folder and an Automatic badge, and its view says a track leaves it once filed and that it cannot be renamed or deleted. The delete confirmation, the delete notice and the upload lines say where tracks go back to. Signed-off-by: InstaZDLL --- webapp/e2e/studio-nocturne.spec.ts | 24 ++++++++++---------- webapp/src/components/icons.tsx | 10 ++++++++- webapp/src/pages/folders-page.tsx | 11 ++++++++-- webapp/src/state/i18n.tsx | 35 ++++++++++++++++-------------- webapp/src/styles/styles.css | 11 ++++++++++ 5 files changed, 61 insertions(+), 30 deletions(-) diff --git a/webapp/e2e/studio-nocturne.spec.ts b/webapp/e2e/studio-nocturne.spec.ts index c4d979df..459dc406 100644 --- a/webapp/e2e/studio-nocturne.spec.ts +++ b/webapp/e2e/studio-nocturne.spec.ts @@ -5009,7 +5009,9 @@ test("files tracks into folders from the menu and by dragging", async ({ await page.goto("/folders"); await expect(page.getByRole("heading", { name: "Folders" })).toBeVisible(); const cards = page.getByRole("list", { name: "Folders" }); - await expect(cards.getByRole("link", { name: /Unfiled/ })).toContainText("3 tracks"); + await expect(cards.getByRole("link", { name: /To sort/ })).toContainText("3 tracks"); + // WaveFlow's list, not a folder of the owner's, and it says so. + await expect(cards.getByRole("link", { name: /To sort/ })).toContainText("Automatic"); // A folder, and a second one refused for a name already taken. await page.getByRole("button", { name: "New folder" }).click(); @@ -5025,8 +5027,8 @@ test("files tracks into folders from the menu and by dragging", async ({ await page.getByRole("button", { name: "Cancel" }).click(); // From the menu: the unfiled tracks, the first one filed into Rock. - await cards.getByRole("link", { name: /Unfiled/ }).click(); - await expect(page.getByRole("heading", { name: "Unfiled" })).toBeVisible(); + await cards.getByRole("link", { name: /To sort/ }).click(); + await expect(page.getByRole("heading", { name: "To sort" })).toBeVisible(); const rows = page.locator(".songs tbody tr"); await expect(rows).toHaveCount(3); await rows.first().locator("button.song-title").focus(); @@ -5048,7 +5050,7 @@ test("files tracks into folders from the menu and by dragging", async ({ // there, where it would read as a folder inside "Unfiled". await expect(page.getByRole("list", { name: "Folders" })).toHaveCount(0); await expect( - page.getByText("To file one, open its menu", { exact: false }), + page.getByText("To file a track, open its menu", { exact: false }), ).toBeVisible(); // Inside the folder: a second track, a sub-folder, and a drag onto it. A @@ -5081,9 +5083,9 @@ test("files tracks into folders from the menu and by dragging", async ({ await page.getByRole("button", { name: "Delete" }).click(); await expect(page.getByRole("heading", { name: "Folders" })).toBeVisible(); await expect(page.getByRole("status")).toHaveText( - "Folder deleted: 2 tracks are unfiled.", + "Folder deleted: 2 tracks are back in To sort.", ); - await expect(cards.getByRole("link", { name: /Unfiled/ })).toContainText("3 tracks"); + await expect(cards.getByRole("link", { name: /To sort/ })).toContainText("3 tracks"); await expectNoWcagViolations(page); }); @@ -5100,7 +5102,7 @@ test("shows a listener the filed tree with nothing to change", async ({ page }) await expect(page.getByRole("button", { name })).toHaveCount(0); } // A reader is told what Unfiled is, not how to file, and rows do not drag. - await expect(page.getByText("Tracks that are in no folder yet.", { exact: true })).toBeVisible(); + await expect(page.getByText("WaveFlow keeps this list itself", { exact: false })).toBeVisible(); await expect(page.locator(".songs tbody tr").first()).not.toHaveAttribute("draggable", "true"); await page.locator(".songs tbody tr button.song-title").first().focus(); await page.keyboard.press("Shift+F10"); @@ -5158,7 +5160,7 @@ test("keeps a received track when filing it fails, and says so", async ({ mimeType: "audio/flac", buffer: Buffer.from(Array.from({ length: 20 }, (_, i) => i)), }); - await expect(page.getByText("Added to the library, but not filed")).toBeVisible(); + await expect(page.getByText("Added to the library, in To sort")).toBeVisible(); expect(uploadCommits).toEqual(["upload-1"]); }); @@ -5187,7 +5189,7 @@ test("waits for the folder before taking files, and says when it is gone", async } await expect(page.getByLabel("Choose audio files")).toBeEnabled(); await expect(page.getByRole("status")).toHaveText( - "The folder this upload was opened for cannot be found: received files will stay unfiled.", + "The folder this upload was opened for cannot be found: received files will go to To sort.", ); }); @@ -5324,8 +5326,8 @@ test("closes a folder's form when another folder is opened", async ({ page }) => await page.goto("/folders"); await page.getByRole("button", { name: "New folder" }).click(); await expect(page.getByLabel("Folder name")).toBeVisible(); - await page.getByRole("list", { name: "Folders" }).getByRole("link", { name: /Unfiled/ }).click(); - await expect(page.getByRole("heading", { name: "Unfiled" })).toBeVisible(); + await page.getByRole("list", { name: "Folders" }).getByRole("link", { name: /To sort/ }).click(); + await expect(page.getByRole("heading", { name: "To sort" })).toBeVisible(); await expect(page.getByLabel("Folder name")).toHaveCount(0); expect(state.writes).toEqual([]); }); diff --git a/webapp/src/components/icons.tsx b/webapp/src/components/icons.tsx index 6299e94f..1eea898c 100644 --- a/webapp/src/components/icons.tsx +++ b/webapp/src/components/icons.tsx @@ -32,9 +32,17 @@ export type IconName = | "info" | "download" | "folder" - | "back"; + | "back" + | "inbox"; const paths: Record = { + // A tray: what has arrived and is waiting to be put away. + inbox: ( + <> + + + + ), // An arrow pointing left: back to where the visitor came from. back: ( <> diff --git a/webapp/src/pages/folders-page.tsx b/webapp/src/pages/folders-page.tsx index 27112ffb..39b7d7d0 100644 --- a/webapp/src/pages/folders-page.tsx +++ b/webapp/src/pages/folders-page.tsx @@ -375,8 +375,15 @@ export function FoldersPage({ folder }: { folder: string }) { } {...dropProps(target)} > - - {label} + {/* To sort is WaveFlow's list, not one of the owner's folders: a tray + rather than a folder, and a badge that says it keeps itself. */} + + + {label} + {target ? null : ( + {t("filed.automatic")} + )} + {t("common.tracks", { count })} diff --git a/webapp/src/state/i18n.tsx b/webapp/src/state/i18n.tsx index e0007164..9f8bcd21 100644 --- a/webapp/src/state/i18n.tsx +++ b/webapp/src/state/i18n.tsx @@ -447,7 +447,8 @@ const en = { "folders.where": "Folder path", "folders.subfolders": "Folders", "folders.noLibrary": "No library is available to this account.", - "filed.unfiled": "Unfiled", + "filed.unfiled": "To sort", + "filed.automatic": "Automatic", "filed.topLevel": "Top level", "filed.here": "Here now", "filed.destinations": "Folders to choose from", @@ -467,12 +468,12 @@ const en = { "filed.move": "Move…", "filed.delete": "Delete", "filed.confirmDelete": - "Delete “{name}” and its sub-folders? Their tracks become unfiled; no file is touched.", + "Delete “{name}” and its sub-folders? Their tracks go back to To sort; no file is touched.", "filed.created": "“{name}” created.", "filed.renamed": "Renamed to “{name}”.", "filed.deleted": { - one: "Folder deleted: {count} track is unfiled.", - other: "Folder deleted: {count} tracks are unfiled.", + one: "Folder deleted: {count} track is back in To sort.", + other: "Folder deleted: {count} tracks are back in To sort.", }, "filed.filed": { one: "{count} track filed in {folder}.", @@ -488,13 +489,14 @@ const en = { "filed.allFiled": "Every track is filed.", "filed.uploadHere": "Upload here", "filed.unfiledNote": - "Tracks that are in no folder yet. To file one, open its menu and choose “Move to folder…”.", - "filed.unfiledNoteReader": "Tracks that are in no folder yet.", + "Every track that is in no folder yet. WaveFlow keeps this list itself: a track leaves it as soon as it is filed, and it cannot be renamed or deleted. To file a track, open its menu and choose “Move to folder…”.", + "filed.unfiledNoteReader": + "Every track that is in no folder yet. WaveFlow keeps this list itself: a track leaves it as soon as it is filed.", "trackMenu.move": "Move to folder…", "upload.into": "Received files go into the folder {folder}.", - "upload.doneUnfiled": "Added to the library, but not filed", + "upload.doneUnfiled": "Added to the library, in To sort", "upload.folderMissing": - "The folder this upload was opened for cannot be found: received files will stay unfiled.", + "The folder this upload was opened for cannot be found: received files will go to To sort.", "upload.folderUnchecked": "The folder this upload was opened for could not be checked, so no file is taken yet.", "upload.checkAgain": "Check again", @@ -1134,7 +1136,8 @@ const fr: Record = { "folders.where": "Chemin du dossier", "folders.subfolders": "Dossiers", "folders.noLibrary": "Aucune bibliothèque n’est accessible à ce compte.", - "filed.unfiled": "Non classés", + "filed.unfiled": "À ranger", + "filed.automatic": "Automatique", "filed.topLevel": "Premier niveau", "filed.here": "Ici actuellement", "filed.destinations": "Dossiers au choix", @@ -1154,12 +1157,12 @@ const fr: Record = { "filed.move": "Déplacer…", "filed.delete": "Supprimer", "filed.confirmDelete": - "Supprimer « {name} » et ses sous-dossiers ? Leurs pistes redeviennent non classées ; aucun fichier n’est touché.", + "Supprimer « {name} » et ses sous-dossiers ? Leurs pistes reviennent dans À ranger ; aucun fichier n’est touché.", "filed.created": "« {name} » créé.", "filed.renamed": "Renommé en « {name} ».", "filed.deleted": { - one: "Dossier supprimé : {count} piste n’est plus classée.", - other: "Dossier supprimé : {count} pistes ne sont plus classées.", + one: "Dossier supprimé : {count} piste revient dans À ranger.", + other: "Dossier supprimé : {count} pistes reviennent dans À ranger.", }, "filed.filed": { one: "{count} piste classée dans {folder}.", @@ -1175,14 +1178,14 @@ const fr: Record = { "filed.allFiled": "Toutes les pistes sont classées.", "filed.uploadHere": "Envoyer ici", "filed.unfiledNote": - "Les pistes qui ne sont encore dans aucun dossier. Pour en classer une, ouvrez son menu et choisissez « Déplacer vers un dossier… ».", + "Toutes les pistes qui ne sont encore dans aucun dossier. WaveFlow tient cette liste lui-même : une piste en sort dès qu’elle est rangée, et elle ne peut être ni renommée ni supprimée. Pour ranger une piste, ouvrez son menu et choisissez « Déplacer vers un dossier… ».", "filed.unfiledNoteReader": - "Les pistes qui ne sont encore dans aucun dossier.", + "Toutes les pistes qui ne sont encore dans aucun dossier. WaveFlow tient cette liste lui-même : une piste en sort dès qu’elle est rangée.", "trackMenu.move": "Déplacer vers un dossier…", "upload.into": "Les fichiers reçus vont dans le dossier {folder}.", - "upload.doneUnfiled": "Ajouté à la bibliothèque, mais pas classé", + "upload.doneUnfiled": "Ajouté à la bibliothèque, dans À ranger", "upload.folderMissing": - "Le dossier pour lequel cet envoi a été ouvert est introuvable : les fichiers reçus resteront non classés.", + "Le dossier pour lequel cet envoi a été ouvert est introuvable : les fichiers reçus iront dans À ranger.", "upload.folderUnchecked": "Le dossier pour lequel cet envoi a été ouvert n’a pas pu être vérifié : aucun fichier n’est pris pour l’instant.", "upload.checkAgain": "Vérifier à nouveau", diff --git a/webapp/src/styles/styles.css b/webapp/src/styles/styles.css index 0821c57d..db59882c 100644 --- a/webapp/src/styles/styles.css +++ b/webapp/src/styles/styles.css @@ -3055,3 +3055,14 @@ button.danger { background: var(--panel-soft); color: var(--text); } + +/* A list WaveFlow keeps itself, not a folder anyone made. */ +.folder-badge { + margin-inline-start: var(--space-2); + padding: 0.1rem var(--space-2); + border: 1px solid var(--line-strong); + border-radius: 999px; + color: var(--muted); + font-size: var(--text-xs); + vertical-align: middle; +}