refactor: restore settings through their typed prefs

applyBackup re-derived every key name, type and clamp by hand because
Pref.write was internal to floret-kit. floret-kit now has a typed
MutablePreferences[pref] = value, so a restored value is stored exactly
as its setter would, and a changed range or encoding reaches restore
too.
This commit is contained in:
makiolaj committed 2026-10-21 14:16:58 +02:00
1 parent 405374de7e
commit 67010a632c
3 files changed
+20 -27

No files matched your search

@@ -1,12 +1,7 @@
package de.jeanlucmakiola.clockula.data.prefs
import androidx.datastore.preferences.core.MutablePreferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import de.jeanlucmakiola.clockula.domain.ClockDefaults
import de.jeanlucmakiola.clockula.domain.DismissChallenge
import de.jeanlucmakiola.clockula.domain.Zones
import de.jeanlucmakiola.clockula.domain.backup.BackupSettings
import de.jeanlucmakiola.clockula.domain.timer.TimerPresets
import de.jeanlucmakiola.floret.prefs.Appearance
@@ -14,6 +9,7 @@ import de.jeanlucmakiola.floret.prefs.AppearancePrefs
import de.jeanlucmakiola.floret.prefs.PrefStore
import de.jeanlucmakiola.floret.prefs.ThemeMode
import de.jeanlucmakiola.floret.prefs.appearance
import de.jeanlucmakiola.floret.prefs.set
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -79,31 +75,27 @@ class SettingsPrefs @Inject constructor(private val store: PrefStore) {
suspend fun currentDefaults(): ClockDefaults = defaults.first()
/** Restore every backed-up preference in one DataStore transaction. */
/**
* Restore every backed-up preference in one DataStore transaction, each
* through its own [de.jeanlucmakiola.floret.prefs.Pref], so a restored value
* is clamped and encoded exactly as its setter would store it.
*/
suspend fun applyBackup(backup: BackupSettings) {
store.edit { prefs ->
// PrefStore's multi-key edit is public, while Pref.write belongs to
// floret-kit. Use the same named keys and normalisation as the
// individual ClockPrefs/TimerPrefs setters in this one transaction.
prefs[stringPreferencesKey(AppearancePrefs.themeMode.name)] = backup.themeMode.name
prefs[booleanPreferencesKey(AppearancePrefs.dynamicColor.name)] = backup.dynamicColor
prefs[intPreferencesKey(ClockPrefs.snoozeMinutes.name)] = backup.defaults.snoozeMinutes.coerceIn(1..60)
prefs[intPreferencesKey(ClockPrefs.snoozeLimit.name)] = backup.defaults.snoozeLimit.coerceIn(0..10)
prefs[booleanPreferencesKey(ClockPrefs.vibrate.name)] = backup.defaults.vibrate
prefs[intPreferencesKey(ClockPrefs.volumeRampSeconds.name)] = backup.defaults.volumeRampSeconds.coerceIn(0..60)
prefs.setNullable(ClockPrefs.alarmRingtoneUri.name, backup.defaults.alarmRingtoneUri?.trim()?.takeIf(String::isNotEmpty))
prefs.setNullable(ClockPrefs.timerRingtoneUri.name, backup.defaults.timerRingtoneUri?.trim()?.takeIf(String::isNotEmpty))
prefs[stringPreferencesKey(ClockPrefs.dismissChallenge.name)] = backup.defaults.dismissChallenge.name
prefs.setNullable(ClockPrefs.homeZoneId.name, backup.defaults.homeZoneId?.let(Zones::normalise))
prefs[stringPreferencesKey(TimerPrefs.presets.name)] = TimerPresets.encode(TimerPresets.sanitise(backup.timerPresets))
prefs[AppearancePrefs.themeMode] = backup.themeMode
prefs[AppearancePrefs.dynamicColor] = backup.dynamicColor
prefs[ClockPrefs.snoozeMinutes] = backup.defaults.snoozeMinutes
prefs[ClockPrefs.snoozeLimit] = backup.defaults.snoozeLimit
prefs[ClockPrefs.vibrate] = backup.defaults.vibrate
prefs[ClockPrefs.volumeRampSeconds] = backup.defaults.volumeRampSeconds
prefs[ClockPrefs.alarmRingtoneUri] = backup.defaults.alarmRingtoneUri
prefs[ClockPrefs.timerRingtoneUri] = backup.defaults.timerRingtoneUri
prefs[ClockPrefs.dismissChallenge] = backup.defaults.dismissChallenge
prefs[ClockPrefs.homeZoneId] = backup.defaults.homeZoneId
prefs[TimerPrefs.presets] = backup.timerPresets
}
}
private fun MutablePreferences.setNullable(name: String, value: String?) {
val key = stringPreferencesKey(name)
if (value == null) remove(key) else this[key] = value
}
suspend fun setSnoozeMinutes(minutes: Int) = store.set(ClockPrefs.snoozeMinutes, minutes)
suspend fun setSnoozeLimit(limit: Int) = store.set(ClockPrefs.snoozeLimit, limit)
@@ -74,7 +74,7 @@ class SettingsPrefsTest {
BackupSettings(
themeMode = ThemeMode.DARK,
dynamicColor = false,
defaults = ClockDefaults(snoozeMinutes = 99, volumeRampSeconds = 99),
defaults = ClockDefaults(snoozeMinutes = 99, volumeRampSeconds = 99, alarmRingtoneUri = " "),
timerPresets = listOf(5.minutes, 1.minutes, 5.minutes),
),
)
@@ -83,6 +83,7 @@ class SettingsPrefsTest {
assertThat(prefs.dynamicColor.first()).isFalse()
assertThat(prefs.currentDefaults().snoozeMinutes).isEqualTo(60)
assertThat(prefs.currentDefaults().volumeRampSeconds).isEqualTo(60)
assertThat(prefs.currentDefaults().alarmRingtoneUri).isNull()
assertThat(prefs.timerPresets.first()).containsExactly(1.minutes, 5.minutes).inOrder()
}