From d0be59f6073ddab9c92739b7561befd764bde79c Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Tue, 9 Jun 2026 00:16:36 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=98=85=E8=AF=BB=E8=83=8C=E6=99=AF?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReadBookStyleConfigRepository.kt | 1 + .../legado/app/help/config/ReadBookConfig.kt | 31 +++- .../app/ui/book/read/ReadBookContract.kt | 4 + .../app/ui/book/read/ReadBookViewModel.kt | 24 ++- .../ui/book/read/sheet/BgTextConfigSheet.kt | 137 ++++++++++++------ 5 files changed, 148 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/repository/ReadBookStyleConfigRepository.kt b/app/src/main/java/io/legado/app/data/repository/ReadBookStyleConfigRepository.kt index 6458f65e6..ddef437be 100644 --- a/app/src/main/java/io/legado/app/data/repository/ReadBookStyleConfigRepository.kt +++ b/app/src/main/java/io/legado/app/data/repository/ReadBookStyleConfigRepository.kt @@ -58,4 +58,5 @@ class ReadBookStyleConfigRepository { } save() } + } diff --git a/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt index 20065ee86..91ebdd4a1 100644 --- a/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt +++ b/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt @@ -7,6 +7,8 @@ import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import androidx.annotation.Keep import androidx.core.graphics.toColorInt +import androidx.datastore.preferences.core.emptyPreferences +import androidx.datastore.preferences.core.intPreferencesKey import io.legado.app.R import io.legado.app.constant.PageAnim import io.legado.app.constant.PreferKey @@ -14,6 +16,7 @@ import io.legado.app.constant.ReadMenuBlurMode import io.legado.app.constant.ReadMenuBlurStyle import io.legado.app.data.repository.ReadPreferences import io.legado.app.data.repository.ReadStyleRepository +import io.legado.app.data.repository.dataStore import io.legado.app.help.DefaultData import io.legado.app.help.coroutine.Coroutine import io.legado.app.utils.GSON @@ -26,7 +29,13 @@ import io.legado.app.utils.getPrefString import io.legado.app.utils.hexString import io.legado.app.utils.putPrefBoolean import io.legado.app.utils.putPrefInt +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.runBlocking import splitties.init.appCtx +import java.io.IOException import java.io.InputStream /** @@ -85,6 +94,23 @@ object ReadBookConfig { initShareConfig() } + /** + * 从 DataStore 读取 Int 值,DS 无值时回退到 SharedPreferences。 + * 与 PrefDelegate.readFromDs() 逻辑一致。 + */ + private fun readIntFromDataStore(key: String, defaultValue: Int): Int { + return runBlocking(Dispatchers.IO) { + try { + appCtx.dataStore.data + .catch { if (it is IOException) emit(emptyPreferences()) else throw it } + .map { it[intPreferencesKey(key)] } + .first() ?: defaultValue + } catch (_: Exception) { + appCtx.getPrefInt(key, defaultValue) + } + } + } + @Synchronized fun getConfig(index: Int): Config { if (configList.size < 5) { @@ -179,7 +205,7 @@ object ReadBookConfig { } } - private var readStyleSelectValue = appCtx.getPrefInt(PreferKey.readStyleSelect) + private var readStyleSelectValue = readIntFromDataStore(PreferKey.readStyleSelect, 0) var readStyleSelect: Int get() = readStyleSelectValue set(value) { @@ -189,7 +215,8 @@ object ReadBookConfig { } } - private var comicStyleSelectValue = appCtx.getPrefInt(PreferKey.comicStyleSelect, readStyleSelect) + private var comicStyleSelectValue = + readIntFromDataStore(PreferKey.comicStyleSelect, readStyleSelect) var comicStyleSelect: Int get() = comicStyleSelectValue set(value) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt index e6d8a5011..339b6aac4 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt @@ -363,6 +363,7 @@ sealed interface ReadBookIntent { data object SaveReadStyleConfig : ReadBookIntent data object AddReadStyleConfig : ReadBookIntent data object DeleteCurrentReadStyleConfig : ReadBookIntent + data class ApplyPresetTheme(val presetIndex: Int) : ReadBookIntent // Bookshelf data object RemoveFromBookshelf : ReadBookIntent @@ -900,6 +901,9 @@ sealed interface ConfigUpdate { data class StatusIconDark(val value: Boolean) : ConfigUpdate { override val actions = setOf(ConfigUpdateAction.ReloadContent) } + data class StyleName(val value: String) : ConfigUpdate { + override val actions = emptySet() + } data class MenuIconShowText(val value: Boolean) : ConfigUpdate { override val actions = emptySet() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index 74e4038cb..7fc15579c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -28,6 +28,7 @@ import io.legado.app.domain.model.ReadingProgress import io.legado.app.domain.usecase.GetReadingProgressUseCase import io.legado.app.domain.usecase.UploadReadingProgressUseCase import io.legado.app.exception.NoStackTraceException +import io.legado.app.help.DefaultData import io.legado.app.help.book.BookHelp import io.legado.app.help.book.ContentProcessor import io.legado.app.help.book.isEpub @@ -63,8 +64,8 @@ import io.legado.app.ui.config.themeConfig.ThemeConfig import io.legado.app.utils.GSON import io.legado.app.utils.ImageSaveUtils import io.legado.app.utils.NetworkUtils -import io.legado.app.utils.fromJsonObject import io.legado.app.utils.StringUtils +import io.legado.app.utils.fromJsonObject import io.legado.app.utils.hexString import io.legado.app.utils.isAbsUrl import io.legado.app.utils.isTrue @@ -705,6 +706,26 @@ class ReadBookViewModel( )) } } + is ReadBookIntent.ApplyPresetTheme -> { + val presets = DefaultData.readConfigs + val preset = presets.getOrNull(intent.presetIndex) ?: return@onIntent + ReadBookConfig.durConfig = + GSON.fromJsonObject(GSON.toJson(preset)).getOrNull() + ?: return@onIntent + readBookStyleConfigRepository.save() + _uiState.update { it.copy(styleConfig = buildStyleConfig()) } + _effects.tryEmit( + ReadBookEffect.UpdateReadViewConfig( + setOf( + ConfigUpdateAction.UpdateBackground, + ConfigUpdateAction.UpdateBackgroundAlpha, + ConfigUpdateAction.UpdateStyle, + ConfigUpdateAction.UpdateSystemUi, + ConfigUpdateAction.ReloadContent + ) + ) + ) + } is ReadBookIntent.OpenBgTextConfig -> { ReadBookConfig.styleSelect = intent.index viewModelScope.launch { @@ -2246,6 +2267,7 @@ class ReadBookViewModel( is ConfigUpdate.BgTypeEInk -> ReadBookConfig.durConfig.bgTypeEInk = update.value is ConfigUpdate.BgAlpha -> ReadBookConfig.bgAlpha = update.value is ConfigUpdate.StatusIconDark -> ReadBookConfig.durConfig.setCurStatusIconDark(update.value) + is ConfigUpdate.StyleName -> ReadBookConfig.durConfig.name = update.value is ConfigUpdate.MenuIconShowText -> { ReadBookConfig.readMenuIconShowText = update.value viewModelScope.launch { diff --git a/app/src/main/java/io/legado/app/ui/book/read/sheet/BgTextConfigSheet.kt b/app/src/main/java/io/legado/app/ui/book/read/sheet/BgTextConfigSheet.kt index 103c5f849..610fdf8a4 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/sheet/BgTextConfigSheet.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/sheet/BgTextConfigSheet.kt @@ -1,6 +1,8 @@ package io.legado.app.ui.book.read.sheet +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -8,15 +10,17 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Upload import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -25,26 +29,28 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.core.graphics.toColorInt import io.legado.app.R +import io.legado.app.help.DefaultData import io.legado.app.ui.book.read.ConfigUpdate -import io.legado.app.ui.book.read.ReadBookColorPickerIds import io.legado.app.ui.book.read.ReadBookIntent import io.legado.app.ui.book.read.ReadBookStyleConfig import io.legado.app.ui.theme.LegadoTheme +import io.legado.app.ui.widget.components.alert.AppAlertDialog import io.legado.app.ui.widget.components.card.NormalCard import io.legado.app.ui.widget.components.dialog.ColorPickerSheet +import io.legado.app.ui.widget.components.dialog.TextListInputDialog import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet import io.legado.app.ui.widget.components.settingItem.TinyBgImageModeSettingItem -import io.legado.app.ui.widget.components.settingItem.TinyClickableSettingItem import io.legado.app.ui.widget.components.settingItem.TinyColorModeSettingItem import io.legado.app.ui.widget.components.settingItem.TinySliderSettingItem import io.legado.app.ui.widget.components.settingItem.TinySwitchSettingItem +import io.legado.app.ui.widget.components.text.AppText import io.legado.app.utils.hexString @Composable @@ -73,6 +79,8 @@ fun BgTextConfigSheet( var showColorPicker by remember { mutableStateOf(false) } var colorPickerIsNight by remember { mutableStateOf(false) } + var showEditNameDialog by remember { mutableStateOf(false) } + var showPresetDialog by remember { mutableStateOf(false) } AppModalBottomSheet( show = show, @@ -80,7 +88,7 @@ fun BgTextConfigSheet( onIntent(ReadBookIntent.SaveReadStyleConfig) onDismissRequest() }, - title = stringResource(R.string.style_name), + title = styleName, ) { Column( modifier = Modifier @@ -88,54 +96,28 @@ fun BgTextConfigSheet( .padding(bottom = 16.dp) .verticalScroll(rememberScrollState()), ) { - // Style name section - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Column(modifier = Modifier.weight(1f)) { - Text( - text = stringResource(R.string.style_name), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - ) - Text( - text = styleName, - style = MaterialTheme.typography.headlineMedium, - ) - } - Row { - IconButton(onClick = { - // TODO: Show edit name dialog - }) { - Icon( - painter = painterResource(R.drawable.ic_edit), - contentDescription = stringResource(R.string.edit), - ) - } - IconButton(onClick = { - onIntent(ReadBookIntent.DeleteCurrentReadStyleConfig) - }) { - Icon( - painter = painterResource(R.drawable.ic_clear_all), - contentDescription = stringResource(R.string.delete), - ) - } - } - } - - Spacer(Modifier.height(8.dp)) - // Action buttons row Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(4.dp), ) { + ActionCard( + title = stringResource(R.string.edit), + imageVector = Icons.Default.Edit, + modifier = Modifier.weight(1f), + onClick = { showEditNameDialog = true }, + ) + ActionCard( + title = stringResource(R.string.delete), + imageVector = Icons.Default.Delete, + modifier = Modifier.weight(1f), + onClick = { onIntent(ReadBookIntent.DeleteCurrentReadStyleConfig) }, + ) ActionCard( title = stringResource(R.string.restore), imageVector = Icons.Default.Refresh, modifier = Modifier.weight(1f), - onClick = { /* TODO: Show restore presets dialog */ }, + onClick = { showPresetDialog = true }, ) ActionCard( title = stringResource(R.string.import_str), @@ -251,6 +233,69 @@ fun BgTextConfigSheet( }, ) } + + + TextListInputDialog( + show = showEditNameDialog, + title = stringResource(R.string.style_name), + hint = stringResource(R.string.style_name), + initialValue = styleName, + onDismissRequest = { showEditNameDialog = false }, + onConfirm = { newName -> + onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.StyleName(newName))) + showEditNameDialog = false + }, + ) + + val presets = DefaultData.readConfigs + AppAlertDialog( + show = showPresetDialog, + onDismissRequest = { showPresetDialog = false }, + title = stringResource(R.string.restore), + content = { + presets.forEachIndexed { index, preset -> + val bgColor = runCatching { preset.bgStr.toColorInt() } + .getOrDefault(0xFFEEEEEE.toInt()) + val textColor = runCatching { preset.curTextColor() } + .getOrDefault(0xFF000000.toInt()) + NormalCard( + onClick = { + onIntent(ReadBookIntent.ApplyPresetTheme(index)) + showPresetDialog = false + }, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + ) { + Box( + modifier = Modifier + .size(24.dp) + .clip(CircleShape) + .background(Color(bgColor)) + ) + Spacer(Modifier.width(8.dp)) + Box( + modifier = Modifier + .size(24.dp) + .clip(CircleShape) + .background(Color(textColor)) + ) + Spacer(Modifier.width(12.dp)) + AppText( + text = preset.name.ifBlank { "预设${index}" }, + style = LegadoTheme.typography.labelMediumEmphasized + ) + } + } + } + }, + ) } @Composable