feat(设置): 「版本」行可点进「关于」页

设置页的「关于」分组里,「版本」此行此前点了没反应——它是全页唯一
没有 onClick 的行。现在它带 ChevronForward(与管理员入口行同一种「可
进子页」提示)并可点,进入新的「关于」页。

「关于」页只有四项:应用名(Cyrene)、版本、一句话简介、作者
(AskaEth),自己带 Scaffold + 返回按钮,观感与设置页一致。版本号复用
BuildConfig,不写死;页面里不放任何链接 / 邮箱 / 版权年份——这些信息
没有人提供过。

返回层级照项目既有的手写布尔状态 + 分层 BackHandler 模式(本项目无导航
库),新增 showAbout:
- 关于 → 设置 → 聊天;
- 设置页的处理器条件是 showSettings && !showAbout(与关于页互斥),
  所以在关于页按返回只退一层,不会一步跳回聊天;
- 抽屉 / 设置 / 管理页既有层级未动。

版本文本抽成纯函数 versionText(versionName, versionCode),设置页与关于页
共用一份格式,并由 VersionTextTest 钉住输出。

测试:485 → 487(新增 VersionTextTest 2 条),0 失败 / 0 错误;
assembleDebug 通过。
This commit is contained in:
AskaEth committed 2026-10-04 18:31:03 +08:00
1 parent a6b4bae2a0
commit 3ed2377b3c
4 files changed
+138 -2

No files matched your search

@@ -15,6 +15,7 @@ import top.yeij.cyrene.ui.admin.AdminHomeScreen
import top.yeij.cyrene.ui.common.MiuixDrawer
import top.yeij.cyrene.ui.sessions.SessionDrawerContent
import top.yeij.cyrene.ui.sessions.SessionsViewModel
import top.yeij.cyrene.ui.settings.AboutScreen
import top.yeij.cyrene.ui.settings.SettingsScreen
/**
@@ -33,9 +34,13 @@ fun ChatHomeScreen(
var drawerOpen by remember { mutableStateOf(false) }
var showSettings by remember { mutableStateOf(false) }
var showAdmin by remember { mutableStateOf(false) }
var showAbout by remember { mutableStateOf(false) }
BackHandler(enabled = showAdmin) { showAdmin = false }
BackHandler(enabled = showSettings) { showSettings = false }
// 「关于」嵌在设置页里,所以两个条件必须**互斥**:在关于页时设置页的处理器关掉,
// 返回才只退一层(关于 → 设置),不会一步跳回聊天。
BackHandler(enabled = showAbout) { showAbout = false }
BackHandler(enabled = showSettings && !showAbout) { showSettings = false }
BackHandler(enabled = drawerOpen) { drawerOpen = false }
when {
@@ -44,10 +49,13 @@ fun ChatHomeScreen(
onBack = { showAdmin = false },
)
showAbout -> AboutScreen(onBack = { showAbout = false })
showSettings -> SettingsScreen(
container = container,
onBack = { showSettings = false },
onLogout = onLogout,
onOpenAbout = { showAbout = true },
)
else -> MiuixDrawer(
@@ -0,0 +1,96 @@
package top.yeij.cyrene.ui.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import top.yeij.cyrene.BuildConfig
import top.yeij.cyrene.ui.common.PreferenceCard
import top.yukonga.miuix.kmp.basic.BasicComponent
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.SmallTopAppBar
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.ChevronBackward
import top.yukonga.miuix.kmp.theme.MiuixTheme
/**
* 「关于」页:只有名称、版本、简介、作者这四项。
*
* 这四项分别来自本端常量与 `BuildConfig`(由 `app/build.gradle.kts` 生成),**不写死版本号**;
* 页面里刻意不放任何链接 / 邮箱 / 版权年份——没有人提供过这些信息,编不得。
*
* 与设置页同为聊天之上的独立层级:自己带 `Scaffold`,返回落到设置页(层级见 `ChatHomeScreen`)。
*/
@Composable
fun AboutScreen(onBack: () -> Unit) {
Scaffold(
topBar = {
SmallTopAppBar(
title = "关于",
navigationIcon = {
IconButton(onClick = onBack) {
Icon(imageVector = MiuixIcons.ChevronBackward, contentDescription = "返回")
}
},
)
},
) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(Modifier.height(40.dp))
Text("Cyrene", style = MiuixTheme.textStyles.title1)
Spacer(Modifier.height(4.dp))
Text(
text = "Cyrene v2 后端的 Android 客户端",
style = MiuixTheme.textStyles.footnote1,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
modifier = Modifier.padding(horizontal = 24.dp),
)
Spacer(Modifier.height(32.dp))
AboutRow(title = "版本", value = versionText(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE))
Spacer(Modifier.height(8.dp))
AboutRow(title = "作者", value = "AskaEth")
Spacer(Modifier.height(32.dp))
}
}
}
@Composable
private fun AboutRow(title: String, value: String) {
PreferenceCard {
BasicComponent(
title = title,
endActions = {
Text(
text = value,
style = MiuixTheme.textStyles.footnote1,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
)
},
)
}
}
/**
* 版本号的显示文本(纯函数):设置页的「版本」行与关于页共用同一份输出,格式不会各写各的。
*
* 值一律来自 `BuildConfig`(AGP 按 `build.gradle.kts` 的 `versionName` / `versionCode` 生成),
* 两处都不写死版本号,发版只需改构建文件。
*/
internal fun versionText(versionName: String, versionCode: Int): String = "$versionName ($versionCode)"
@@ -33,6 +33,7 @@ import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Back
import top.yukonga.miuix.kmp.icon.extended.ChevronBackward
import top.yukonga.miuix.kmp.icon.extended.ChevronForward
import top.yukonga.miuix.kmp.preference.SwitchPreference
import top.yukonga.miuix.kmp.theme.MiuixTheme
@@ -41,6 +42,7 @@ fun SettingsScreen(
container: AppContainer,
onBack: () -> Unit,
onLogout: () -> Unit = {},
onOpenAbout: () -> Unit = {},
) {
val prefs by container.appSettings.data.collectAsState(initial = AppPrefs())
val scope = rememberCoroutineScope()
@@ -129,12 +131,19 @@ fun SettingsScreen(
BasicComponent(
title = "版本",
summary = null,
onClick = onOpenAbout,
endActions = {
Text(
text = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
text = versionText(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE),
style = MiuixTheme.textStyles.footnote1,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
)
Icon(
imageVector = MiuixIcons.ChevronForward,
contentDescription = null,
modifier = Modifier.padding(start = 6.dp),
tint = MiuixTheme.colorScheme.onSurfaceVariantSummary,
)
},
)
}
@@ -0,0 +1,23 @@
package top.yeij.cyrene.ui.settings
import org.junit.Assert.assertEquals
import org.junit.Test
/**
* 版本文本的格式(纯函数):设置页的「版本」行与关于页共用同一份输出。
*
* 这里钉的是**格式**,不是某个具体版本号——值来自 `BuildConfig`,发版只改构建文件。
*/
class VersionTextTest {
@Test
fun `version name is followed by the code in parentheses`() {
assertEquals("0.1.0 (1)", versionText("0.1.0", 1))
}
@Test
fun `both inputs are rendered verbatim, so the label tracks the build config`() {
assertEquals("2.3.4 (567)", versionText("2.3.4", 567))
assertEquals("1.0.0-beta (42)", versionText("1.0.0-beta", 42))
}
}