From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Modrinth Enhanced Date: Mon, 14 Sep 2026 13:06:38 +0200 Subject: [PATCH] Make the sidebars foldable Three sidebar preferences, all stored as feature flags so they survive a restart without needing a settings migration: * The Modrinth Servers button in the left sidebar can be switched off under Settings > Features > Sidebar. It stays on by default. * The news section in the right sidebar collapses by clicking its heading, remembering the state the way the friends list does. * The button that folds the right sidebar away is always in the title bar, rather than appearing only once "Hide right sidebar" is turned on in settings, and what it is set to is remembered. That setting still works, and still seeds the state. Its label was pointing at the wrong message ("Next image"), which is fixed here too. None of these three reach Modrinth. Preference syncing maps a fixed list of named fields in both directions, and these are not in it, so they are neither sent nor overwritten by another device. The Modrinth Servers switch says so where it sits, because it sits on a page headed "Sync features across devices" and would otherwise look like it syncs. The other two are not settings entries - a heading and a title bar button - so nothing there promises anything. --- apps/app-frontend/src/App.vue | 67 ++++++++++++++++--- .../ui/settings/display/FeaturesSettings.vue | 28 ++++++++ .../src/composables/use-app-settings.ts | 3 + packages/app-lib/src/state/settings.rs | 3 + 4 files changed, 92 insertions(+), 9 deletions(-) diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index e5c6fd4..a9e3945 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -10,6 +10,7 @@ import { } from '@modrinth/api-client' import { ArrowLeftRightIcon, + ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CompassIcon, @@ -254,12 +255,31 @@ const credentials = ref() const storedModrinthAccounts = ref([]) let credentialsRefreshId = 0 const sidebarToggled = ref(true) +// Whether the right sidebar is folded away, remembered across restarts in the +// feature flags. The title bar button below is what normally sets it; the +// "Hide right sidebar" setting still seeds it, so turning that on folds the +// sidebar away as it always did. watch( - () => appSettings.toggleSidebar, - (toggleSidebar) => { - sidebarToggled.value = !toggleSidebar + () => appSettings.getFeatureFlag('right_sidebar_collapsed'), + (collapsed) => { + sidebarToggled.value = !collapsed }, + { immediate: true }, ) +watch( + () => appSettings.toggleSidebar, + (hide) => setSidebarCollapsed(hide), +) + +function setSidebarCollapsed(collapsed) { + appSettings.featureFlags.right_sidebar_collapsed = collapsed + getSettings() + .then((settings) => { + settings.feature_flags.right_sidebar_collapsed = collapsed + return setSettings(settings) + }) + .catch(handleError) +} const forceSidebar = computed( () => route.path.startsWith('/browse') || @@ -358,6 +378,21 @@ useQuery({ // ad and the gradient being drawn above it. const showAd = computed(() => false) const adConsentAvailable = computed(() => false) + +// Whether the news section in the right sidebar is folded away. Kept in the +// feature flags so it survives a restart, the same way the friends list +// remembers its collapsed sections. +const newsCollapsed = computed(() => appSettings.getFeatureFlag('news_collapsed')) + +function setNewsCollapsed(collapsed) { + appSettings.featureFlags.news_collapsed = collapsed + getSettings() + .then((settings) => { + settings.feature_flags.news_collapsed = collapsed + return setSettings(settings) + }) + .catch(handleError) +} providePageContext({ hierarchicalSidebarAvailable: ref(true), showAds: showAd, @@ -588,6 +623,8 @@ const messages = defineMessages({ goBack: { id: 'app.navigation.go-back', defaultMessage: 'Go back' }, goForward: { id: 'app.navigation.go-forward', defaultMessage: 'Go forward' }, nextImage: { id: 'app.navigation.next-image', defaultMessage: 'Next image' }, + hideSidebar: { id: 'app.navigation.hide-sidebar', defaultMessage: 'Hide sidebar' }, + showSidebar: { id: 'app.navigation.show-sidebar', defaultMessage: 'Show sidebar' }, updateDownloadMissingVersion: { id: 'app.update.download-error.missing-version', defaultMessage: 'Failed to download update: no version available', @@ -2252,6 +2289,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
@@ -2502,10 +2543,18 @@ provideAppUpdateDownloadProgress(appUpdateDownload) class="p-4 border-0 border-b-[1px] border-[--brand-gradient-border] border-solid" />
-

+

-
+ + +
{ />
+
+
+

+ {{ formatMessage(messages.showHostingTitle) }} +

+

{{ formatMessage(messages.showHostingDescription) }}

+
+ +
+

{{ formatMessage(messages.quickInstancesTitle) }} diff --git a/apps/app-frontend/src/composables/use-app-settings.ts b/apps/app-frontend/src/composables/use-app-settings.ts index 0d58903..59da62b 100644 --- a/apps/app-frontend/src/composables/use-app-settings.ts +++ b/apps/app-frontend/src/composables/use-app-settings.ts @@ -24,6 +24,9 @@ export const DEFAULT_FEATURE_FLAGS = { friends_pending_collapsed: true, dismissed_photosensitivity_filter_warning: false, localhost_sign_in: false, + show_hosting_in_sidebar: true, + news_collapsed: false, + right_sidebar_collapsed: false, } export type FeatureFlag = keyof typeof DEFAULT_FEATURE_FLAGS diff --git a/packages/app-lib/src/state/settings.rs b/packages/app-lib/src/state/settings.rs index 20601b8..2fd0d87 100644 --- a/packages/app-lib/src/state/settings.rs +++ b/packages/app-lib/src/state/settings.rs @@ -88,6 +88,9 @@ pub enum FeatureFlag { FriendsPendingCollapsed, DismissedPhotosensitivityFilterWarning, LocalhostSignIn, + ShowHostingInSidebar, + NewsCollapsed, + RightSidebarCollapsed, } impl Settings {