diff --git a/android/build.gradle.kts b/android/build.gradle.kts index a78a0b1aa..a3dd98377 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -27,8 +27,8 @@ android { applicationId = "com.xiaoyv.bangumi.multiplatform" minSdk = libs.versions.android.minSdk.get().toInt() targetSdk = libs.versions.android.targetSdk.get().toInt() - versionCode = 1 - versionName = "1.0" + versionCode = 2 + versionName = "2.0.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunnerArguments["clearPackageData"] = "false" diff --git a/features/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/main/DefaultNavigationSuiteScaffoldOverride.kt b/features/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/main/DefaultNavigationSuiteScaffoldOverride.kt index 68ac92180..c78f1ce40 100644 --- a/features/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/main/DefaultNavigationSuiteScaffoldOverride.kt +++ b/features/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/main/DefaultNavigationSuiteScaffoldOverride.kt @@ -8,9 +8,12 @@ import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi +import androidx.compose.material3.adaptive.navigationsuite.LocalNavigationSuiteScaffoldOverride import androidx.compose.material3.adaptive.navigationsuite.NavigationItemIcon +import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldOverride import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldOverrideScope +import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope import androidx.compose.material3.adaptive.navigationsuite.rememberStateOfItems import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -24,7 +27,7 @@ import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import com.kyant.backdrop.catalog.components.LiquidBottomTab import com.kyant.backdrop.catalog.components.LiquidBottomTabs -import com.xiaoyv.bangumi.features.main.DefaultNavigationSuiteScaffoldOverride.NavigationSuiteScaffold +import com.xiaoyv.bangumi.shared.core.types.settings.SettingBottomBarAppearance private val NoWindowInsets = WindowInsets(0, 0, 0, 0) @@ -81,6 +84,30 @@ object DefaultNavigationSuiteScaffoldOverride : NavigationSuiteScaffoldOverride } } +@OptIn(ExperimentalMaterial3AdaptiveComponentOverrideApi::class) +@Composable +fun BgmNavigationSuiteScaffold( + @SettingBottomBarAppearance appearance: Int, + navigationSuiteItems: NavigationSuiteScope.() -> Unit, + modifier: Modifier = Modifier, + content: @Composable () -> Unit = {}, +) { + val scaffoldOverride = when (appearance) { + SettingBottomBarAppearance.LIQUID_GLASS -> DefaultNavigationSuiteScaffoldOverride + else -> androidx.compose.material3.adaptive.navigationsuite.DefaultNavigationSuiteScaffoldOverride + } + + CompositionLocalProvider( + LocalNavigationSuiteScaffoldOverride provides scaffoldOverride, + ) { + NavigationSuiteScaffold( + navigationSuiteItems = navigationSuiteItems, + modifier = modifier, + content = content, + ) + } +} + @Composable internal fun ProvideContentColorTextStyle( contentColor: Color, @@ -93,4 +120,4 @@ internal fun ProvideContentColorTextStyle( LocalTextStyle provides mergedStyle, content = content, ) -} \ No newline at end of file +} diff --git a/features/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/main/MainScreen.kt b/features/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/main/MainScreen.kt index 1d515ce4d..d66eac3f6 100644 --- a/features/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/main/MainScreen.kt +++ b/features/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/main/MainScreen.kt @@ -11,7 +11,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.Text -import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -26,6 +25,7 @@ import com.xiaoyv.bangumi.features.main.business.MainState import com.xiaoyv.bangumi.features.main.business.MainViewModel import com.xiaoyv.bangumi.shared.data.manager.shared.LocalHideNavIcon import com.xiaoyv.bangumi.shared.data.manager.shared.LocalSharedState +import com.xiaoyv.bangumi.shared.data.manager.shared.currentSettings import com.xiaoyv.bangumi.shared.ui.component.navigation.PagerNavHost import com.xiaoyv.bangumi.shared.ui.component.navigation.Screen import com.xiaoyv.bangumi.shared.ui.component.navigation.current @@ -66,6 +66,7 @@ fun MainScreen( onUiEvent: (MainEvent.UI) -> Unit, onActionEvent: (MainEvent.Action) -> Unit, ) { + val settings = currentSettings() val bottomTabs = state.rememberBottomTabs() val startDestination = remember(bottomTabs, state.defaultSelected) { bottomTabs.getOrNull(state.defaultSelected) ?: bottomTabs.first() @@ -79,7 +80,8 @@ fun MainScreen( ), ) - NavigationSuiteScaffold( + BgmNavigationSuiteScaffold( + appearance = settings.homeTab.appearance, navigationSuiteItems = { bottomTabs.forEach { item -> val selected = backStack.current == item.first diff --git a/features/settings/bar/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/bar/SettingsBarScreen.kt b/features/settings/bar/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/bar/SettingsBarScreen.kt index 840408b61..b4e183d1d 100644 --- a/features/settings/bar/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/bar/SettingsBarScreen.kt +++ b/features/settings/bar/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/bar/SettingsBarScreen.kt @@ -23,13 +23,16 @@ import com.xiaoyv.bangumi.core_resource.resources.settings_bar_2 import com.xiaoyv.bangumi.core_resource.resources.settings_bar_3 import com.xiaoyv.bangumi.core_resource.resources.settings_bar_4 import com.xiaoyv.bangumi.core_resource.resources.settings_bar_5 +import com.xiaoyv.bangumi.core_resource.resources.settings_bar_appearance import com.xiaoyv.bangumi.core_resource.resources.settings_bar_boot import com.xiaoyv.bangumi.core_resource.resources.settings_bar_boot_default import com.xiaoyv.bangumi.core_resource.resources.settings_bar_tab +import com.xiaoyv.bangumi.core_resource.resources.settings_appearance import com.xiaoyv.bangumi.features.settings.bar.business.SettingsBarEvent import com.xiaoyv.bangumi.features.settings.bar.business.SettingsBarState import com.xiaoyv.bangumi.features.settings.bar.business.SettingsBarViewModel import com.xiaoyv.bangumi.shared.core.mvi.BaseState +import com.xiaoyv.bangumi.shared.core.types.settings.SettingBottomBarAppearance import com.xiaoyv.bangumi.shared.data.manager.shared.currentSettings import com.xiaoyv.bangumi.shared.ui.component.bar.BgmLargeTopAppBar import com.xiaoyv.bangumi.shared.ui.component.dialog.alert.AlertOptionDialog @@ -39,6 +42,7 @@ import com.xiaoyv.bangumi.shared.ui.component.navigation.Screen import com.xiaoyv.bangumi.shared.ui.component.settings.SettingContainer import com.xiaoyv.bangumi.shared.ui.component.settings.SettingItem import com.xiaoyv.bangumi.shared.ui.component.settings.SettingItemTrailing +import com.xiaoyv.bangumi.shared.ui.component.settings.SettingOptionItem import com.xiaoyv.bangumi.shared.ui.composition.TabTokens import com.xiaoyv.bangumi.shared.ui.kts.collectBaseSideEffect import kotlinx.collections.immutable.persistentListOf @@ -112,6 +116,16 @@ private fun SettingsBarScreenContent( val settings = currentSettings() Column(modifier = Modifier.padding(vertical = 24.dp)) { + SettingContainer(label = { Text(text = stringResource(Res.string.settings_appearance)) }) { + SettingOptionItem( + title = stringResource(Res.string.settings_bar_appearance), + value = stringResource(SettingBottomBarAppearance.string(settings.homeTab.appearance)), + items = TabTokens.settingBottomBarAppearanceItems, + onClick = { + onActionEvent(SettingsBarEvent.Action.OnUpdate(settings.homeTab.copy(appearance = it))) + } + ) + } SettingContainer(label = { Text(text = stringResource(Res.string.settings_bar_boot)) }) { val chooseDefaultTabDialogState = rememberAlertDialogState() AlertOptionDialog( @@ -181,4 +195,3 @@ private fun SettingsBarScreenContent( } } } - diff --git a/features/settings/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/main/SettingsMainScreen.kt b/features/settings/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/main/SettingsMainScreen.kt index 4a1409008..b1291a639 100644 --- a/features/settings/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/main/SettingsMainScreen.kt +++ b/features/settings/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/main/SettingsMainScreen.kt @@ -72,6 +72,7 @@ import com.xiaoyv.bangumi.shared.ui.component.layout.state.StateLayout import com.xiaoyv.bangumi.shared.ui.component.navigation.Screen import com.xiaoyv.bangumi.shared.ui.component.settings.SettingContainer import com.xiaoyv.bangumi.shared.ui.component.settings.SettingItem +import com.xiaoyv.bangumi.features.settings.main.component.BangumiStatusTopBarAction import com.xiaoyv.bangumi.shared.ui.kts.collectBaseSideEffect import com.xiaoyv.bangumi.shared.ui.theme.BgmIcons import org.jetbrains.compose.resources.stringResource @@ -121,6 +122,9 @@ private fun SettingsMainScreen( BgmLargeTopAppBar( title = stringResource(Res.string.settings_title), scrollBehavior = scrollBehavior, + actions = { + BangumiStatusTopBarAction() + }, onNavigationClick = { onUiEvent(SettingsMainEvent.UI.OnNavUp) } ) } @@ -278,4 +282,3 @@ private fun SettingsMainScreenContent( } } } - diff --git a/features/settings/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/main/component/BangumiStatusTopBarAction.kt b/features/settings/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/main/component/BangumiStatusTopBarAction.kt new file mode 100644 index 000000000..7aa2c3c6d --- /dev/null +++ b/features/settings/main/src/commonMain/kotlin/com/xiaoyv/bangumi/features/settings/main/component/BangumiStatusTopBarAction.kt @@ -0,0 +1,338 @@ +package com.xiaoyv.bangumi.features.settings.main.component + +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +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.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight +import androidx.compose.material.icons.rounded.NetworkCheck +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.xiaoyv.bangumi.core_resource.resources.Res +import com.xiaoyv.bangumi.core_resource.resources.global_unknown +import com.xiaoyv.bangumi.core_resource.resources.settings_bgm_status_current +import com.xiaoyv.bangumi.core_resource.resources.settings_bgm_status_hint_degraded +import com.xiaoyv.bangumi.core_resource.resources.settings_bgm_status_hint_down +import com.xiaoyv.bangumi.core_resource.resources.settings_bgm_status_hint_ok +import com.xiaoyv.bangumi.core_resource.resources.settings_bgm_status_hint_unknown +import com.xiaoyv.bangumi.core_resource.resources.settings_bgm_status_title +import com.xiaoyv.bangumi.core_resource.resources.settings_bgm_status_updated_at +import com.xiaoyv.bangumi.core_resource.resources.settings_bgm_status_view_detail +import com.xiaoyv.bangumi.shared.core.utils.formatDate +import com.xiaoyv.bangumi.shared.data.api.client.createHttpClient +import com.xiaoyv.bangumi.shared.data.constant.WebConstant +import com.xiaoyv.bangumi.shared.data.manager.shared.currentSettings +import com.xiaoyv.bangumi.shared.ui.component.action.LocalActionHandler +import com.xiaoyv.bangumi.shared.ui.component.divider.BgmHorizontalDivider +import com.xiaoyv.bangumi.shared.ui.theme.BgmIcons +import com.xiaoyv.bangumi.shared.ui.theme.BgmIconsMirrored +import io.ktor.client.call.body +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.request.get +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.jetbrains.compose.resources.stringResource + +@Serializable +private data class BangumiStatusMini( + @SerialName("message") val message: String = "", + @SerialName("status") val status: String = "", + @SerialName("updated_at") val updatedAt: Long = 0L, +) + +private data class StatusDotInfo( + val color: Color, + val breathing: Boolean, +) + +@Composable +fun BangumiStatusTopBarAction( + modifier: Modifier = Modifier, + iconSize: Dp = 22.dp, +) { + val actionHandler = LocalActionHandler.current + val networkConfig = currentSettings().network + val client = remember(networkConfig) { createHttpClient(config = networkConfig, logLevel = LogLevel.NONE) } + DisposableEffect(client) { onDispose { client.close() } } + + val titleText = stringResource(Res.string.settings_bgm_status_title) + val currentStatusText = stringResource(Res.string.settings_bgm_status_current) + val viewDetailText = stringResource(Res.string.settings_bgm_status_view_detail) + val unknownText = stringResource(Res.string.global_unknown) + + var expanded by remember { mutableStateOf(false) } + var loading by remember { mutableStateOf(false) } + var data by remember { mutableStateOf(null) } + var requestFailed by remember { mutableStateOf(false) } + + suspend fun refresh() { + if (loading) return + loading = true + requestFailed = false + runCatching { + client.get(WebConstant.URL_BGM_STATUS_API).body() + }.onSuccess { + data = it + }.onFailure { + requestFailed = true + } + loading = false + } + + LaunchedEffect(Unit) { refresh() } + LaunchedEffect(expanded) { if (expanded) refresh() } + + Box(modifier = modifier) { + IconButton(onClick = { expanded = true }) { + Box(modifier = Modifier.size(iconSize)) { + val status = data?.status?.takeIf { it.isNotBlank() } + val indicatorStatus = if (requestFailed) null else status + val showIconOnly = requestFailed.not() && (indicatorStatus == null || indicatorStatus == "ok") + + if (showIconOnly) { + Icon( + modifier = Modifier.matchParentSize(), + imageVector = BgmIcons.NetworkCheck, + contentDescription = titleText, + ) + } else { + val dotInfo = statusDotInfo(indicatorStatus) + Box( + modifier = Modifier + .matchParentSize() + .breathing(dotInfo.breathing) + .background(dotInfo.color, CircleShape) + ) + } + } + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + Column( + modifier = Modifier + .widthIn(min = 280.dp, max = 360.dp) + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + Text( + text = titleText, + style = MaterialTheme.typography.titleMedium, + ) + + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = currentStatusText, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurface, + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + val dotInfo = statusDotInfo(if (requestFailed) null else data?.status?.takeIf { it.isNotBlank() }) + StatusBreathingDot(color = dotInfo.color, breathing = dotInfo.breathing) + Spacer(Modifier.width(6.dp)) + + TextButton( + onClick = { + expanded = false + actionHandler.openInBrowser(WebConstant.URL_BGM_STATUS_WEB) + }, + contentPadding = PaddingValues(horizontal = 6.dp, vertical = 0.dp), + colors = ButtonDefaults.textButtonColors(), + ) { + Text(text = viewDetailText) + Icon( + modifier = Modifier.size(18.dp), + imageVector = BgmIconsMirrored.KeyboardArrowRight, + contentDescription = null, + ) + } + + if (loading && data == null) { + Spacer(Modifier.width(8.dp)) + CircularProgressIndicator( + modifier = Modifier.size(14.dp), + strokeWidth = 2.dp, + ) + } + } + } + + val hintOk = stringResource(Res.string.settings_bgm_status_hint_ok) + val hintDegraded = stringResource(Res.string.settings_bgm_status_hint_degraded) + val hintDown = stringResource(Res.string.settings_bgm_status_hint_down) + val hintUnknown = stringResource(Res.string.settings_bgm_status_hint_unknown) + + val activeStatus = if (requestFailed) null else data?.status?.takeIf { it.isNotBlank() } + val inactiveHintColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) + val okHintColor = Color(0xFF55CC55) + val degradedHintColor = Color(0xFFFF9800) + val downHintColor = MaterialTheme.colorScheme.error + val unknownActiveHintColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.9f) + + val hintText = remember( + hintOk, + hintDegraded, + hintDown, + hintUnknown, + activeStatus, + inactiveHintColor, + okHintColor, + degradedHintColor, + downHintColor, + unknownActiveHintColor, + ) { + buildAnnotatedString { + fun appendLine(text: String, active: Boolean, activeColor: Color) { + withStyle(SpanStyle(color = if (active) activeColor else inactiveHintColor)) { + append(text) + } + append('\n') + } + + appendLine( + text = hintOk, + active = activeStatus == "ok", + activeColor = okHintColor, + ) + appendLine( + text = hintDegraded, + active = activeStatus == "degraded", + activeColor = degradedHintColor, + ) + appendLine( + text = hintDown, + active = activeStatus == "down", + activeColor = downHintColor, + ) + withStyle( + SpanStyle( + color = if (activeStatus == null || (activeStatus != "ok" && activeStatus != "degraded" && activeStatus != "down")) + unknownActiveHintColor + else inactiveHintColor + ) + ) { + append(hintUnknown) + } + } + } + + Text( + text = hintText, + style = MaterialTheme.typography.bodySmall, + ) + } + + val updatedAtText = data?.updatedAt + ?.takeIf { it > 0 } + ?.let { (it * 1000L).formatDate("yyyy-MM-dd HH:mm") } + ?: unknownText + + Text( + text = stringResource(Res.string.settings_bgm_status_updated_at, updatedAtText), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } +} + +@Composable +private fun StatusBreathingDot( + color: Color, + breathing: Boolean, + modifier: Modifier = Modifier, + size: Dp = 10.dp, +) { + Box( + modifier = modifier + .size(size) + .breathing(breathing) + .background(color, CircleShape) + ) +} + +@Composable +private fun statusDotInfo(status: String?): StatusDotInfo { + return when (status) { + "ok" -> StatusDotInfo(color = Color(0xFF55CC55), breathing = true) + "degraded" -> StatusDotInfo(color = Color(0xFFFF9800), breathing = true) + "down" -> StatusDotInfo(color = MaterialTheme.colorScheme.error, breathing = true) + else -> StatusDotInfo(color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f), breathing = true) + } +} + +@Composable +private fun Modifier.breathing(enabled: Boolean): Modifier { + if (!enabled) return this + val transition = rememberInfiniteTransition(label = "BangumiStatusBreathing") + val alpha by transition.animateFloat( + initialValue = 0.45f, + targetValue = 1f, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis = 1000), + repeatMode = RepeatMode.Reverse, + ), + label = "alpha", + ) + val scale by transition.animateFloat( + initialValue = 0.85f, + targetValue = 1.15f, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis = 1000), + repeatMode = RepeatMode.Reverse, + ), + label = "scale", + ) + return this.graphicsLayer( + alpha = alpha, + scaleX = scale, + scaleY = scale, + ) +} diff --git a/shared/core-resource/src/androidMain/res/values/strings.xml b/shared/core-resource/src/androidMain/res/values/strings.xml index f96e710e7..afabb2e76 100644 --- a/shared/core-resource/src/androidMain/res/values/strings.xml +++ b/shared/core-resource/src/androidMain/res/values/strings.xml @@ -247,10 +247,21 @@ 水波纹 蒙版 + Bangumi Status + 当前服务状态 + 更新于 %1$s + 查看详情 + 绿色:正常 + 橙色:服务降级、延迟很大 + 红色:服务器崩溃、Bangumi不可用 + 灰色:未知 启动 冷启动默认 Tab 底栏 + 底栏外观 + 质感设计 + 液态玻璃 首页底栏 Tab-1 首页底栏 Tab-2 首页底栏 Tab-3 diff --git a/shared/core/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/core/types/settings/SettingBottomBarAppearance.kt b/shared/core/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/core/types/settings/SettingBottomBarAppearance.kt new file mode 100644 index 000000000..f6761b21d --- /dev/null +++ b/shared/core/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/core/types/settings/SettingBottomBarAppearance.kt @@ -0,0 +1,23 @@ +package com.xiaoyv.bangumi.shared.core.types.settings + +import androidx.annotation.IntDef +import com.xiaoyv.bangumi.core_resource.resources.Res +import com.xiaoyv.bangumi.core_resource.resources.settings_bar_appearance_liquid_glass +import com.xiaoyv.bangumi.core_resource.resources.settings_bar_appearance_material3 + +@IntDef( + SettingBottomBarAppearance.MATERIAL3, + SettingBottomBarAppearance.LIQUID_GLASS, +) +@Retention(AnnotationRetention.SOURCE) +annotation class SettingBottomBarAppearance { + companion object { + const val MATERIAL3 = 0 + const val LIQUID_GLASS = 1 + + fun string(@SettingBottomBarAppearance value: Int) = when (value) { + LIQUID_GLASS -> Res.string.settings_bar_appearance_liquid_glass + else -> Res.string.settings_bar_appearance_material3 + } + } +} diff --git a/shared/data/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/data/constant/WebConstant.kt b/shared/data/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/data/constant/WebConstant.kt index 1761e4a0b..6a47a8e23 100644 --- a/shared/data/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/data/constant/WebConstant.kt +++ b/shared/data/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/data/constant/WebConstant.kt @@ -16,10 +16,11 @@ object WebConstant { const val URL_BASE_API_DOUBAN = "https://frodo.douban.com/" const val URL_BASE_API_PIXIV = "https://app-api.pixiv.net/" const val URL_BASE_WEB = "https://bgm.tv/" + const val URL_BGM_STATUS_WEB = "https://bgm-status.ry.mk/" + const val URL_BGM_STATUS_API = "https://bgm-status.ry.mk/api/mini" } fun subjectImage(id: Long, type: String = "large") = "https://api.bgm.tv/v0/subjects/$id/image?type=$type" fun characterImage(id: Long, type: String = "large") = "https://api.bgm.tv/v0/characters/$id/image?type=$type" fun personImage(id: Long, type: String = "large") = "https://api.bgm.tv/v0/persons/$id/image?type=$type" fun userImage(username: String, type: String = "large") = "https://api.bgm.tv/v0/users/$username/avatar?type=$type" - diff --git a/shared/data/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/data/model/response/bgm/ComposeSetting.kt b/shared/data/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/data/model/response/bgm/ComposeSetting.kt index 60a7ca406..82b55c9d2 100644 --- a/shared/data/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/data/model/response/bgm/ComposeSetting.kt +++ b/shared/data/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/data/model/response/bgm/ComposeSetting.kt @@ -4,6 +4,7 @@ package com.xiaoyv.bangumi.shared.data.model.response.bgm import androidx.compose.runtime.Immutable import com.xiaoyv.bangumi.shared.core.types.FeatureType +import com.xiaoyv.bangumi.shared.core.types.settings.SettingBottomBarAppearance import com.xiaoyv.bangumi.shared.core.types.settings.SettingIndication import com.xiaoyv.bangumi.shared.core.types.settings.SettingNavigationAnimation import com.xiaoyv.bangumi.shared.core.types.settings.SettingTheme @@ -60,6 +61,8 @@ data class ComposeSetting( @Serializable @Immutable data class HomeTabConfig( + @field:SettingBottomBarAppearance + @SerialName("appearance") val appearance: Int = SettingBottomBarAppearance.MATERIAL3, @SerialName("defaultSelected") val defaultSelected: Int = 0, @SerialName("tab1") @field:FeatureType val tab1: String = FeatureType.TYPE_HOME, @SerialName("tab2") @field:FeatureType val tab2: String = FeatureType.TYPE_TIMELINE, diff --git a/shared/ui/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/ui/composition/TabTokens.kt b/shared/ui/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/ui/composition/TabTokens.kt index b1db8e274..94482cda6 100644 --- a/shared/ui/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/ui/composition/TabTokens.kt +++ b/shared/ui/src/commonMain/kotlin/com/xiaoyv/bangumi/shared/ui/composition/TabTokens.kt @@ -81,6 +81,8 @@ import com.xiaoyv.bangumi.core_resource.resources.settings_navigation_animation_ import com.xiaoyv.bangumi.core_resource.resources.settings_navigation_animation_none import com.xiaoyv.bangumi.core_resource.resources.settings_navigation_animation_slide import com.xiaoyv.bangumi.core_resource.resources.settings_proxy_none +import com.xiaoyv.bangumi.core_resource.resources.settings_bar_appearance_liquid_glass +import com.xiaoyv.bangumi.core_resource.resources.settings_bar_appearance_material3 import com.xiaoyv.bangumi.core_resource.resources.settings_theme_dark import com.xiaoyv.bangumi.core_resource.resources.settings_theme_light import com.xiaoyv.bangumi.core_resource.resources.settings_theme_system @@ -122,6 +124,7 @@ import com.xiaoyv.bangumi.shared.core.types.SubjectType import com.xiaoyv.bangumi.shared.core.types.SubjectWebPath import com.xiaoyv.bangumi.shared.core.types.TimelineCat import com.xiaoyv.bangumi.shared.core.types.TimelineTab +import com.xiaoyv.bangumi.shared.core.types.settings.SettingBottomBarAppearance import com.xiaoyv.bangumi.shared.core.types.settings.SettingIndication import com.xiaoyv.bangumi.shared.core.types.settings.SettingNavigationAnimation import com.xiaoyv.bangumi.shared.core.types.settings.SettingTheme @@ -435,6 +438,11 @@ object TabTokens { ComposeTextTab(SettingTheme.DARK, Res.string.settings_theme_dark), ) + val settingBottomBarAppearanceItems = persistentListOf( + ComposeTextTab(SettingBottomBarAppearance.MATERIAL3, Res.string.settings_bar_appearance_material3), + ComposeTextTab(SettingBottomBarAppearance.LIQUID_GLASS, Res.string.settings_bar_appearance_liquid_glass), + ) + val settingTimeoutItems = persistentListOf( ComposeTextTab(10_000L, labelText = "10s"), ComposeTextTab(15_000L, labelText = "15s"),