From 6c4c33f9566fd92747bfc3bcc73a63ae9a826263 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Fri, 26 Jun 2026 00:32:17 +0200 Subject: [PATCH] fix(notifications): name the entity in action toasts Eight confirmations stated a bare fact without saying what they acted on. Pass the entity through the mutation variables and interpolate it: channel unsubscribe/reset name the channel, whitelist/demo-whitelist adds name the email, tag delete names the tag, and playlist revert/push name the playlist. EN/HU/DE. --- frontend/src/components/AdminUsers.tsx | 8 ++++---- frontend/src/components/Channels.tsx | 16 ++++++++-------- frontend/src/components/Playlists.tsx | 8 +++++--- frontend/src/components/TagManager.tsx | 8 ++++---- frontend/src/i18n/locales/de/channels.json | 4 ++-- frontend/src/i18n/locales/de/playlists.json | 6 +++--- frontend/src/i18n/locales/de/settings.json | 4 ++-- frontend/src/i18n/locales/de/tagManager.json | 2 +- frontend/src/i18n/locales/en/channels.json | 4 ++-- frontend/src/i18n/locales/en/playlists.json | 6 +++--- frontend/src/i18n/locales/en/settings.json | 4 ++-- frontend/src/i18n/locales/en/tagManager.json | 2 +- frontend/src/i18n/locales/hu/channels.json | 4 ++-- frontend/src/i18n/locales/hu/playlists.json | 6 +++--- frontend/src/i18n/locales/hu/settings.json | 4 ++-- frontend/src/i18n/locales/hu/tagManager.json | 2 +- 16 files changed, 45 insertions(+), 43 deletions(-) diff --git a/frontend/src/components/AdminUsers.tsx b/frontend/src/components/AdminUsers.tsx index 20165b6..8c6aeae 100644 --- a/frontend/src/components/AdminUsers.tsx +++ b/frontend/src/components/AdminUsers.tsx @@ -242,10 +242,10 @@ function AdminInvites() { }); const add = useMutation({ mutationFn: (email: string) => api.addInvite(email), - onSuccess: () => { + onSuccess: (_d, email) => { setNewEmail(""); refresh(); - notify({ level: "success", message: t("settings.invites.addedToWhitelist") }); + notify({ level: "success", message: t("settings.invites.addedToWhitelist", { email }) }); }, onError: () => notify({ level: "error", message: t("settings.invites.addFailed") }), }); @@ -324,10 +324,10 @@ function AdminDemo() { const add = useMutation({ mutationFn: (email: string) => api.addDemoWhitelist(email), - onSuccess: () => { + onSuccess: (_d, email) => { setNewEmail(""); qc.invalidateQueries({ queryKey: ["demo-whitelist"] }); - notify({ level: "success", message: t("settings.demo.added") }); + notify({ level: "success", message: t("settings.demo.added", { email }) }); }, onError: () => notify({ level: "error", message: t("settings.demo.addFailed") }), }); diff --git a/frontend/src/components/Channels.tsx b/frontend/src/components/Channels.tsx index 76447a7..ddcdfa4 100644 --- a/frontend/src/components/Channels.tsx +++ b/frontend/src/components/Channels.tsx @@ -157,20 +157,20 @@ export default function Channels({ onError: (e) => notifyActionError(e, "channels.notify.syncFailed"), }); const unsubscribe = useMutation({ - mutationFn: (id: string) => api.unsubscribeChannel(id), - onSuccess: () => { + mutationFn: (v: { id: string; name: string }) => api.unsubscribeChannel(v.id), + onSuccess: (_d, v) => { qc.invalidateQueries({ queryKey: ["channels"] }); qc.invalidateQueries({ queryKey: ["my-status"] }); - notify({ level: "success", message: t("channels.notify.unsubscribed") }); + notify({ level: "success", message: t("channels.notify.unsubscribed", { name: v.name }) }); }, onError: (e) => notifyActionError(e, "channels.notify.unsubscribeFailed"), }); const resetBackfill = useMutation({ - mutationFn: (id: string) => api.resetChannelBackfill(id), - onSuccess: () => { + mutationFn: (v: { id: string; name: string }) => api.resetChannelBackfill(v.id), + onSuccess: (_d, v) => { qc.invalidateQueries({ queryKey: ["channels"] }); qc.invalidateQueries({ queryKey: ["my-status"] }); - notify({ level: "success", message: t("channels.notify.resetDone") }); + notify({ level: "success", message: t("channels.notify.resetDone", { name: v.name }) }); }, onError: (e) => notifyActionError(e, "channels.notify.resetFailed"), }); @@ -210,7 +210,7 @@ export default function Channels({ confirmLabel: t("channels.row.unsubscribeOnYoutube"), danger: true, }); - if (ok) unsubscribe.mutate(c.id); + if (ok) unsubscribe.mutate({ id: c.id, name: c.title ?? c.id }); }; const onReset = async (c: ManagedChannel) => { const ok = await confirm({ @@ -218,7 +218,7 @@ export default function Channels({ message: t("channels.confirmReset", { name: c.title ?? c.id }), confirmLabel: t("channels.row.backfillThis"), }); - if (ok) resetBackfill.mutate(c.id); + if (ok) resetBackfill.mutate({ id: c.id, name: c.title ?? c.id }); }; const columns: Column[] = [ diff --git a/frontend/src/components/Playlists.tsx b/frontend/src/components/Playlists.tsx index 8bf346e..8c49f64 100644 --- a/frontend/src/components/Playlists.tsx +++ b/frontend/src/components/Playlists.tsx @@ -353,11 +353,12 @@ 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") }); + notify({ level: "success", message: t("playlists.revertDone", { name: plName }) }); } catch { notify({ level: "warning", message: t("playlists.revertFailed") }); } finally { @@ -367,11 +368,12 @@ 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") }); + notify({ level: "info", message: t("playlists.pushUpToDate", { name: plName }) }); return; } if (!plan.affordable) { @@ -410,7 +412,7 @@ 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") }); + notify({ level: "success", message: t("playlists.pushDone", { name: plName }) }); } } catch { notify({ level: "warning", message: t("playlists.pushFailed") }); diff --git a/frontend/src/components/TagManager.tsx b/frontend/src/components/TagManager.tsx index 5999728..72bd36a 100644 --- a/frontend/src/components/TagManager.tsx +++ b/frontend/src/components/TagManager.tsx @@ -137,10 +137,10 @@ export default function TagManager({ onSuccess: invalidate, }); const del = useMutation({ - mutationFn: (id: number) => api.deleteTag(id), - onSuccess: () => { + mutationFn: (v: { id: number; name: string }) => api.deleteTag(v.id), + onSuccess: (_d, v) => { invalidate(); - notify({ level: "success", message: t("tagManager.deleted") }); + notify({ level: "success", message: t("tagManager.deleted", { name: v.name }) }); }, }); @@ -179,7 +179,7 @@ export default function TagManager({ }); if (!ok) return; } - del.mutate(tag.id); + del.mutate({ id: tag.id, name: tag.name }); }} /> ))} diff --git a/frontend/src/i18n/locales/de/channels.json b/frontend/src/i18n/locales/de/channels.json index 7898cba..93cafc2 100644 --- a/frontend/src/i18n/locales/de/channels.json +++ b/frontend/src/i18n/locales/de/channels.json @@ -111,14 +111,14 @@ "notify": { "synced": "{{count}} Abos synchronisiert", "syncFailed": "Abo-Synchronisierung fehlgeschlagen", - "unsubscribed": "Bei YouTube abbestellt", + "unsubscribed": "{{name}} bei YouTube abbestellt", "unsubscribeFailed": "Abbestellen fehlgeschlagen", "fullHistoryRequested": "Vollständiger Verlauf für {{count}} Kanäle angefordert", "fullHistoryFailed": "Vollständiger Verlauf konnte nicht angefordert werden", "needYouTube": "Verbinde dein YouTube-Konto, um dies zu tun.", "connect": "Verbinden", "filteredByTag": "Feed nach Tag gefiltert: {{name}}", - "resetDone": "Kanal zurückgesetzt — wird neu geladen", + "resetDone": "{{name}} zurückgesetzt — wird neu geladen", "resetFailed": "Kanal konnte nicht zurückgesetzt werden" }, "confirmUnsubscribe": "Kanal „{{name}}“ bei YouTube abbestellen? Dies ändert dein echtes YouTube-Konto. Um ihn nur aus deinem Feed zu entfernen, blende ihn stattdessen aus.", diff --git a/frontend/src/i18n/locales/de/playlists.json b/frontend/src/i18n/locales/de/playlists.json index e23f006..706522b 100644 --- a/frontend/src/i18n/locales/de/playlists.json +++ b/frontend/src/i18n/locales/de/playlists.json @@ -26,7 +26,7 @@ "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?", "revertConfirm": "Verwerfen & neu laden", - "revertDone": "Von YouTube neu geladen ✓", + "revertDone": "„{{name}}“ von YouTube neu geladen ✓", "revertFailed": "Neuladen von YouTube fehlgeschlagen.", "pushTitle": "Mit YouTube synchronisieren", "pushConfirm": "Synchronisieren", @@ -34,8 +34,8 @@ "pushPlanUpdate": "Auf YouTube aktualisieren — {{insert}} hinzufügen, {{del}} entfernen, {{reorder}} neu anordnen? (~{{units}} Kontingenteinheiten; heute noch {{left}} übrig.)", "pushDiverged": "{{count}} Video(s), die derzeit auf YouTube sind, werden entfernt.", "pushNoQuota": "Nicht genug YouTube-Kontingent für heute ({{left}}) für diese Synchronisierung (~{{units}} benötigt). Versuche es morgen erneut.", - "pushUpToDate": "Bereits mit YouTube synchronisiert.", - "pushDone": "Mit YouTube synchronisiert ✓", + "pushUpToDate": "„{{name}}“ ist bereits mit YouTube synchronisiert.", + "pushDone": "„{{name}}“ mit YouTube synchronisiert ✓", "pushPartial": "Synchronisiert, aber {{count}} Element(e) wurden übersprungen.", "pushFailed": "Synchronisierung mit YouTube fehlgeschlagen.", "deleteOnYoutubeTitle": "Auch auf YouTube löschen?", diff --git a/frontend/src/i18n/locales/de/settings.json b/frontend/src/i18n/locales/de/settings.json index 0f7bced..228d491 100644 --- a/frontend/src/i18n/locales/de/settings.json +++ b/frontend/src/i18n/locales/de/settings.json @@ -93,7 +93,7 @@ "intro": "E-Mails hier können das gemeinsame Demo-Konto direkt von der Anmeldeseite aus betreten — ohne Google-Anmeldung. Sie tippen die Adresse einfach ins Feld und werden nach einem Moment eingelassen.", "addPlaceholder": "E-Mail für die Demo auf die Whitelist…", "add": "Hinzufügen", - "added": "Zur Demo-Whitelist hinzugefügt", + "added": "{{email}} zur Demo-Whitelist hinzugefügt", "addFailed": "Diese E-Mail konnte nicht hinzugefügt werden", "removeFailed": "Diese E-Mail konnte nicht entfernt werden", "remove": "Entfernen", @@ -115,7 +115,7 @@ "approved": "{{email}} genehmigt — sie können sich jetzt anmelden", "approveFailed": "Genehmigung fehlgeschlagen", "denyFailed": "Ablehnung fehlgeschlagen", - "addedToWhitelist": "Zur Whitelist hinzugefügt", + "addedToWhitelist": "{{email}} zur Whitelist hinzugefügt", "addFailed": "Diese E-Mail konnte nicht hinzugefügt werden", "requested": "angefragt {{time}}", "approveHint": "Genehmigen — setzt diese E-Mail auf die Whitelist und benachrichtigt sie per E-Mail, dass sie dabei sind.", diff --git a/frontend/src/i18n/locales/de/tagManager.json b/frontend/src/i18n/locales/de/tagManager.json index a5a86d3..7042cf1 100644 --- a/frontend/src/i18n/locales/de/tagManager.json +++ b/frontend/src/i18n/locales/de/tagManager.json @@ -3,7 +3,7 @@ "channels": "{{count}} Kan.", "onChannels": "Getaggte Kanäle", "delete": "Löschen", - "deleted": "Tag gelöscht", + "deleted": "Tag „{{name}}“ gelöscht", "deleteTitle": "Tag löschen", "confirmDelete": "Tag „{{name}}“ löschen? Er wird von allen Kanälen entfernt, auf denen er liegt.", "empty": "Noch keine Tags — füge unten einen hinzu.", diff --git a/frontend/src/i18n/locales/en/channels.json b/frontend/src/i18n/locales/en/channels.json index fed9566..eef0389 100644 --- a/frontend/src/i18n/locales/en/channels.json +++ b/frontend/src/i18n/locales/en/channels.json @@ -111,14 +111,14 @@ "notify": { "synced": "Synced {{count}} subscriptions", "syncFailed": "Subscription sync failed", - "unsubscribed": "Unsubscribed on YouTube", + "unsubscribed": "Unsubscribed from {{name}} on YouTube", "unsubscribeFailed": "Unsubscribe failed", "fullHistoryRequested": "Full history requested for {{count}} channels", "fullHistoryFailed": "Couldn't request full history", "needYouTube": "Connect your YouTube account to do this.", "connect": "Connect", "filteredByTag": "Feed filtered by tag: {{name}}", - "resetDone": "Channel reset — re-fetching now", + "resetDone": "{{name}} reset — re-fetching now", "resetFailed": "Couldn't reset this channel" }, "confirmUnsubscribe": "Unsubscribe from \"{{name}}\" on YouTube? This changes your real YouTube account. To just remove it from your feed, hide it instead.", diff --git a/frontend/src/i18n/locales/en/playlists.json b/frontend/src/i18n/locales/en/playlists.json index ca48e4c..179101e 100644 --- a/frontend/src/i18n/locales/en/playlists.json +++ b/frontend/src/i18n/locales/en/playlists.json @@ -26,7 +26,7 @@ "revertTitle": "Discard local changes?", "revertMsg": "This reloads the playlist from YouTube and discards your unsynced local changes (order, additions, removals, rename). Continue?", "revertConfirm": "Discard & reload", - "revertDone": "Reloaded from YouTube ✓", + "revertDone": "“{{name}}” reloaded from YouTube ✓", "revertFailed": "Couldn't reload from YouTube.", "pushTitle": "Sync to YouTube", "pushConfirm": "Sync", @@ -34,8 +34,8 @@ "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 ✓", + "pushUpToDate": "“{{name}}” is already in sync with YouTube.", + "pushDone": "“{{name}}” synced to YouTube ✓", "pushPartial": "Synced, but {{count}} item(s) were skipped.", "pushFailed": "Couldn't sync to YouTube.", "deleteOnYoutubeTitle": "Delete on YouTube too?", diff --git a/frontend/src/i18n/locales/en/settings.json b/frontend/src/i18n/locales/en/settings.json index 91c992b..21fb2e7 100644 --- a/frontend/src/i18n/locales/en/settings.json +++ b/frontend/src/i18n/locales/en/settings.json @@ -93,7 +93,7 @@ "intro": "Emails here can enter the shared demo account straight from the login page — no Google sign-in. They just type the address into the field and are let in after a moment.", "addPlaceholder": "Whitelist an email for demo…", "add": "Add", - "added": "Added to the demo whitelist", + "added": "{{email}} added to the demo whitelist", "addFailed": "Couldn't add that email", "removeFailed": "Couldn't remove that email", "remove": "Remove", @@ -115,7 +115,7 @@ "approved": "Approved {{email}} — they can sign in now", "approveFailed": "Approve failed", "denyFailed": "Deny failed", - "addedToWhitelist": "Added to the whitelist", + "addedToWhitelist": "{{email}} added to the whitelist", "addFailed": "Couldn't add that email", "requested": "requested {{time}}", "approveHint": "Approve — whitelist this email and email them they're in.", diff --git a/frontend/src/i18n/locales/en/tagManager.json b/frontend/src/i18n/locales/en/tagManager.json index 89ffd64..64b33ae 100644 --- a/frontend/src/i18n/locales/en/tagManager.json +++ b/frontend/src/i18n/locales/en/tagManager.json @@ -3,7 +3,7 @@ "channels": "{{count}} ch.", "onChannels": "Tagged channels", "delete": "Delete", - "deleted": "Tag deleted", + "deleted": "Tag “{{name}}” deleted", "deleteTitle": "Delete tag", "confirmDelete": "Delete the tag “{{name}}”? It will be removed from every channel it's on.", "empty": "No tags yet — add one below.", diff --git a/frontend/src/i18n/locales/hu/channels.json b/frontend/src/i18n/locales/hu/channels.json index 8595dfc..6b9fa1e 100644 --- a/frontend/src/i18n/locales/hu/channels.json +++ b/frontend/src/i18n/locales/hu/channels.json @@ -111,14 +111,14 @@ "notify": { "synced": "{{count}} feliratkozás szinkronizálva", "syncFailed": "A feliratkozások szinkronizálása sikertelen", - "unsubscribed": "Leiratkozva a YouTube-on", + "unsubscribed": "Leiratkozva a(z) {{name}} csatornáról a YouTube-on", "unsubscribeFailed": "A leiratkozás sikertelen", "fullHistoryRequested": "Teljes előzmény kérve {{count}} csatornához", "fullHistoryFailed": "Nem sikerült teljes előzményt kérni", "needYouTube": "Ehhez csatlakoztasd a YouTube-fiókod.", "connect": "Csatlakoztatás", "filteredByTag": "Hírfolyam szűrve címkére: {{name}}", - "resetDone": "Csatorna resetelve — újraletöltés folyamatban", + "resetDone": "{{name}} resetelve — újraletöltés folyamatban", "resetFailed": "Nem sikerült resetelni a csatornát" }, "confirmUnsubscribe": "Leiratkozol a(z) „{{name}}” csatornáról a YouTube-on? Ez megváltoztatja a valódi YouTube-fiókodat. Ha csak a hírfolyamodból szeretnéd eltávolítani, inkább rejtsd el.", diff --git a/frontend/src/i18n/locales/hu/playlists.json b/frontend/src/i18n/locales/hu/playlists.json index b9f6835..c7104c9 100644 --- a/frontend/src/i18n/locales/hu/playlists.json +++ b/frontend/src/i18n/locales/hu/playlists.json @@ -26,7 +26,7 @@ "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?", "revertConfirm": "Eldobás és újratöltés", - "revertDone": "Újratöltve a YouTube-ról ✓", + "revertDone": "„{{name}}” újratöltve a YouTube-ról ✓", "revertFailed": "Nem sikerült újratölteni a YouTube-ról.", "pushTitle": "Szinkron YouTube-ra", "pushConfirm": "Szinkron", @@ -34,8 +34,8 @@ "pushPlanUpdate": "Frissítés a YouTube-on — {{insert}} hozzáadása, {{del}} eltávolítása, {{reorder}} átrendezése? (~{{units}} kvótaegység; ma még {{left}} maradt.)", "pushDiverged": "{{count}} videó, amely jelenleg a YouTube-on van, el lesz távolítva.", "pushNoQuota": "Nincs elég YouTube-kvóta mára ({{left}}) ehhez a szinkronhoz (~{{units}} kell). Próbáld újra holnap.", - "pushUpToDate": "Már szinkronban van a YouTube-bal.", - "pushDone": "Szinkronizálva a YouTube-ra ✓", + "pushUpToDate": "A(z) „{{name}}” már szinkronban van a YouTube-bal.", + "pushDone": "„{{name}}” szinkronizálva a YouTube-ra ✓", "pushPartial": "Szinkronizálva, de {{count}} elem kimaradt.", "pushFailed": "Nem sikerült a YouTube-ra szinkronizálás.", "deleteOnYoutubeTitle": "Törlöd a YouTube-on is?", diff --git a/frontend/src/i18n/locales/hu/settings.json b/frontend/src/i18n/locales/hu/settings.json index 77c5119..6ae1077 100644 --- a/frontend/src/i18n/locales/hu/settings.json +++ b/frontend/src/i18n/locales/hu/settings.json @@ -93,7 +93,7 @@ "intro": "Az itt szereplő e-mailek a bejelentkező oldalról közvetlenül beléphetnek a közös demo fiókba — Google-bejelentkezés nélkül. Csak beírják a címet a mezőbe, és pár pillanat múlva bekerülnek.", "addPlaceholder": "E-mail fehérlistára a demóhoz…", "add": "Hozzáadás", - "added": "Hozzáadva a demo fehérlistához", + "added": "{{email}} hozzáadva a demo fehérlistához", "addFailed": "Nem sikerült hozzáadni ezt az e-mailt", "removeFailed": "Nem sikerült eltávolítani ezt az e-mailt", "remove": "Eltávolítás", @@ -115,7 +115,7 @@ "approved": "Jóváhagyva: {{email}} — most már be tud jelentkezni", "approveFailed": "A jóváhagyás sikertelen", "denyFailed": "Az elutasítás sikertelen", - "addedToWhitelist": "Hozzáadva a fehérlistához", + "addedToWhitelist": "{{email}} hozzáadva a fehérlistához", "addFailed": "Nem sikerült hozzáadni ezt az e-mailt", "requested": "kérve: {{time}}", "approveHint": "Jóváhagyás — fehérlistára teszi ezt az e-mailt és e-mailben értesíti őket, hogy bekerültek.", diff --git a/frontend/src/i18n/locales/hu/tagManager.json b/frontend/src/i18n/locales/hu/tagManager.json index 6ee27a5..4a57382 100644 --- a/frontend/src/i18n/locales/hu/tagManager.json +++ b/frontend/src/i18n/locales/hu/tagManager.json @@ -3,7 +3,7 @@ "channels": "{{count}} csat.", "onChannels": "Címkézett csatornák", "delete": "Törlés", - "deleted": "Címke törölve", + "deleted": "„{{name}}” címke törölve", "deleteTitle": "Címke törlése", "confirmDelete": "Törlöd a(z) „{{name}}” címkét? Minden csatornáról lekerül, amin rajta van.", "empty": "Még nincs címke — adj hozzá lentebb.",