fix: 优化登录状态管理与界面提示
- 在 MainActivity.kt 中调整登录状态管理逻辑,确保实时同步 TokenManager 的状态,并根据登录状态自动导航至主界面或登录页。 - 更新 NicknameSetupScreen.kt 中的图标描述文本,以提升用户体验。 - 在 AuthViewModel.kt 中重构 token 刷新逻辑,采用静默刷新策略,确保用户状态的稳定性而不影响当前登录状态。
This commit is contained in:
@@ -143,7 +143,9 @@ fun SystemUiController(
|
||||
@PreviewScreenSizes
|
||||
@Composable
|
||||
fun LifeechoApp(initialLoggedIn: Boolean = false) {
|
||||
var isLoggedIn by rememberSaveable { mutableStateOf(initialLoggedIn) }
|
||||
// 使用 remember(非 rememberSaveable),避免进程恢复时使用过时的登录状态
|
||||
// 登录状态始终以 TokenManager 为准
|
||||
var isLoggedIn by remember { mutableStateOf(initialLoggedIn) }
|
||||
val navController = rememberNavController()
|
||||
var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.CHAT) }
|
||||
|
||||
@@ -151,20 +153,25 @@ fun LifeechoApp(initialLoggedIn: Boolean = false) {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
|
||||
// 同步登录状态变化,并在登录状态恢复时自动导航到主界面
|
||||
// 实时同步 TokenManager 的登录状态,并处理导航
|
||||
androidx.compose.runtime.LaunchedEffect(TokenManager.isLoggedIn) {
|
||||
val newLoggedIn = TokenManager.isLoggedIn
|
||||
val wasLoggedOut = !isLoggedIn
|
||||
val previousLoggedIn = isLoggedIn
|
||||
isLoggedIn = newLoggedIn
|
||||
|
||||
// 如果从未登录变为已登录,且当前在登录页,自动跳转到主界面
|
||||
if (newLoggedIn && wasLoggedOut) {
|
||||
val currentRoute = navController.currentDestination?.route
|
||||
if (currentRoute == Screen.Login.route || currentRoute == null) {
|
||||
if (newLoggedIn && !previousLoggedIn) {
|
||||
// 从未登录变为已登录:跳转到主界面
|
||||
val route = navController.currentDestination?.route
|
||||
if (route == Screen.Login.route || route == Screen.NicknameSetup.route || route == null) {
|
||||
navController.navigate(Screen.ConversationList.route) {
|
||||
popUpTo(0) { inclusive = true }
|
||||
}
|
||||
}
|
||||
} else if (!newLoggedIn && previousLoggedIn) {
|
||||
// 从已登录变为未登录(如后台token刷新失败):跳转到登录页
|
||||
navController.navigate(Screen.Login.route) {
|
||||
popUpTo(0) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -222,8 +222,8 @@ fun ConversationListScreen(
|
||||
|
||||
// 对话列表
|
||||
items(conversations, key = { it.id }) { conversation ->
|
||||
// 兼容旧数据:将旧名称"回忆录助手"也识别为默认助手
|
||||
val isAssistant = conversation.title == null || conversation.title == "回忆录助手"
|
||||
// 兼容新旧数据:将"岁月知己"和旧名称"回忆录助手"都识别为默认助手
|
||||
val isAssistant = conversation.title == null || conversation.title == "岁月知己" || conversation.title == "回忆录助手"
|
||||
val displayTitle = if (isAssistant) "岁月知己" else conversation.title!!
|
||||
val dto = ConversationListItemDto(
|
||||
id = conversation.id,
|
||||
|
||||
@@ -160,8 +160,7 @@ fun CreateMemoryScreen(
|
||||
ChatHeader(
|
||||
title = "岁月知己",
|
||||
isOnline = connectionStatus == "已连接",
|
||||
onBackClick = { navController?.popBackStack() },
|
||||
onNewConversationClick = { viewModel.startConversation() }
|
||||
onBackClick = { navController?.popBackStack() }
|
||||
)
|
||||
|
||||
// WebSocket调试面板(仅在开发模式下显示)
|
||||
|
||||
@@ -75,7 +75,7 @@ fun NicknameSetupScreen(
|
||||
// 欢迎图标
|
||||
Icon(
|
||||
imageVector = AppIcons.PersonAdd,
|
||||
contentDescription = "设置昵称",
|
||||
contentDescription = "怎么称呼你呢?",
|
||||
modifier = Modifier.size(80.dp),
|
||||
tint = LightPurple
|
||||
)
|
||||
|
||||
@@ -54,62 +54,64 @@ class AuthViewModel(private val context: Context) : ViewModel() {
|
||||
|
||||
/**
|
||||
* 检查认证状态
|
||||
* 启动时验证token有效性,如果access_token过期则尝试用refresh_token刷新
|
||||
*
|
||||
* 策略:只要本地有缓存的token,就认为已登录,直接进入主界面。
|
||||
* 后台尝试验证和刷新token,但不会因为网络错误而清除token。
|
||||
* token的失效由AuthInterceptor在实际API调用时处理。
|
||||
*/
|
||||
fun checkAuthStatus() {
|
||||
viewModelScope.launch {
|
||||
val accessToken = TokenManager.getAccessToken()
|
||||
val refreshToken = TokenManager.getRefreshToken()
|
||||
val hasToken = !accessToken.isNullOrBlank() || !refreshToken.isNullOrBlank()
|
||||
|
||||
// 只要有缓存的token就认为已登录
|
||||
_isLoggedIn.value = hasToken
|
||||
|
||||
if (!hasToken) return@launch
|
||||
|
||||
// 后台尝试获取用户信息(best effort,不影响登录状态)
|
||||
if (!accessToken.isNullOrBlank()) {
|
||||
// 有access_token,尝试获取用户信息验证token有效性
|
||||
val success = loadUserInfo(accessToken)
|
||||
if (success) {
|
||||
_isLoggedIn.value = true
|
||||
} else {
|
||||
// access_token可能过期,尝试用refresh_token刷新
|
||||
if (!refreshToken.isNullOrBlank()) {
|
||||
tryRefreshAndRestore(refreshToken)
|
||||
} else {
|
||||
// 没有refresh_token,需要重新登录
|
||||
TokenManager.clearTokens()
|
||||
_isLoggedIn.value = false
|
||||
}
|
||||
if (!success && !refreshToken.isNullOrBlank()) {
|
||||
// access_token可能过期,尝试用refresh_token静默刷新
|
||||
tryRefreshTokenSilently(refreshToken)
|
||||
}
|
||||
} else if (!refreshToken.isNullOrBlank()) {
|
||||
// 没有access_token但有refresh_token,尝试刷新
|
||||
tryRefreshAndRestore(refreshToken)
|
||||
} else {
|
||||
// 完全没有token
|
||||
_isLoggedIn.value = false
|
||||
// 只有refresh_token,尝试静默刷新
|
||||
tryRefreshTokenSilently(refreshToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试使用refresh_token刷新access_token并恢复登录
|
||||
* 静默刷新token(后台执行,不影响登录状态)
|
||||
* 只在刷新成功时更新token,失败时不清除现有token
|
||||
* token失效的处理交给AuthInterceptor在实际API调用时处理
|
||||
*/
|
||||
private suspend fun tryRefreshAndRestore(refreshToken: String) {
|
||||
val refreshResult = authService.refreshToken(refreshToken)
|
||||
refreshResult.fold(
|
||||
onSuccess = { tokenResponse ->
|
||||
// 刷新成功,保存新token
|
||||
val userId = TokenManager.getUserId() ?: ""
|
||||
TokenManager.saveTokens(
|
||||
tokenResponse.access_token,
|
||||
tokenResponse.refresh_token,
|
||||
userId
|
||||
)
|
||||
// 用新token获取用户信息
|
||||
val success = loadUserInfo(tokenResponse.access_token)
|
||||
_isLoggedIn.value = true
|
||||
},
|
||||
onFailure = {
|
||||
// 刷新也失败了,需要重新登录
|
||||
TokenManager.clearTokens()
|
||||
_isLoggedIn.value = false
|
||||
}
|
||||
)
|
||||
private suspend fun tryRefreshTokenSilently(refreshToken: String) {
|
||||
try {
|
||||
val refreshResult = authService.refreshToken(refreshToken)
|
||||
refreshResult.fold(
|
||||
onSuccess = { tokenResponse ->
|
||||
// 刷新成功,保存新token
|
||||
val userId = TokenManager.getUserId() ?: ""
|
||||
TokenManager.saveTokens(
|
||||
tokenResponse.access_token,
|
||||
tokenResponse.refresh_token,
|
||||
userId
|
||||
)
|
||||
// 用新token获取用户信息
|
||||
loadUserInfo(tokenResponse.access_token)
|
||||
},
|
||||
onFailure = {
|
||||
// 静默刷新失败,不清除token
|
||||
// 后续API调用时AuthInterceptor会再次尝试刷新
|
||||
}
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
// 网络异常等,忽略,保持现有登录状态
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user