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