From 80259cb0e9280fd570692f123516bfe737cb715f Mon Sep 17 00:00:00 2001 From: lobo Date: Fri, 2 Oct 2026 07:53:35 +0200 Subject: [PATCH] feat(settings): remove the platform mails switch Broadcasts go to every active user, so the settings switch and its API call are gone. Admin broadcast texts no longer mention an opt-out. --- README.md | 1 - public/i18n/de.json | 8 +++----- public/i18n/en.json | 8 +++----- src/components/PreferencesCard.jsx | 17 +---------------- src/i18n.test.js | 11 +++++++++++ src/pages/SettingsPage.test.jsx | 24 ------------------------ src/utils/broadcasts.js | 2 +- src/utils/settings.js | 8 -------- 8 files changed, 19 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 3becec1..1ae1acb 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,6 @@ To Be Played is split into three repositories: (menu) lists all entries and marks them seen. Admins publish, preview, edit and delete entries on `/admin/news`. - **Broadcast mail**: admins write a mail in both languages on `/admin/broadcasts`, send it to all active users or to picked ones, send a test to themselves, confirm with the real recipient count, and see the history with the counts. - Users turn platform mails off in the settings; account mails always come. - **Welcome page**: `/welcome` opens once after the first login (Auth `settings.welcomeSeenAt` is null; only the dashboard redirects, deep links stay). Feature cards with links, two next steps (Steam, import) with checks from `/me` and the library summary. "Let's go" and "Skip" stamp it in Auth and open the dashboard. The settings page diff --git a/public/i18n/de.json b/public/i18n/de.json index 50dd494..e22cdfa 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -143,9 +143,7 @@ "PROFILE_TILES_HINT": "Wähle, was dein Profil zeigt und wo: öffne dein Profil und klicke auf \"Anpassen\".", "FAVORITE_PLATFORM": "Lieblingsplattform", "FAVORITE_PLATFORM_HINT": "Wird zuerst gewählt, wenn du ein Spiel für mehrere Plattformen hinzufügst.", - "FAVORITE_PLATFORM_NONE": "Keine", - "PLATFORM_MAILS": "Mails von der Plattform", - "PLATFORM_MAILS_HINT": "Neuigkeiten und Ankündigungen per Mail. Mails zu deinem Konto kommen immer." + "FAVORITE_PLATFORM_NONE": "Keine" }, "VALIDATION": { "EMAIL_REQUIRED": "Email ist erforderlich.", @@ -718,7 +716,7 @@ "SUBJECT_DE": "Betreff (Deutsch)", "BODY_EN": "Text (Englisch)", "BODY_DE": "Text (Deutsch)", - "TEXT_HELP": "Nur Text, in beiden Sprachen. Betreff höchstens 150, Text höchstens 5000 Zeichen. Eine Fußzeile mit dem Link zum Abschalten wird angehängt.", + "TEXT_HELP": "Nur Text, in beiden Sprachen. Betreff höchstens 150, Text höchstens 5000 Zeichen. Eine Fußzeile wird angehängt.", "RECIPIENTS": "Empfänger", "ALL": "Alle aktiven Nutzer", "PICKED": "Ausgewählte Nutzer", @@ -732,7 +730,7 @@ "CONFIRM_TEXT": "An {{count}} Nutzer senden? Das kann nicht rückgängig gemacht werden.", "CONFIRM": "Senden", "CANCEL": "Abbrechen", - "NO_RECIPIENTS": "Kein Empfänger: niemand ist aktiv und hat Mails an.", + "NO_RECIPIENTS": "Kein Empfänger: niemand davon ist aktiv.", "SENT": "Mail für {{count}} Nutzer eingereiht.", "ERROR": "Die Aktion ist fehlgeschlagen.", "LIST": "Gesendete Mails", diff --git a/public/i18n/en.json b/public/i18n/en.json index 6f4f085..4414511 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -143,9 +143,7 @@ "PROFILE_TILES_HINT": "Choose what your profile shows, and where: open your profile and click \"Customize\".", "FAVORITE_PLATFORM": "Favorite platform", "FAVORITE_PLATFORM_HINT": "Selected first when you add a game on more than one platform.", - "FAVORITE_PLATFORM_NONE": "None", - "PLATFORM_MAILS": "Mails from the platform", - "PLATFORM_MAILS_HINT": "News and announcements by mail. Mails about your account always come." + "FAVORITE_PLATFORM_NONE": "None" }, "VALIDATION": { "EMAIL_REQUIRED": "Email is required.", @@ -718,7 +716,7 @@ "SUBJECT_DE": "Subject (German)", "BODY_EN": "Text (English)", "BODY_DE": "Text (German)", - "TEXT_HELP": "Plain text, both languages. Subject max 150, text max 5000 characters. A footer with the opt-out link is added.", + "TEXT_HELP": "Plain text, both languages. Subject max 150, text max 5000 characters. A footer is added.", "RECIPIENTS": "Recipients", "ALL": "All active users", "PICKED": "Picked users", @@ -732,7 +730,7 @@ "CONFIRM_TEXT": "Send to {{count}} users? This cannot be undone.", "CONFIRM": "Send", "CANCEL": "Cancel", - "NO_RECIPIENTS": "No recipient: nobody active with mails on.", + "NO_RECIPIENTS": "No recipient: nobody active among them.", "SENT": "Mail queued for {{count}} users.", "ERROR": "The action failed.", "LIST": "Sent mails", diff --git a/src/components/PreferencesCard.jsx b/src/components/PreferencesCard.jsx index aedce60..e2dfb06 100644 --- a/src/components/PreferencesCard.jsx +++ b/src/components/PreferencesCard.jsx @@ -11,7 +11,7 @@ import {usePreferences} from '../context/PreferencesContext.jsx'; import usePersistSettings from '../hooks/usePersistSettings.js'; import useSaveResponse from '../hooks/useSaveResponse.js'; import {FAVORITE_PLATFORMS, saveFavoritePlatform} from '../utils/platforms.js'; -import {LANGUAGES, savePlatformMails, saveSettings} from '../utils/settings.js'; +import {LANGUAGES, saveSettings} from '../utils/settings.js'; // Radix Select has no empty value. const NO_FAVORITE = 'NONE'; @@ -41,13 +41,6 @@ export default function PreferencesCard() { } }; - const handlePlatformMailsChange = async (next) => { - if (await runSave(() => savePlatformMails(next))) { - updateProfile((p) => ({...p, settings: {...p.settings, platformMails: next}})); - toast.success(t('SETTINGS.SAVED')); - } - }; - return ( @@ -90,14 +83,6 @@ export default function PreferencesCard() { -
-
- -

{t('SETTINGS.PLATFORM_MAILS_HINT')}

-
- -
); diff --git a/src/i18n.test.js b/src/i18n.test.js index 9292d25..439279c 100644 --- a/src/i18n.test.js +++ b/src/i18n.test.js @@ -20,3 +20,14 @@ describe('translation files', () => { expect(loadPath).toBe(`/i18n/{{lng}}.json?v=${hash.digest('hex').slice(0, 12)}`); }); }); + +describe('admin broadcast texts', () => { + it('adminBroadcastTexts_DoNotMentionAnOptOut', () => { + const dir = path.resolve(import.meta.dirname, '../public/i18n'); + for (const file of ['en.json', 'de.json']) { + const texts = JSON.parse(readFileSync(path.join(dir, file), 'utf8')).ADMIN_BROADCAST; + expect(texts.TEXT_HELP, file).not.toMatch(/opt-out|Abschalten/i); + expect(texts.NO_RECIPIENTS, file).not.toMatch(/mails on|Mails an/i); + } + }); +}); diff --git a/src/pages/SettingsPage.test.jsx b/src/pages/SettingsPage.test.jsx index 95d5d7a..ae1a525 100644 --- a/src/pages/SettingsPage.test.jsx +++ b/src/pages/SettingsPage.test.jsx @@ -31,7 +31,6 @@ const authRoutes = (me) => ({ 'PUT /api/users/settings/appearance': noBody, 'PUT /api/users/settings/profile-style': noBody, 'PUT /api/users/settings/favorite-platform': noBody, - 'PUT /api/users/settings/platform-mails': noBody, 'GET /api/version': json(404, {}), }); @@ -257,29 +256,6 @@ describe('SettingsPage favorite platform', () => { }); }); -describe('SettingsPage platform mails', () => { - beforeEach(() => localStorage.setItem('tbp_session', '1')); - - afterEach(() => { - cleanup(); - localStorage.clear(); - }); - - it('is on by default and saves the opt-out', async () => { - toast.success.mockClear(); - renderPage(profile()); - - const mails = await screen.findByRole('switch', {name: 'SETTINGS.PLATFORM_MAILS'}); - expect(mails.getAttribute('aria-checked')).toBe('true'); - fireEvent.click(mails); - - await waitFor(() => expect(puts()).toEqual([{enabled: false}])); - expect(fetch.mock.calls.find(([, init]) => init?.method === 'PUT')[0]).toContain('/api/users/settings/platform-mails'); - await waitFor(() => expect(toast.success).toHaveBeenCalledWith('SETTINGS.SAVED')); - expect(mails.getAttribute('aria-checked')).toBe('false'); - }); -}); - describe('SettingsPage session', () => { beforeEach(() => localStorage.setItem('tbp_session', '1')); diff --git a/src/utils/broadcasts.js b/src/utils/broadcasts.js index 754e026..448f132 100644 --- a/src/utils/broadcasts.js +++ b/src/utils/broadcasts.js @@ -10,7 +10,7 @@ const JSON_HEADERS = {'Content-Type': 'application/json'}; export const fetchBroadcasts = () => apiFetch(`${AUTH()}/api/admin/broadcasts`); /** - * {count}: the active users with platform mails on, among `userIds`; all of them when `userIds` is null. + * {count}: the active users among `userIds`; all of them when `userIds` is null. */ export const fetchRecipientCount = (userIds) => apiFetch(`${AUTH()}/api/admin/broadcasts/recipient-count${userIds ? `?userIds=${userIds.join(',')}` : ''}`); diff --git a/src/utils/settings.js b/src/utils/settings.js index 07460b9..1a16541 100644 --- a/src/utils/settings.js +++ b/src/utils/settings.js @@ -58,11 +58,3 @@ export const markWelcomeSeen = () => method: 'PUT', signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), }); - -/** PUT on Auth, 204: turns the broadcast mails on or off. The caller handles the response status. */ -export const savePlatformMails = (enabled) => - apiFetch(`${import.meta.env.VITE_API_URL}/api/users/settings/platform-mails`, { - method: 'PUT', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({enabled}), - });