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) <noreply@anthropic.com>
This commit is contained in:
@@ -540,7 +540,7 @@ fun App(
|
|||||||
initialTorSettings: com.vitorpamplona.amethyst.commons.tor.TorSettings,
|
initialTorSettings: com.vitorpamplona.amethyst.commons.tor.TorSettings,
|
||||||
) {
|
) {
|
||||||
val singlePaneState = remember { SinglePaneState() }
|
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
|
// Always reload from prefs — after key() rebuild, prefs have the latest saved settings
|
||||||
var torSettings by remember {
|
var torSettings by remember {
|
||||||
@@ -795,17 +795,12 @@ fun App(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LayoutMode.SINGLE_PANE -> {
|
LayoutMode.SINGLE_PANE -> {
|
||||||
// Set nav bar screens from workspace
|
// Load nav bar from workspace + navigate to first screen
|
||||||
if (ws.singlePaneScreens.isNotEmpty()) {
|
pinnedNavBarState.loadFromWorkspace()
|
||||||
val screens =
|
val firstKey =
|
||||||
ws.singlePaneScreens.mapNotNull {
|
ws.singlePaneScreens.firstOrNull() ?: "home"
|
||||||
DeckState.parseColumnTypeFromKey(it)
|
val type = DeckState.parseColumnTypeFromKey(firstKey)
|
||||||
}
|
if (type != null) singlePaneState.navigate(type)
|
||||||
if (screens.isNotEmpty()) {
|
|
||||||
pinnedNavBarState.loadFromList(screens)
|
|
||||||
singlePaneState.navigate(screens.first())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+22
-24
@@ -20,7 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.desktop.ui.deck
|
package com.vitorpamplona.amethyst.desktop.ui.deck
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.desktop.DesktopPreferences
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@@ -28,9 +27,11 @@ import kotlinx.coroutines.flow.update
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages which screens are pinned to the navigation sidebar.
|
* 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)
|
private val _pinnedScreens = MutableStateFlow(DEFAULT_PINNED)
|
||||||
val pinnedScreens: StateFlow<List<DeckColumnType>> = _pinnedScreens.asStateFlow()
|
val pinnedScreens: StateFlow<List<DeckColumnType>> = _pinnedScreens.asStateFlow()
|
||||||
|
|
||||||
@@ -40,13 +41,13 @@ class PinnedNavBarState {
|
|||||||
if (isPinned(type)) return
|
if (isPinned(type)) return
|
||||||
if (!isPinnable(type)) return
|
if (!isPinnable(type)) return
|
||||||
_pinnedScreens.update { it + type }
|
_pinnedScreens.update { it + type }
|
||||||
save()
|
syncToWorkspace()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unpin(type: DeckColumnType) {
|
fun unpin(type: DeckColumnType) {
|
||||||
if (!isUnpinnable(type)) return
|
if (!isUnpinnable(type)) return
|
||||||
_pinnedScreens.update { current -> current.filter { it.typeKey() != type.typeKey() } }
|
_pinnedScreens.update { current -> current.filter { it.typeKey() != type.typeKey() } }
|
||||||
save()
|
syncToWorkspace()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun move(
|
fun move(
|
||||||
@@ -60,34 +61,32 @@ class PinnedNavBarState {
|
|||||||
mutable.add(toIndex, item)
|
mutable.add(toIndex, item)
|
||||||
mutable.toList()
|
mutable.toList()
|
||||||
}
|
}
|
||||||
save()
|
syncToWorkspace()
|
||||||
}
|
|
||||||
|
|
||||||
fun save() {
|
|
||||||
DesktopPreferences.pinnedNavItems = _pinnedScreens.value.joinToString(",") { it.typeKey() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadFromList(screens: List<DeckColumnType>) {
|
fun loadFromList(screens: List<DeckColumnType>) {
|
||||||
_pinnedScreens.value = screens.ifEmpty { DEFAULT_PINNED }
|
_pinnedScreens.value = screens.ifEmpty { DEFAULT_PINNED }
|
||||||
save()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load() {
|
fun loadFromWorkspace() {
|
||||||
val raw = DesktopPreferences.pinnedNavItems
|
val ws = workspaceManager?.activeWorkspace ?: return
|
||||||
if (raw.isBlank()) {
|
if (ws.singlePaneScreens.isNotEmpty()) {
|
||||||
_pinnedScreens.value = DEFAULT_PINNED
|
val screens =
|
||||||
return
|
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 ->
|
private fun syncToWorkspace() {
|
||||||
PINNABLE_SCREENS.find { it.typeKey() == key }
|
val wm = workspaceManager ?: return
|
||||||
}
|
val ws = wm.activeWorkspace
|
||||||
_pinnedScreens.value = screens.ifEmpty { DEFAULT_PINNED }
|
val updated = ws.copy(singlePaneScreens = _pinnedScreens.value.map { it.typeKey() })
|
||||||
|
wm.updateWorkspace(updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Only object types are pinnable (no parameterized types like Hashtag, Editor)
|
|
||||||
val PINNABLE_SCREENS: List<DeckColumnType> =
|
val PINNABLE_SCREENS: List<DeckColumnType> =
|
||||||
LAUNCHABLE_SCREENS.filter { !it.requiresInput() && it !is DeckColumnType.Editor }
|
LAUNCHABLE_SCREENS.filter { !it.requiresInput() && it !is DeckColumnType.Editor }
|
||||||
|
|
||||||
@@ -106,7 +105,6 @@ class PinnedNavBarState {
|
|||||||
DeckColumnType.Settings,
|
DeckColumnType.Settings,
|
||||||
)
|
)
|
||||||
|
|
||||||
// These screens cannot be unpinned
|
|
||||||
private val ALWAYS_PINNED = setOf("home", "settings")
|
private val ALWAYS_PINNED = setOf("home", "settings")
|
||||||
|
|
||||||
fun isPinnable(type: DeckColumnType): Boolean = PINNABLE_SCREENS.any { it.typeKey() == type.typeKey() }
|
fun isPinnable(type: DeckColumnType): Boolean = PINNABLE_SCREENS.any { it.typeKey() == type.typeKey() }
|
||||||
|
|||||||
Reference in New Issue
Block a user