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. Once the fold state is saved, a check pops onto the button and its tooltip says it is remembered after a restart, since nothing else shows that. On pages that need the sidebar (browse, projects, users) the button stays in place, disabled, instead of vanishing and shifting the title bar. 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 | 153 ++++++++++++++++-- .../ui/settings/display/FeaturesSettings.vue | 28 ++++ .../src/composables/use-app-settings.ts | 3 + packages/app-lib/src/state/settings.rs | 3 + 4 files changed, 172 insertions(+), 15 deletions(-) diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index d750a5f..ac8308c 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -10,6 +10,8 @@ import { } from '@modrinth/api-client' import { ArrowLeftRightIcon, + CheckIcon, + ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CompassIcon, @@ -254,12 +256,41 @@ 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), ) + +// Briefly shows a check on the fold button once the state is saved, so it is +// clear the choice is remembered. +const sidebarSaved = ref(false) +let sidebarSavedTimeout + +function setSidebarCollapsed(collapsed) { + appSettings.featureFlags.right_sidebar_collapsed = collapsed + getSettings() + .then((settings) => { + settings.feature_flags.right_sidebar_collapsed = collapsed + return setSettings(settings) + }) + .then(() => { + sidebarSaved.value = true + clearTimeout(sidebarSavedTimeout) + sidebarSavedTimeout = setTimeout(() => (sidebarSaved.value = false), 1600) + }) + .catch(handleError) +} const forceSidebar = computed( () => route.path.startsWith('/browse') || @@ -358,6 +389,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 +634,16 @@ 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' }, + sidebarRemembered: { + id: 'app.navigation.sidebar-remembered', + defaultMessage: 'Remembered after restart', + }, + sidebarRequired: { + id: 'app.navigation.sidebar-required', + defaultMessage: 'This page needs the sidebar', + }, updateDownloadMissingVersion: { id: 'app.update.download-error.missing-version', defaultMessage: 'Failed to download update: no version available', @@ -2252,6 +2308,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
- + - - + + + + + + + + +
@@ -2502,10 +2587,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 {