From c94c09aeaec1059727fbd05ffadd2d27ac840d8f Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sat, 10 Oct 2026 23:34:51 -0400 Subject: [PATCH] feat: Settings is one page per section down a left rail, Account first, every setting a row; the profile form no longer saves blanks over your name and email (M503 #5595 #5596 #5597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /settings lands on Account: the Android app, profile, password, signed-in devices. Then Playback, Appearance, Notifications, Connections, About. - Each setting is a row (name, one short line, control at the end) in a capped-width column; one-of-N choices are a shared Segmented control. - Collection and Settings share one SectionRail. - Connections leads ListenBrainz with a status line: not connected, connected and not sending, or sending with the last scrobble's age. - Notifications prints its column labels once; the digest email's settings link points at /settings/notifications. - #5596: the profile form now loads what is stored and sends only the fields that changed. It used to start blank, and the server reads a blank as "clear", so one Save wiped the display name and email. - Recommendation metrics moves to Library › Your listening, and Hidden tracks gets a Library tab. Co-Authored-By: Claude Opus 5.5 --- internal/notifications/digest_render.go | 2 +- internal/notifications/digest_test.go | 2 +- web/src/lib/api/me.ts | 6 + web/src/lib/components/ActiveSessions.svelte | 14 +- .../lib/components/MobileAppDownload.svelte | 49 +- .../components/NormalizationSetting.svelte | 98 +-- .../components/NotificationSettings.svelte | 97 +-- .../components/NotificationSettings.test.ts | 2 +- .../components/RecommendationMetrics.svelte | 213 ++++++ .../components/RecommendationMetrics.test.ts | 164 ++++ web/src/lib/components/SectionRail.svelte | 61 ++ web/src/lib/components/ServerVersion.svelte | 9 +- .../lib/components/settings/Segmented.svelte | 39 + .../lib/components/settings/SettingRow.svelte | 36 + .../components/settings/SettingsGroup.svelte | 26 + .../components/wanted/CollectionRail.svelte | 54 +- web/src/routes/library/+layout.svelte | 6 +- web/src/routes/library/listening/+page.svelte | 10 + web/src/routes/settings/+layout.svelte | 32 + web/src/routes/settings/+page.svelte | 701 ------------------ web/src/routes/settings/+page.ts | 7 + web/src/routes/settings/Appearance.test.ts | 91 --- web/src/routes/settings/about/+page.svelte | 20 + web/src/routes/settings/account/+page.svelte | 191 +++++ .../routes/settings/account/account.test.ts | 94 +++ .../routes/settings/appearance/+page.svelte | 21 + .../settings/appearance/appearance.test.ts | 31 + .../routes/settings/connections/+page.svelte | 260 +++++++ .../settings/connections/connections.test.ts | 118 +++ .../settings/notifications/+page.svelte | 8 + web/src/routes/settings/playback/+page.svelte | 32 + web/src/routes/settings/settings.test.ts | 466 +----------- 32 files changed, 1552 insertions(+), 1408 deletions(-) create mode 100644 web/src/lib/components/RecommendationMetrics.svelte create mode 100644 web/src/lib/components/RecommendationMetrics.test.ts create mode 100644 web/src/lib/components/SectionRail.svelte create mode 100644 web/src/lib/components/settings/Segmented.svelte create mode 100644 web/src/lib/components/settings/SettingRow.svelte create mode 100644 web/src/lib/components/settings/SettingsGroup.svelte create mode 100644 web/src/routes/library/listening/+page.svelte create mode 100644 web/src/routes/settings/+layout.svelte delete mode 100644 web/src/routes/settings/+page.svelte create mode 100644 web/src/routes/settings/+page.ts delete mode 100644 web/src/routes/settings/Appearance.test.ts create mode 100644 web/src/routes/settings/about/+page.svelte create mode 100644 web/src/routes/settings/account/+page.svelte create mode 100644 web/src/routes/settings/account/account.test.ts create mode 100644 web/src/routes/settings/appearance/+page.svelte create mode 100644 web/src/routes/settings/appearance/appearance.test.ts create mode 100644 web/src/routes/settings/connections/+page.svelte create mode 100644 web/src/routes/settings/connections/connections.test.ts create mode 100644 web/src/routes/settings/notifications/+page.svelte create mode 100644 web/src/routes/settings/playback/+page.svelte diff --git a/internal/notifications/digest_render.go b/internal/notifications/digest_render.go index 59213582..5c9ae146 100644 --- a/internal/notifications/digest_render.go +++ b/internal/notifications/digest_render.go @@ -48,7 +48,7 @@ type digestVars struct { // renderDigest renders one group's email. base is the public address; with // none set, the email still goes out, without links. func renderDigest(group EmailGroup, name string, items []pendingItem, base string) (digestEmail, error) { - vars := digestVars{Name: name, SettingsURL: joinURL(base, "/settings#notifications")} + vars := digestVars{Name: name, SettingsURL: joinURL(base, "/settings/notifications")} var subject string if group == EmailSummary { vars.Artists = summaryArtists(items, base) diff --git a/internal/notifications/digest_test.go b/internal/notifications/digest_test.go index b89539ff..60799a13 100644 --- a/internal/notifications/digest_test.go +++ b/internal/notifications/digest_test.go @@ -154,7 +154,7 @@ func TestRenderDigest_SummaryGroupsByArtist(t *testing.T) { require.Contains(t, e.Text, " - Pure\n https://music.example/albums/al-3") require.Contains(t, e.Text, "Boards of Canada\n https://music.example/artists/ar-2") require.Contains(t, e.Text, "Tycho\n - Awake") - require.Contains(t, e.Text, "https://music.example/settings#notifications") + require.Contains(t, e.Text, "https://music.example/settings/notifications") require.Contains(t, e.HTML, ` { + return api.get('/api/me'); +} + export async function updateProfile(input: { display_name?: string; email?: string }): Promise { return api.put('/api/me/profile', input); } diff --git a/web/src/lib/components/ActiveSessions.svelte b/web/src/lib/components/ActiveSessions.svelte index f1aaa0b0..674c1d93 100644 --- a/web/src/lib/components/ActiveSessions.svelte +++ b/web/src/lib/components/ActiveSessions.svelte @@ -119,12 +119,14 @@ } -
-

Active sessions

-

- Every device signed in to your account. If you see one you don't recognise — especially - one marked as having moved — sign it out and change your password. -

+ +
+
+

Signed-in devices

+

+ One you don't recognise? Sign it out and change your password. +

+
{#if loadError}

diff --git a/web/src/lib/components/MobileAppDownload.svelte b/web/src/lib/components/MobileAppDownload.svelte index 519b3219..19ee9e10 100644 --- a/web/src/lib/components/MobileAppDownload.svelte +++ b/web/src/lib/components/MobileAppDownload.svelte @@ -1,19 +1,20 @@ {#if info} -

-

Mobile app

-

- Install the Android app paired to this server. Sign in with the same account. -

-
+ - - - Get the Android app - - {info.version}{info.sizeBytes > 0 ? ` · ${formatSize(info.sizeBytes)}` : ''} - - - -
+