mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
Merge pull request #3444 from vitorpamplona/claude/bottom-nav-reset-defaults-jxg5z0
Persist bottom bar defaults as blank sentinel for auto-migration
This commit is contained in:
+14
-2
@@ -205,7 +205,7 @@ class UiSharedPreferences(
|
||||
preferences[UI_PROPOSE_AI_IMPROVEMENTS] = sharedSettings.automaticallyProposeAiImprovements.name
|
||||
preferences[UI_USE_TRACKED_BROADCASTS] = sharedSettings.useTrackedBroadcasts.name
|
||||
preferences[UI_AUTOMATICALLY_CREATE_DRAFTS] = sharedSettings.automaticallyCreateDrafts.name
|
||||
preferences[UI_BOTTOM_BAR_ITEMS] = JsonMapper.toJson(sharedSettings.bottomBarItems)
|
||||
preferences[UI_BOTTOM_BAR_ITEMS] = encodeBottomBarItems(sharedSettings.bottomBarItems)
|
||||
preferences[UI_SHOW_HOME_NEW_THREADS_TAB] = sharedSettings.showHomeNewThreadsTab
|
||||
preferences[UI_SHOW_HOME_CONVERSATIONS_TAB] = sharedSettings.showHomeConversationsTab
|
||||
preferences[UI_SHOW_HOME_EVERYTHING_TAB] = sharedSettings.showHomeEverythingTab
|
||||
@@ -226,7 +226,19 @@ class UiSharedPreferences(
|
||||
}
|
||||
}
|
||||
|
||||
private fun decodeBottomBarItems(raw: String): List<BottomBarEntry>? {
|
||||
/**
|
||||
* Persists "follow the defaults" as a blank sentinel instead of the concrete default list.
|
||||
*
|
||||
* A user who resets the bottom bar (or who never customized it) should track whatever
|
||||
* [DefaultBottomBarEntries] is in the *installed* app version. Storing the concrete list would
|
||||
* pin them to today's default, so a future version that changes the default would never reach
|
||||
* them. Storing a blank value instead makes [decodeBottomBarItems] resolve it back to the
|
||||
* current [DefaultBottomBarEntries] on every load — i.e. the user is automatically migrated to
|
||||
* the new default. Any genuinely customized bar is still stored as JSON.
|
||||
*/
|
||||
internal fun encodeBottomBarItems(items: List<BottomBarEntry>): String = if (items == DefaultBottomBarEntries) "" else JsonMapper.toJson(items)
|
||||
|
||||
internal fun decodeBottomBarItems(raw: String): List<BottomBarEntry>? {
|
||||
if (raw.isBlank()) return DefaultBottomBarEntries
|
||||
// Current format: a JSON list of BottomBarEntry (built-ins + favorites).
|
||||
runCatching { return JsonMapper.fromJson<List<BottomBarEntry>>(raw) }
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.preferences
|
||||
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.BottomBarEntry
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.DefaultBottomBarEntries
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.NavBarItem
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Locks the "reset to defaults migrates automatically" behavior.
|
||||
*
|
||||
* Resetting the bottom bar (or never customizing it) is persisted as a blank sentinel rather than the
|
||||
* concrete default list, so that a user on the defaults tracks whatever [DefaultBottomBarEntries] the
|
||||
* *installed* app version ships. If a future version changes the default, that blank value resolves to
|
||||
* the new default on load — the user is migrated instead of being pinned to the old default.
|
||||
*/
|
||||
class BottomBarPersistenceTest {
|
||||
@Test
|
||||
fun defaultsAreStoredAsBlankSentinel() {
|
||||
assertEquals("", UiSharedPreferences.encodeBottomBarItems(DefaultBottomBarEntries))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun blankSentinelDecodesToCurrentDefaults() {
|
||||
// The blank sentinel resolves to whatever DefaultBottomBarEntries this build ships. Because it
|
||||
// returns the current constant (not a value frozen at reset time), a future version that changes
|
||||
// the default automatically migrates every user who is on the defaults.
|
||||
assertEquals(DefaultBottomBarEntries, UiSharedPreferences.decodeBottomBarItems(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun customizedBarIsStoredVerbatimAndRoundTrips() {
|
||||
val custom =
|
||||
listOf(
|
||||
BottomBarEntry.BuiltIn(NavBarItem.HOME),
|
||||
BottomBarEntry.Favorite("url:https://example.com"),
|
||||
)
|
||||
val encoded = UiSharedPreferences.encodeBottomBarItems(custom)
|
||||
assertEquals(custom, UiSharedPreferences.decodeBottomBarItems(encoded))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user