Explorar o código

refactor: 迁移 ReadSettings 设置网关

5151561 hai 3 meses
pai
achega
f212aa4ea9

+ 54 - 48
app/src/main/java/io/legado/app/data/repository/ReadSettingsRepository.kt

@@ -9,7 +9,6 @@ import io.legado.app.constant.PreferKey
 import io.legado.app.constant.ReadMenuBlurMode
 import io.legado.app.constant.ReadMenuBlurStyle
 import io.legado.app.domain.gateway.ReadSettingsGateway
-import io.legado.app.domain.gateway.ReadSettingsUpdate
 import io.legado.app.domain.model.settings.ReadSettings
 import io.legado.app.help.config.AppConfigStore
 import io.legado.app.help.config.compatDsValue
@@ -34,53 +33,12 @@ class ReadSettingsRepository(
 
     val preferences: Flow<ReadPreferences> = settings
 
-    override suspend fun update(update: ReadSettingsUpdate) {
-        when (update) {
-            is ReadSettingsUpdate.ScreenOrientation -> setScreenOrientation(update.value)
-            is ReadSettingsUpdate.KeepLight -> setKeepLight(update.value)
-            is ReadSettingsUpdate.HideStatusBar -> setHideStatusBar(update.value)
-            is ReadSettingsUpdate.HideNavigationBar -> setHideNavigationBar(update.value)
-            is ReadSettingsUpdate.PaddingDisplayCutouts -> setPaddingDisplayCutouts(update.value)
-            is ReadSettingsUpdate.TitleBarMode -> setTitleBarMode(update.value)
-            is ReadSettingsUpdate.ReadMenuBlurAlpha -> setReadMenuBlurAlpha(update.value)
-            is ReadSettingsUpdate.ReadBodyToLh -> setReadBodyToLh(update.value)
-            is ReadSettingsUpdate.DefaultSourceChangeAll -> setDefaultSourceChangeAll(update.value)
-            is ReadSettingsUpdate.TextFullJustify -> setTextFullJustify(update.value)
-            is ReadSettingsUpdate.TextBottomJustify -> setTextBottomJustify(update.value)
-            is ReadSettingsUpdate.AdaptSpecialStyle -> setAdaptSpecialStyle(update.value)
-            is ReadSettingsUpdate.UseZhLayout -> setUseZhLayout(update.value)
-            is ReadSettingsUpdate.ShowBrightnessView -> setShowBrightnessView(update.value)
-            is ReadSettingsUpdate.BrightnessVwPos -> setBrightnessVwPos(update.value)
-            is ReadSettingsUpdate.Brightness -> setReadBrightness(update.value)
-            is ReadSettingsUpdate.BrightnessAuto -> setBrightnessAuto(update.value)
-            is ReadSettingsUpdate.UseUnderline -> setUseUnderline(update.value)
-            is ReadSettingsUpdate.ReadSliderMode -> setReadSliderMode(update.value)
-            is ReadSettingsUpdate.DoubleHorizontalPage -> setDoubleHorizontalPage(update.value)
-            is ReadSettingsUpdate.ProgressBarBehavior -> setProgressBarBehavior(update.value)
-            is ReadSettingsUpdate.MouseWheelPage -> setMouseWheelPage(update.value)
-            is ReadSettingsUpdate.VolumeKeyPage -> setVolumeKeyPage(update.value)
-            is ReadSettingsUpdate.VolumeKeyPageOnPlay -> setVolumeKeyPageOnPlay(update.value)
-            is ReadSettingsUpdate.KeyPageOnLongPress -> setKeyPageOnLongPress(update.value)
-            is ReadSettingsUpdate.PageTouchSlop -> setPageTouchSlop(update.value)
-            is ReadSettingsUpdate.SliderVibrator -> setSliderVibrator(update.value)
-            is ReadSettingsUpdate.SelectVibrator -> setSelectVibrator(update.value)
-            is ReadSettingsUpdate.AutoChangeSource -> setAutoChangeSource(update.value)
-            is ReadSettingsUpdate.AutoSuggestDayNight -> setAutoSuggestDayNight(update.value)
-            is ReadSettingsUpdate.SelectText -> setSelectText(update.value)
-            is ReadSettingsUpdate.NoAnimScrollPage -> setNoAnimScrollPage(update.value)
-            is ReadSettingsUpdate.ClickImgWay -> setClickImgWay(update.value)
-            is ReadSettingsUpdate.OptimizeRender -> setOptimizeRender(update.value)
-            is ReadSettingsUpdate.DisableReturnKey -> setDisableReturnKey(update.value)
-            is ReadSettingsUpdate.ShowReadTitleAddition -> setShowReadTitleAddition(update.value)
-            is ReadSettingsUpdate.TextSelectMenuConfig -> settingsRepository.putString(PreferKey.textSelectMenuConfig, update.value)
-            is ReadSettingsUpdate.ReadUrlInBrowser -> settingsRepository.putBoolean(PreferKey.readUrlOpenInBrowser, update.value)
-            is ReadSettingsUpdate.ShowMenuIcon -> setShowMenuIcon(update.value)
-            is ReadSettingsUpdate.TitleBarCompact -> setTitleBarCompact(update.value)
-            is ReadSettingsUpdate.PageKeys -> setPageKeys(update.previous, update.next)
-            is ReadSettingsUpdate.FontFolder -> setFontFolder(update.value)
-            is ReadSettingsUpdate.SystemTypefaces -> setSystemTypefaces(update.value)
-            is ReadSettingsUpdate.PreDownloadNum -> setPreDownloadNum(update.value)
-        }
+    override suspend fun update(transform: (ReadSettings) -> ReadSettings) {
+        AppConfigStore.atomicUpdate(
+            read = { it.toReadSettings() },
+            toPrefMap = ReadSettings::toGatewayPrefMap,
+            transform = transform,
+        )
     }
 
     suspend fun setScreenOrientation(value: String) =
@@ -584,3 +542,51 @@ class ReadSettingsRepository(
         val TitleBarCompact = booleanPreferencesKey(PreferKey.titleBarCompact)
     }
 }
+
+internal fun ReadSettings.toGatewayPrefMap(): Map<String, Any?> = mapOf(
+    PreferKey.screenOrientation to screenOrientation,
+    PreferKey.keepLight to keepLight,
+    PreferKey.hideStatusBar to hideStatusBar,
+    PreferKey.hideNavigationBar to hideNavigationBar,
+    PreferKey.paddingDisplayCutouts to paddingDisplayCutouts,
+    PreferKey.titleBarMode to titleBarMode,
+    PreferKey.readMenuBlurAlpha to readMenuBlurAlpha,
+    PreferKey.readBodyToLh to readBodyToLh,
+    PreferKey.defaultSourceChangeAll to defaultSourceChangeAll,
+    PreferKey.textFullJustify to textFullJustify,
+    PreferKey.textBottomJustify to textBottomJustify,
+    PreferKey.adaptSpecialStyle to adaptSpecialStyle,
+    PreferKey.useZhLayout to useZhLayout,
+    PreferKey.showBrightnessView to showBrightnessView,
+    PreferKey.brightnessVwPos to brightnessVwPos,
+    PreferKey.brightness to readBrightness,
+    PreferKey.brightnessAuto to brightnessAuto,
+    PreferKey.useUnderline to useUnderline,
+    PreferKey.readSliderMode to readSliderMode,
+    PreferKey.doublePageHorizontal to doubleHorizontalPage,
+    PreferKey.progressBarBehavior to progressBarBehavior,
+    PreferKey.mouseWheelPage to mouseWheelPage,
+    PreferKey.volumeKeyPage to volumeKeyPage,
+    PreferKey.volumeKeyPageOnPlay to volumeKeyPageOnPlay,
+    PreferKey.keyPageOnLongPress to keyPageOnLongPress,
+    PreferKey.pageTouchSlop to pageTouchSlop,
+    PreferKey.sliderVibrator to sliderVibrator,
+    PreferKey.selectVibrator to selectVibrator,
+    PreferKey.autoChangeSource to autoChangeSource,
+    PreferKey.autoSuggestDayNight to autoSuggestDayNight,
+    PreferKey.selectText to selectText,
+    PreferKey.noAnimScrollPage to noAnimScrollPage,
+    PreferKey.clickImgWay to clickImgWay,
+    PreferKey.optimizeRender to optimizeRender,
+    PreferKey.disableReturnKey to disableReturnKey,
+    PreferKey.showReadTitleAddition to showReadTitleAddition,
+    PreferKey.textSelectMenuConfig to textSelectMenuConfig,
+    PreferKey.readUrlOpenInBrowser to readUrlInBrowser,
+    PreferKey.showMenuIcon to showMenuIcon,
+    PreferKey.titleBarCompact to titleBarCompact,
+    PreferKey.prevKeys to prevKeys,
+    PreferKey.nextKeys to nextKeys,
+    PreferKey.fontFolder to fontFolder,
+    PreferKey.systemTypefaces to systemTypefaces,
+    PreferKey.preDownloadNum to preDownloadNum,
+)

+ 1 - 48
app/src/main/java/io/legado/app/domain/gateway/ReadSettingsGateway.kt

@@ -7,52 +7,5 @@ interface ReadSettingsGateway {
     val currentSettings: ReadSettings
     val settings: Flow<ReadSettings>
 
-    suspend fun update(update: ReadSettingsUpdate)
-}
-
-sealed interface ReadSettingsUpdate {
-    data class ScreenOrientation(val value: String) : ReadSettingsUpdate
-    data class KeepLight(val value: String) : ReadSettingsUpdate
-    data class HideStatusBar(val value: Boolean) : ReadSettingsUpdate
-    data class HideNavigationBar(val value: Boolean) : ReadSettingsUpdate
-    data class PaddingDisplayCutouts(val value: Boolean) : ReadSettingsUpdate
-    data class TitleBarMode(val value: String) : ReadSettingsUpdate
-    data class ReadMenuBlurAlpha(val value: Int) : ReadSettingsUpdate
-    data class ReadBodyToLh(val value: Boolean) : ReadSettingsUpdate
-    data class DefaultSourceChangeAll(val value: Boolean) : ReadSettingsUpdate
-    data class TextFullJustify(val value: Boolean) : ReadSettingsUpdate
-    data class TextBottomJustify(val value: Boolean) : ReadSettingsUpdate
-    data class AdaptSpecialStyle(val value: Boolean) : ReadSettingsUpdate
-    data class UseZhLayout(val value: Boolean) : ReadSettingsUpdate
-    data class ShowBrightnessView(val value: String) : ReadSettingsUpdate
-    data class BrightnessVwPos(val value: String) : ReadSettingsUpdate
-    data class Brightness(val value: Int) : ReadSettingsUpdate
-    data class BrightnessAuto(val value: Boolean) : ReadSettingsUpdate
-    data class UseUnderline(val value: Boolean) : ReadSettingsUpdate
-    data class ReadSliderMode(val value: String) : ReadSettingsUpdate
-    data class DoubleHorizontalPage(val value: String) : ReadSettingsUpdate
-    data class ProgressBarBehavior(val value: String) : ReadSettingsUpdate
-    data class MouseWheelPage(val value: Boolean) : ReadSettingsUpdate
-    data class VolumeKeyPage(val value: Boolean) : ReadSettingsUpdate
-    data class VolumeKeyPageOnPlay(val value: Boolean) : ReadSettingsUpdate
-    data class KeyPageOnLongPress(val value: Boolean) : ReadSettingsUpdate
-    data class PageTouchSlop(val value: Int) : ReadSettingsUpdate
-    data class SliderVibrator(val value: Boolean) : ReadSettingsUpdate
-    data class SelectVibrator(val value: Boolean) : ReadSettingsUpdate
-    data class AutoChangeSource(val value: Boolean) : ReadSettingsUpdate
-    data class AutoSuggestDayNight(val value: Boolean) : ReadSettingsUpdate
-    data class SelectText(val value: Boolean) : ReadSettingsUpdate
-    data class NoAnimScrollPage(val value: Boolean) : ReadSettingsUpdate
-    data class ClickImgWay(val value: String) : ReadSettingsUpdate
-    data class OptimizeRender(val value: Boolean) : ReadSettingsUpdate
-    data class DisableReturnKey(val value: Boolean) : ReadSettingsUpdate
-    data class ShowReadTitleAddition(val value: Boolean) : ReadSettingsUpdate
-    data class TextSelectMenuConfig(val value: String) : ReadSettingsUpdate
-    data class ReadUrlInBrowser(val value: Boolean) : ReadSettingsUpdate
-    data class ShowMenuIcon(val value: Boolean) : ReadSettingsUpdate
-    data class TitleBarCompact(val value: Boolean) : ReadSettingsUpdate
-    data class PageKeys(val previous: String, val next: String) : ReadSettingsUpdate
-    data class FontFolder(val value: String) : ReadSettingsUpdate
-    data class SystemTypefaces(val value: Int) : ReadSettingsUpdate
-    data class PreDownloadNum(val value: Int) : ReadSettingsUpdate
+    suspend fun update(transform: (ReadSettings) -> ReadSettings)
 }

+ 1 - 2
app/src/main/java/io/legado/app/ui/book/read/MangaMenu.kt

@@ -22,7 +22,6 @@ import io.legado.app.model.ReadManga
 import io.legado.app.ui.browser.WebViewActivity
 import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.domain.gateway.ReadSettingsGateway
-import io.legado.app.domain.gateway.ReadSettingsUpdate
 import androidx.lifecycle.lifecycleScope
 import kotlinx.coroutines.launch
 import org.koin.core.context.GlobalContext
@@ -282,7 +281,7 @@ class MangaMenu @JvmOverloads constructor(
 
     private fun updateReadUrlInBrowser(enabled: Boolean) {
         activity?.lifecycleScope?.launch {
-            readSettingsGateway.update(ReadSettingsUpdate.ReadUrlInBrowser(enabled))
+            readSettingsGateway.update { it.copy(readUrlInBrowser = enabled) }
         }
     }
 

+ 2 - 3
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -47,7 +47,6 @@ import io.legado.app.domain.gateway.BookContentProcessGateway
 import io.legado.app.domain.gateway.ChangeSourceSettingsGateway
 import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.domain.gateway.OtherSettingsGateway
-import io.legado.app.domain.gateway.ReadSettingsUpdate
 import io.legado.app.domain.gateway.ReadStyleBooleanKey
 import io.legado.app.domain.gateway.ReadStyleColorKey
 import io.legado.app.domain.gateway.ReadStyleFloatKey
@@ -256,7 +255,7 @@ class ReadBookViewModel(
 
     fun setTextSelectMenuConfig(value: String) {
         viewModelScope.launch {
-            readSettingsRepository.update(ReadSettingsUpdate.TextSelectMenuConfig(value))
+            readSettingsRepository.update { it.copy(textSelectMenuConfig = value) }
         }
     }
 
@@ -5255,7 +5254,7 @@ class ReadBookViewModel(
             }
             is ConfigUpdate.TitleBarCompact -> {
                 viewModelScope.launch {
-                    readSettingsRepository.update(ReadSettingsUpdate.TitleBarCompact(update.value))
+                    readSettingsRepository.update { it.copy(titleBarCompact = update.value) }
                 }
                 _uiState.update { it.copy(menuConfig = it.menuConfig.copy(titleBarCompact = update.value)) }
             }

+ 16 - 17
app/src/main/java/io/legado/app/ui/config/readConfig/ApplyReadSettingUseCase.kt

@@ -1,7 +1,6 @@
 package io.legado.app.ui.config.readConfig
 
 import io.legado.app.constant.EventBus
-import io.legado.app.domain.gateway.ReadSettingsUpdate
 import io.legado.app.model.ReadBook
 import io.legado.app.ui.book.read.page.provider.ChapterProvider
 import io.legado.app.utils.postEvent
@@ -9,35 +8,35 @@ import io.legado.app.utils.postEvent
 /** Applies runtime reader changes after a setting has entered the effective settings snapshot. */
 class ApplyReadSettingUseCase {
 
-    operator fun invoke(update: ReadSettingsUpdate) {
-        when (update) {
-            is ReadSettingsUpdate.HideStatusBar,
-            is ReadSettingsUpdate.HideNavigationBar -> {
+    operator fun invoke(intent: ReadConfigIntent) {
+        when (intent) {
+            is ReadConfigIntent.HideStatusBarChanged,
+            is ReadConfigIntent.HideNavigationBarChanged -> {
                 postEvent(EventBus.UP_CONFIG, arrayListOf(0, 2))
             }
 
-            is ReadSettingsUpdate.ReadMenuBlurAlpha,
-            is ReadSettingsUpdate.ReadSliderMode,
-            is ReadSettingsUpdate.ShowReadTitleAddition,
-            is ReadSettingsUpdate.ShowMenuIcon -> {
+            is ReadConfigIntent.ReadMenuBlurAlphaChanged,
+            is ReadConfigIntent.ReadSliderModeChanged,
+            is ReadConfigIntent.ShowReadTitleAdditionChanged,
+            is ReadConfigIntent.ShowMenuIconChanged -> {
                 postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
             }
 
-            is ReadSettingsUpdate.TextFullJustify,
-            is ReadSettingsUpdate.TextBottomJustify,
-            is ReadSettingsUpdate.UseZhLayout,
-            is ReadSettingsUpdate.DoubleHorizontalPage -> updateLayout()
+            is ReadConfigIntent.TextFullJustifyChanged,
+            is ReadConfigIntent.TextBottomJustifyChanged,
+            is ReadConfigIntent.UseZhLayoutChanged,
+            is ReadConfigIntent.DoubleHorizontalPageChanged -> updateLayout()
 
-            is ReadSettingsUpdate.ProgressBarBehavior -> {
+            is ReadConfigIntent.ProgressBarBehaviorChanged -> {
                 postEvent(EventBus.UP_SEEK_BAR, true)
             }
 
-            is ReadSettingsUpdate.PageTouchSlop -> {
+            is ReadConfigIntent.PageTouchSlopChanged -> {
                 postEvent(EventBus.UP_CONFIG, arrayListOf(4))
             }
 
-            is ReadSettingsUpdate.NoAnimScrollPage -> ReadBook.callBack?.upPageAnim()
-            is ReadSettingsUpdate.OptimizeRender -> updateStyle()
+            is ReadConfigIntent.NoAnimScrollPageChanged -> ReadBook.callBack?.upPageAnim()
+            is ReadConfigIntent.OptimizeRenderChanged -> updateStyle()
             else -> Unit
         }
     }

+ 44 - 43
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt

@@ -3,7 +3,6 @@ package io.legado.app.ui.config.readConfig
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import io.legado.app.domain.gateway.ReadSettingsGateway
-import io.legado.app.domain.gateway.ReadSettingsUpdate
 import io.legado.app.domain.model.settings.ReadSettings
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -38,7 +37,7 @@ class ReadConfigViewModel(
             ReadConfigIntent.OpenPageKeys -> setSheet(ReadConfigSheet.PageKeys)
             ReadConfigIntent.OpenClickActions -> setSheet(ReadConfigSheet.ClickActions)
             ReadConfigIntent.DismissSheet -> setSheet(null)
-            else -> updateSetting(intent.toSettingsUpdate())
+            else -> updateSetting(intent)
         }
     }
 
@@ -46,12 +45,12 @@ class ReadConfigViewModel(
         _uiState.update { it.copy(activeSheet = sheet) }
     }
 
-    private fun updateSetting(update: ReadSettingsUpdate) {
+    private fun updateSetting(intent: ReadConfigIntent) {
         viewModelScope.launch {
-            runCatching { settingsGateway.update(update) }
+            runCatching { settingsGateway.update(intent.toSettingsTransform()) }
                 .onSuccess {
-                    applyReadSetting(update)
-                    if (update is ReadSettingsUpdate.PageKeys) setSheet(null)
+                    applyReadSetting(intent)
+                    if (intent is ReadConfigIntent.PageKeysChanged) setSheet(null)
                 }
                 .onFailure { error ->
                     _effects.tryEmit(
@@ -64,43 +63,45 @@ class ReadConfigViewModel(
     }
 }
 
-private fun ReadConfigIntent.toSettingsUpdate(): ReadSettingsUpdate = when (this) {
-    is ReadConfigIntent.ScreenOrientationChanged -> ReadSettingsUpdate.ScreenOrientation(value)
-    is ReadConfigIntent.KeepLightChanged -> ReadSettingsUpdate.KeepLight(value)
-    is ReadConfigIntent.HideStatusBarChanged -> ReadSettingsUpdate.HideStatusBar(value)
-    is ReadConfigIntent.HideNavigationBarChanged -> ReadSettingsUpdate.HideNavigationBar(value)
-    is ReadConfigIntent.PaddingDisplayCutoutsChanged -> ReadSettingsUpdate.PaddingDisplayCutouts(value)
-    is ReadConfigIntent.TitleBarModeChanged -> ReadSettingsUpdate.TitleBarMode(value)
-    is ReadConfigIntent.ReadMenuBlurAlphaChanged -> ReadSettingsUpdate.ReadMenuBlurAlpha(value)
-    is ReadConfigIntent.ReadBodyToLhChanged -> ReadSettingsUpdate.ReadBodyToLh(value)
-    is ReadConfigIntent.DefaultSourceChangeAllChanged -> ReadSettingsUpdate.DefaultSourceChangeAll(value)
-    is ReadConfigIntent.TextFullJustifyChanged -> ReadSettingsUpdate.TextFullJustify(value)
-    is ReadConfigIntent.TextBottomJustifyChanged -> ReadSettingsUpdate.TextBottomJustify(value)
-    is ReadConfigIntent.AdaptSpecialStyleChanged -> ReadSettingsUpdate.AdaptSpecialStyle(value)
-    is ReadConfigIntent.UseZhLayoutChanged -> ReadSettingsUpdate.UseZhLayout(value)
-    is ReadConfigIntent.ShowBrightnessViewChanged -> ReadSettingsUpdate.ShowBrightnessView(value)
-    is ReadConfigIntent.BrightnessVwPosChanged -> ReadSettingsUpdate.BrightnessVwPos(value)
-    is ReadConfigIntent.UseUnderlineChanged -> ReadSettingsUpdate.UseUnderline(value)
-    is ReadConfigIntent.ReadSliderModeChanged -> ReadSettingsUpdate.ReadSliderMode(value)
-    is ReadConfigIntent.DoubleHorizontalPageChanged -> ReadSettingsUpdate.DoubleHorizontalPage(value)
-    is ReadConfigIntent.ProgressBarBehaviorChanged -> ReadSettingsUpdate.ProgressBarBehavior(value)
-    is ReadConfigIntent.MouseWheelPageChanged -> ReadSettingsUpdate.MouseWheelPage(value)
-    is ReadConfigIntent.VolumeKeyPageChanged -> ReadSettingsUpdate.VolumeKeyPage(value)
-    is ReadConfigIntent.VolumeKeyPageOnPlayChanged -> ReadSettingsUpdate.VolumeKeyPageOnPlay(value)
-    is ReadConfigIntent.KeyPageOnLongPressChanged -> ReadSettingsUpdate.KeyPageOnLongPress(value)
-    is ReadConfigIntent.PageTouchSlopChanged -> ReadSettingsUpdate.PageTouchSlop(value)
-    is ReadConfigIntent.SliderVibratorChanged -> ReadSettingsUpdate.SliderVibrator(value)
-    is ReadConfigIntent.SelectVibratorChanged -> ReadSettingsUpdate.SelectVibrator(value)
-    is ReadConfigIntent.AutoChangeSourceChanged -> ReadSettingsUpdate.AutoChangeSource(value)
-    is ReadConfigIntent.AutoSuggestDayNightChanged -> ReadSettingsUpdate.AutoSuggestDayNight(value)
-    is ReadConfigIntent.SelectTextChanged -> ReadSettingsUpdate.SelectText(value)
-    is ReadConfigIntent.NoAnimScrollPageChanged -> ReadSettingsUpdate.NoAnimScrollPage(value)
-    is ReadConfigIntent.ClickImgWayChanged -> ReadSettingsUpdate.ClickImgWay(value)
-    is ReadConfigIntent.OptimizeRenderChanged -> ReadSettingsUpdate.OptimizeRender(value)
-    is ReadConfigIntent.DisableReturnKeyChanged -> ReadSettingsUpdate.DisableReturnKey(value)
-    is ReadConfigIntent.ShowReadTitleAdditionChanged -> ReadSettingsUpdate.ShowReadTitleAddition(value)
-    is ReadConfigIntent.ShowMenuIconChanged -> ReadSettingsUpdate.ShowMenuIcon(value)
-    is ReadConfigIntent.PageKeysChanged -> ReadSettingsUpdate.PageKeys(prevKeys, nextKeys)
+private fun ReadConfigIntent.toSettingsTransform(): (ReadSettings) -> ReadSettings = when (this) {
+    is ReadConfigIntent.ScreenOrientationChanged -> { settings -> settings.copy(screenOrientation = value) }
+    is ReadConfigIntent.KeepLightChanged -> { settings -> settings.copy(keepLight = value) }
+    is ReadConfigIntent.HideStatusBarChanged -> { settings -> settings.copy(hideStatusBar = value) }
+    is ReadConfigIntent.HideNavigationBarChanged -> { settings -> settings.copy(hideNavigationBar = value) }
+    is ReadConfigIntent.PaddingDisplayCutoutsChanged -> { settings -> settings.copy(paddingDisplayCutouts = value) }
+    is ReadConfigIntent.TitleBarModeChanged -> { settings -> settings.copy(titleBarMode = value) }
+    is ReadConfigIntent.ReadMenuBlurAlphaChanged -> { settings -> settings.copy(readMenuBlurAlpha = value) }
+    is ReadConfigIntent.ReadBodyToLhChanged -> { settings -> settings.copy(readBodyToLh = value) }
+    is ReadConfigIntent.DefaultSourceChangeAllChanged -> { settings -> settings.copy(defaultSourceChangeAll = value) }
+    is ReadConfigIntent.TextFullJustifyChanged -> { settings -> settings.copy(textFullJustify = value) }
+    is ReadConfigIntent.TextBottomJustifyChanged -> { settings -> settings.copy(textBottomJustify = value) }
+    is ReadConfigIntent.AdaptSpecialStyleChanged -> { settings -> settings.copy(adaptSpecialStyle = value) }
+    is ReadConfigIntent.UseZhLayoutChanged -> { settings -> settings.copy(useZhLayout = value) }
+    is ReadConfigIntent.ShowBrightnessViewChanged -> { settings -> settings.copy(showBrightnessView = value) }
+    is ReadConfigIntent.BrightnessVwPosChanged -> { settings -> settings.copy(brightnessVwPos = value) }
+    is ReadConfigIntent.UseUnderlineChanged -> { settings -> settings.copy(useUnderline = value) }
+    is ReadConfigIntent.ReadSliderModeChanged -> { settings -> settings.copy(readSliderMode = value) }
+    is ReadConfigIntent.DoubleHorizontalPageChanged -> { settings -> settings.copy(doubleHorizontalPage = value) }
+    is ReadConfigIntent.ProgressBarBehaviorChanged -> { settings -> settings.copy(progressBarBehavior = value) }
+    is ReadConfigIntent.MouseWheelPageChanged -> { settings -> settings.copy(mouseWheelPage = value) }
+    is ReadConfigIntent.VolumeKeyPageChanged -> { settings -> settings.copy(volumeKeyPage = value) }
+    is ReadConfigIntent.VolumeKeyPageOnPlayChanged -> { settings -> settings.copy(volumeKeyPageOnPlay = value) }
+    is ReadConfigIntent.KeyPageOnLongPressChanged -> { settings -> settings.copy(keyPageOnLongPress = value) }
+    is ReadConfigIntent.PageTouchSlopChanged -> { settings -> settings.copy(pageTouchSlop = value) }
+    is ReadConfigIntent.SliderVibratorChanged -> { settings -> settings.copy(sliderVibrator = value) }
+    is ReadConfigIntent.SelectVibratorChanged -> { settings -> settings.copy(selectVibrator = value) }
+    is ReadConfigIntent.AutoChangeSourceChanged -> { settings -> settings.copy(autoChangeSource = value) }
+    is ReadConfigIntent.AutoSuggestDayNightChanged -> { settings -> settings.copy(autoSuggestDayNight = value) }
+    is ReadConfigIntent.SelectTextChanged -> { settings -> settings.copy(selectText = value) }
+    is ReadConfigIntent.NoAnimScrollPageChanged -> { settings -> settings.copy(noAnimScrollPage = value) }
+    is ReadConfigIntent.ClickImgWayChanged -> { settings -> settings.copy(clickImgWay = value) }
+    is ReadConfigIntent.OptimizeRenderChanged -> { settings -> settings.copy(optimizeRender = value) }
+    is ReadConfigIntent.DisableReturnKeyChanged -> { settings -> settings.copy(disableReturnKey = value) }
+    is ReadConfigIntent.ShowReadTitleAdditionChanged -> { settings -> settings.copy(showReadTitleAddition = value) }
+    is ReadConfigIntent.ShowMenuIconChanged -> { settings -> settings.copy(showMenuIcon = value) }
+    is ReadConfigIntent.PageKeysChanged -> { settings ->
+        settings.copy(prevKeys = prevKeys, nextKeys = nextKeys)
+    }
     ReadConfigIntent.OpenPageKeys,
     ReadConfigIntent.OpenClickActions,
     ReadConfigIntent.DismissSheet -> error("Sheet intents do not update settings")

+ 1 - 2
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigViewModel.kt

@@ -10,7 +10,6 @@ import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.domain.gateway.AppShellSettingsUpdate
 import io.legado.app.domain.gateway.AppShellStringSetting
 import io.legado.app.domain.gateway.ReadSettingsGateway
-import io.legado.app.domain.gateway.ReadSettingsUpdate
 import io.legado.app.domain.gateway.ThemeBooleanSetting
 import io.legado.app.domain.gateway.ThemeIntSetting
 import io.legado.app.domain.gateway.ThemeSettingsGateway
@@ -113,7 +112,7 @@ class ThemeConfigViewModel(
             is ThemeConfigIntent.SelectAppFont -> setAppFont(intent.file)
             ThemeConfigIntent.ClearAppFont -> updateTheme(ThemeSettingsUpdate.AppFontPath(null))
             is ThemeConfigIntent.SetFontFolder -> viewModelScope.launch {
-                readSettingsGateway.update(ReadSettingsUpdate.FontFolder(intent.path))
+                readSettingsGateway.update { it.copy(fontFolder = intent.path) }
             }
             ThemeConfigIntent.RequestFontFolder -> _effects.tryEmit(ThemeConfigEffect.OpenFontFolder)
             is ThemeConfigIntent.RequestTimePicker -> _effects.tryEmit(

+ 141 - 0
app/src/test/java/io/legado/app/data/repository/ReadSettingsMappingTest.kt

@@ -1,7 +1,9 @@
 package io.legado.app.data.repository
 
 import androidx.datastore.preferences.core.mutablePreferencesOf
+import io.legado.app.constant.PreferKey
 import io.legado.app.constant.ReadMenuBlurStyle
+import io.legado.app.domain.model.settings.ReadSettings
 import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
@@ -10,6 +12,44 @@ import org.junit.Test
 
 class ReadSettingsMappingTest {
 
+    @Test
+    fun `阅读设置 gateway 45 键写读映射逐字段对应`() {
+        val repository = ReadSettingsRepository(
+            settingsRepository = SettingsRepository(),
+            preferencesFlow = MutableStateFlow(mutablePreferencesOf()),
+        )
+
+        readSettingsMappingSamples().forEach { expected ->
+            assertEquals(expected.expectedGatewayPrefMap(), expected.toGatewayPrefMap())
+            val actual = with(repository) {
+                expected.expectedGatewayPrefMap().toTestPreferences().toReadSettings()
+            }
+            assertEquals(expected, actual)
+        }
+    }
+
+    @Test
+    fun `PageKeys previous next 通过真实原子路径对称单批写入`() {
+        val repository = ReadSettingsRepository(
+            settingsRepository = SettingsRepository(),
+            preferencesFlow = MutableStateFlow(mutablePreferencesOf()),
+        )
+        val values = captureAtomicUpdateValues(
+            current = ReadSettings(prevKeys = "old-prev", nextKeys = "old-next"),
+            read = { with(repository) { it.toReadSettings() } },
+            toPrefMap = ReadSettings::toGatewayPrefMap,
+            transform = { it.copy(prevKeys = "new-prev", nextKeys = "new-next") },
+        )
+
+        assertEquals(
+            mapOf(
+                PreferKey.prevKeys to "new-prev",
+                PreferKey.nextKeys to "new-next",
+            ),
+            values,
+        )
+    }
+
     @Test
     fun `空快照使用精简阅读菜单默认值`() {
         val preferences = mutablePreferencesOf()
@@ -35,3 +75,104 @@ class ReadSettingsMappingTest {
         assertFalse(settings.titleBarCompact)
     }
 }
+
+private fun readSettingsMappingSamples(): List<ReadSettings> {
+    val base = ReadSettings(
+        screenOrientation = "orientation",
+        keepLight = "keep-light",
+        titleBarMode = "title-mode",
+        readMenuBlurAlpha = 37,
+        showBrightnessView = "brightness-view",
+        brightnessVwPos = "brightness-pos",
+        readBrightness = 73,
+        readSliderMode = "slider-mode",
+        doubleHorizontalPage = "double-page",
+        progressBarBehavior = "chapter",
+        pageTouchSlop = 19,
+        clickImgWay = "click-way",
+        textSelectMenuConfig = "select-menu",
+        prevKeys = "previous-keys",
+        nextKeys = "next-keys",
+        fontFolder = "font-folder",
+        systemTypefaces = 23,
+        preDownloadNum = 29,
+    )
+    return listOf(
+        base,
+        base.copy(hideStatusBar = true),
+        base.copy(hideNavigationBar = true),
+        base.copy(paddingDisplayCutouts = true),
+        base.copy(readBodyToLh = false),
+        base.copy(defaultSourceChangeAll = false),
+        base.copy(textFullJustify = false),
+        base.copy(textBottomJustify = false),
+        base.copy(adaptSpecialStyle = false),
+        base.copy(useZhLayout = true),
+        base.copy(brightnessAuto = true),
+        base.copy(useUnderline = true),
+        base.copy(mouseWheelPage = false),
+        base.copy(volumeKeyPage = false),
+        base.copy(volumeKeyPageOnPlay = false),
+        base.copy(keyPageOnLongPress = true),
+        base.copy(sliderVibrator = true),
+        base.copy(selectVibrator = true),
+        base.copy(autoChangeSource = false),
+        base.copy(autoSuggestDayNight = true),
+        base.copy(selectText = false),
+        base.copy(noAnimScrollPage = true),
+        base.copy(optimizeRender = true),
+        base.copy(disableReturnKey = true),
+        base.copy(showReadTitleAddition = false),
+        base.copy(readUrlInBrowser = true),
+        base.copy(showMenuIcon = true),
+        base.copy(titleBarCompact = true),
+    )
+}
+
+private fun ReadSettings.expectedGatewayPrefMap(): Map<String, Any?> = mapOf(
+    PreferKey.screenOrientation to screenOrientation,
+    PreferKey.keepLight to keepLight,
+    PreferKey.hideStatusBar to hideStatusBar,
+    PreferKey.hideNavigationBar to hideNavigationBar,
+    PreferKey.paddingDisplayCutouts to paddingDisplayCutouts,
+    PreferKey.titleBarMode to titleBarMode,
+    PreferKey.readMenuBlurAlpha to readMenuBlurAlpha,
+    PreferKey.readBodyToLh to readBodyToLh,
+    PreferKey.defaultSourceChangeAll to defaultSourceChangeAll,
+    PreferKey.textFullJustify to textFullJustify,
+    PreferKey.textBottomJustify to textBottomJustify,
+    PreferKey.adaptSpecialStyle to adaptSpecialStyle,
+    PreferKey.useZhLayout to useZhLayout,
+    PreferKey.showBrightnessView to showBrightnessView,
+    PreferKey.brightnessVwPos to brightnessVwPos,
+    PreferKey.brightness to readBrightness,
+    PreferKey.brightnessAuto to brightnessAuto,
+    PreferKey.useUnderline to useUnderline,
+    PreferKey.readSliderMode to readSliderMode,
+    PreferKey.doublePageHorizontal to doubleHorizontalPage,
+    PreferKey.progressBarBehavior to progressBarBehavior,
+    PreferKey.mouseWheelPage to mouseWheelPage,
+    PreferKey.volumeKeyPage to volumeKeyPage,
+    PreferKey.volumeKeyPageOnPlay to volumeKeyPageOnPlay,
+    PreferKey.keyPageOnLongPress to keyPageOnLongPress,
+    PreferKey.pageTouchSlop to pageTouchSlop,
+    PreferKey.sliderVibrator to sliderVibrator,
+    PreferKey.selectVibrator to selectVibrator,
+    PreferKey.autoChangeSource to autoChangeSource,
+    PreferKey.autoSuggestDayNight to autoSuggestDayNight,
+    PreferKey.selectText to selectText,
+    PreferKey.noAnimScrollPage to noAnimScrollPage,
+    PreferKey.clickImgWay to clickImgWay,
+    PreferKey.optimizeRender to optimizeRender,
+    PreferKey.disableReturnKey to disableReturnKey,
+    PreferKey.showReadTitleAddition to showReadTitleAddition,
+    PreferKey.textSelectMenuConfig to textSelectMenuConfig,
+    PreferKey.readUrlOpenInBrowser to readUrlInBrowser,
+    PreferKey.showMenuIcon to showMenuIcon,
+    PreferKey.titleBarCompact to titleBarCompact,
+    PreferKey.prevKeys to prevKeys,
+    PreferKey.nextKeys to nextKeys,
+    PreferKey.fontFolder to fontFolder,
+    PreferKey.systemTypefaces to systemTypefaces,
+    PreferKey.preDownloadNum to preDownloadNum,
+)