fix(credential): 多账号会话凭据按请求账号精确匹配;UI微调

- CredentialRepository 快照新增 requestedAccountId,暂存凭据严格按请求账号匹配,
  避免 A 账号刷新期间把暂存凭据误交给默认账号/其他账号
- Dashboard 首页提示与自动签到开关改为卡片样式,开关配色对齐雨晴粉主题
- 默认账号徽标加 maxLines/softWrap 防窄屏换行
This commit is contained in:
Liuxinyu176 2026-10-08 22:16:06 +08:00
parent 2509ab8a85
commit 79f99f5dee
3 changed files with 52 additions and 26 deletions

View File

@ -40,7 +40,9 @@ class CredentialRepository @Inject constructor(
val revision: Long, val revision: Long,
val fingerprint: String, val fingerprint: String,
/** 快照所属账号;单账号服务为 null。 */ /** 快照所属账号;单账号服务为 null。 */
val accountId: String? = null val accountId: String? = null,
/** 创建快照时调用方请求的账号 ID(多账号服务可能是 null=默认账号),用于会话内精确匹配。 */
val requestedAccountId: String? = accountId
) { ) {
val service: ServiceType get() = credential.service val service: ServiceType get() = credential.service
} }
@ -237,9 +239,9 @@ class CredentialRepository @Inject constructor(
if (session.snapshot.service == service) { if (session.snapshot.service == service) {
val matches = if (ServiceType.isMultiAccount(service)) { val matches = if (ServiceType.isMultiAccount(service)) {
val requested = accountId?.takeIf { it.isNotBlank() } val requested = accountId?.takeIf { it.isNotBlank() }
requested == null || // 只允许读取与快照同一账号的暂存凭据;null=默认账号也要看快照当初请求的是不是默认账号,
session.snapshot.accountId == null || // 避免 A 账号刷新期间把 A 的暂存凭据误交给默认账号(B)的读请求。
session.snapshot.accountId == requested requested == session.snapshot.requestedAccountId
} else { } else {
true true
} }
@ -368,7 +370,8 @@ class CredentialRepository @Inject constructor(
credential = credential, credential = credential,
revision = revisionFor(service, resolvedId), revision = revisionFor(service, resolvedId),
fingerprint = credentialFingerprint(credential)!!, fingerprint = credentialFingerprint(credential)!!,
accountId = resolvedId accountId = resolvedId,
requestedAccountId = accountId?.takeIf { it.isNotBlank() }
) )
} }

View File

@ -102,6 +102,8 @@ import com.rainy.token.ui.components.AppTips
import com.rainy.token.ui.components.asString import com.rainy.token.ui.components.asString
import com.rainy.token.ui.dashboard.autocheckin.AutoCheckinManager import com.rainy.token.ui.dashboard.autocheckin.AutoCheckinManager
import com.rainy.token.ui.theme.inkMuted import com.rainy.token.ui.theme.inkMuted
import com.rainy.token.ui.theme.PureWhite
import com.rainy.token.ui.theme.inkWarm
import com.rainy.token.ui.theme.StrawberryPink import com.rainy.token.ui.theme.StrawberryPink
import android.app.AppOpsManager import android.app.AppOpsManager
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
@ -347,28 +349,45 @@ fun DashboardScreen(
// 随机小技巧提示(每次启动随机一条)+ 最右侧自动签到开关 // 随机小技巧提示(每次启动随机一条)+ 最右侧自动签到开关
val tipTextRes = remember { AppTips.randomHintRes() } val tipTextRes = remember { AppTips.randomHintRes() }
val autoCheckinEnabled by viewModel.autoCheckinEnabled.collectAsStateWithLifecycle() val autoCheckinEnabled by viewModel.autoCheckinEnabled.collectAsStateWithLifecycle()
Row( Card(
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth(),
modifier = Modifier.padding(horizontal = 4.dp) shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
),
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
) { ) {
Text( Row(
text = "💡 " + stringResource(tipTextRes), verticalAlignment = Alignment.CenterVertically,
style = MaterialTheme.typography.bodySmall, modifier = Modifier.padding(horizontal = 14.dp, vertical = 10.dp)
color = inkMuted(), ) {
modifier = Modifier.weight(1f) Text(
) text = "💡 " + stringResource(tipTextRes),
Spacer(modifier = Modifier.width(8.dp)) style = MaterialTheme.typography.bodySmall,
Text( color = inkMuted(),
text = stringResource(R.string.auto_checkin_switch_label), modifier = Modifier.weight(1f)
style = MaterialTheme.typography.labelSmall, )
color = inkMuted() Spacer(modifier = Modifier.width(8.dp))
) Text(
Spacer(modifier = Modifier.width(2.dp)) text = stringResource(R.string.auto_checkin_switch_label),
Switch( style = MaterialTheme.typography.labelMedium,
checked = autoCheckinEnabled, color = inkWarm(),
onCheckedChange = viewModel::setAutoCheckinEnabled, fontWeight = FontWeight.SemiBold
colors = SwitchDefaults.colors(checkedThumbColor = StrawberryPink) )
) Spacer(modifier = Modifier.width(2.dp))
Switch(
checked = autoCheckinEnabled,
onCheckedChange = viewModel::setAutoCheckinEnabled,
colors = SwitchDefaults.colors(
checkedThumbColor = PureWhite,
checkedTrackColor = StrawberryPink,
checkedBorderColor = StrawberryPink,
uncheckedThumbColor = MaterialTheme.colorScheme.onSurfaceVariant,
uncheckedTrackColor = MaterialTheme.colorScheme.surfaceVariant,
uncheckedBorderColor = MaterialTheme.colorScheme.onSurfaceVariant
)
)
}
} }
val items = rememberDashboardItems( val items = rememberDashboardItems(
cards = uiState.cards, cards = uiState.cards,
@ -922,6 +941,8 @@ private fun DashboardCard(
text = stringResource(R.string.account_default_badge), text = stringResource(R.string.account_default_badge),
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
color = StrawberryPink, color = StrawberryPink,
maxLines = 1,
softWrap = false,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(StrawberryPink.copy(alpha = 0.12f)) .background(StrawberryPink.copy(alpha = 0.12f))

View File

@ -296,6 +296,8 @@ private fun AccountRow(
text = stringResource(R.string.account_default_badge), text = stringResource(R.string.account_default_badge),
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
color = StrawberryPink, color = StrawberryPink,
maxLines = 1,
softWrap = false,
modifier = Modifier modifier = Modifier
.padding(horizontal = 6.dp, vertical = 2.dp) .padding(horizontal = 6.dp, vertical = 2.dp)
.background( .background(