From 200c5227fcc2f48777291a04078670b0fc002269 Mon Sep 17 00:00:00 2001 From: nrobi144 Date: Sun, 19 Apr 2026 05:40:28 +0300 Subject: [PATCH] refactor(desktop): pin/unpin syncs to active workspace PinnedNavBarState now takes WorkspaceManager reference. Pin/unpin actions update the active workspace's singlePaneScreens list, making sidebar customization and workspace editing the same action. - PinnedNavBarState.syncToWorkspace() updates active workspace on pin/unpin - PinnedNavBarState.loadFromWorkspace() loads from active workspace - Remove separate DesktopPreferences.pinnedNavItems persistence - Workspace is the single source of truth for nav bar screens Co-Authored-By: Claude Opus 4.6 (1M context) --- .../vitorpamplona/amethyst/desktop/Main.kt | 19 +++----- .../desktop/ui/deck/PinnedNavBarState.kt | 46 +++++++++---------- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt index bbac3c026..f8c28cd80 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt @@ -540,7 +540,7 @@ fun App( initialTorSettings: com.vitorpamplona.amethyst.commons.tor.TorSettings, ) { val singlePaneState = remember { SinglePaneState() } - val pinnedNavBarState = remember { PinnedNavBarState().also { it.load() } } + val pinnedNavBarState = remember { PinnedNavBarState(workspaceManager).also { it.loadFromWorkspace() } } // Always reload from prefs — after key() rebuild, prefs have the latest saved settings var torSettings by remember { @@ -795,17 +795,12 @@ fun App( } LayoutMode.SINGLE_PANE -> { - // Set nav bar screens from workspace - if (ws.singlePaneScreens.isNotEmpty()) { - val screens = - ws.singlePaneScreens.mapNotNull { - DeckState.parseColumnTypeFromKey(it) - } - if (screens.isNotEmpty()) { - pinnedNavBarState.loadFromList(screens) - singlePaneState.navigate(screens.first()) - } - } + // Load nav bar from workspace + navigate to first screen + pinnedNavBarState.loadFromWorkspace() + val firstKey = + ws.singlePaneScreens.firstOrNull() ?: "home" + val type = DeckState.parseColumnTypeFromKey(firstKey) + if (type != null) singlePaneState.navigate(type) } } }, diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/PinnedNavBarState.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/PinnedNavBarState.kt index 00d5f5eb9..6deb120b7 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/PinnedNavBarState.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/PinnedNavBarState.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.desktop.ui.deck -import com.vitorpamplona.amethyst.desktop.DesktopPreferences import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -28,9 +27,11 @@ import kotlinx.coroutines.flow.update /** * Manages which screens are pinned to the navigation sidebar. - * Persists to DesktopPreferences as CSV of typeKey strings. + * Pin/unpin syncs to the active workspace's singlePaneScreens. */ -class PinnedNavBarState { +class PinnedNavBarState( + private val workspaceManager: WorkspaceManager? = null, +) { private val _pinnedScreens = MutableStateFlow(DEFAULT_PINNED) val pinnedScreens: StateFlow> = _pinnedScreens.asStateFlow() @@ -40,13 +41,13 @@ class PinnedNavBarState { if (isPinned(type)) return if (!isPinnable(type)) return _pinnedScreens.update { it + type } - save() + syncToWorkspace() } fun unpin(type: DeckColumnType) { if (!isUnpinnable(type)) return _pinnedScreens.update { current -> current.filter { it.typeKey() != type.typeKey() } } - save() + syncToWorkspace() } fun move( @@ -60,34 +61,32 @@ class PinnedNavBarState { mutable.add(toIndex, item) mutable.toList() } - save() - } - - fun save() { - DesktopPreferences.pinnedNavItems = _pinnedScreens.value.joinToString(",") { it.typeKey() } + syncToWorkspace() } fun loadFromList(screens: List) { _pinnedScreens.value = screens.ifEmpty { DEFAULT_PINNED } - save() } - fun load() { - val raw = DesktopPreferences.pinnedNavItems - if (raw.isBlank()) { - _pinnedScreens.value = DEFAULT_PINNED - return + fun loadFromWorkspace() { + val ws = workspaceManager?.activeWorkspace ?: return + if (ws.singlePaneScreens.isNotEmpty()) { + val screens = + ws.singlePaneScreens.mapNotNull { key -> + PINNABLE_SCREENS.find { it.typeKey() == key } + } + _pinnedScreens.value = screens.ifEmpty { DEFAULT_PINNED } } - val keys = raw.split(",").filter { it.isNotBlank() } - val screens = - keys.mapNotNull { key -> - PINNABLE_SCREENS.find { it.typeKey() == key } - } - _pinnedScreens.value = screens.ifEmpty { DEFAULT_PINNED } + } + + private fun syncToWorkspace() { + val wm = workspaceManager ?: return + val ws = wm.activeWorkspace + val updated = ws.copy(singlePaneScreens = _pinnedScreens.value.map { it.typeKey() }) + wm.updateWorkspace(updated) } companion object { - // Only object types are pinnable (no parameterized types like Hashtag, Editor) val PINNABLE_SCREENS: List = LAUNCHABLE_SCREENS.filter { !it.requiresInput() && it !is DeckColumnType.Editor } @@ -106,7 +105,6 @@ class PinnedNavBarState { DeckColumnType.Settings, ) - // These screens cannot be unpinned private val ALWAYS_PINNED = setOf("home", "settings") fun isPinnable(type: DeckColumnType): Boolean = PINNABLE_SCREENS.any { it.typeKey() == type.typeKey() }