This commit is contained in:
Eric85710 2026-07-01 17:15:02 +08:00
parent eb22555b62
commit b3d00e12fc
4 changed files with 38 additions and 8 deletions

View file

@ -63,6 +63,7 @@ import kotlin.text.ifEmpty
@Composable @Composable
fun LoginScreen( fun LoginScreen(
paddingValues: PaddingValues, paddingValues: PaddingValues,
onLoginSuccess: () -> Unit = {},
viewModel: AuthViewModel = hiltViewModel() viewModel: AuthViewModel = hiltViewModel()
){ ){
@ -75,6 +76,11 @@ fun LoginScreen(
var usernameError by remember { mutableStateOf("") } var usernameError by remember { mutableStateOf("") }
var passwordError by remember { mutableStateOf("") } var passwordError by remember { mutableStateOf("") }
LaunchedEffect(uiState) {
if (uiState is LoginUiState.Success) {
onLoginSuccess()
}
}
//background //background
@ -97,7 +103,6 @@ fun LoginScreen(
//Motion_logo //Motion_logo
SvgImage( SvgImage(
resId = R.raw.tg_chat_v3, resId = R.raw.tg_chat_v3,
@ -145,7 +150,6 @@ fun LoginScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()

View file

@ -1,5 +1,6 @@
package com.example.login_v3.data.repository.basic package com.example.login_v3.data.repository.basic
import android.util.Log
import com.example.login_v3.data.api.RefreshRequest import com.example.login_v3.data.api.RefreshRequest
import com.example.login_v3.data.api.TecnologiaApi import com.example.login_v3.data.api.TecnologiaApi
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -18,8 +19,10 @@ class TokenAuthenticator @Inject constructor(
override fun authenticate(route: Route?, response: Response): Request? { override fun authenticate(route: Route?, response: Response): Request? {
// 1. 取得 DataStore 中的當前帳號資訊 // 1. 取得 DataStore 中的當前帳號資訊
Log.d("AuthDebug", "--- 進入 Authenticator (觸發網址: ${response.request.url}) ---")
val refreshToken = runBlocking { tokenManager.currentRefreshToken.first() } val refreshToken = runBlocking { tokenManager.currentRefreshToken.first() }
val currentUserId = runBlocking { tokenManager.currentUserId.first() } val currentUserId = runBlocking { tokenManager.currentUserId.first() }
Log.d("AuthDebug", "當前用戶: $currentUserId, Token是否存在: ${!refreshToken.isNullOrEmpty()}")
if (refreshToken.isNullOrEmpty() || currentUserId.isNullOrEmpty()) { if (refreshToken.isNullOrEmpty() || currentUserId.isNullOrEmpty()) {
return null // 沒有憑證資訊,不重試,直接拋出原 401 錯誤 return null // 沒有憑證資訊,不重試,直接拋出原 401 錯誤
@ -37,6 +40,9 @@ class TokenAuthenticator @Inject constructor(
// 3. 判斷刷新是否成功 // 3. 判斷刷新是否成功
if (refreshResponse != null && refreshResponse.isSuccessful) { if (refreshResponse != null && refreshResponse.isSuccessful) {
Log.d("AuthDebug", "✅ Token 刷新成功,準備寫入 DataStore")
// saveAuthData...
Log.d("AuthDebug", "✅ DataStore 寫入完成,重新發送原請求")
val newAuthData = refreshResponse.body() val newAuthData = refreshResponse.body()
if (newAuthData != null) { if (newAuthData != null) {

View file

@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@ -71,15 +72,30 @@ class AppViewModel @Inject constructor(
private fun observeGlobalLogout() { private fun observeGlobalLogout() {
viewModelScope.launch { viewModelScope.launch {
// 監聽全域狀態。如果使用者在操作過程中(例如 TokenAuthenticator 刷新失敗觸發 logout), // 同時監聽「帳號清單」與「當前 Access Token」的狀態
// 導致帳號清單變空了,就把畫面強制踢回歡迎頁。 // 只要這兩個其中一個被清空(變成空或 null),就代表完全失聯,必須踢回登入流程
hasAccountLoggedIn.collect { hasAccount -> combine(hasAccountLoggedIn, tokenManager.currentAccessToken) { hasAccount, token ->
if (!hasAccount && _currentScreen.value == AppScreen.ScreensTab) { hasAccount to token
}.collect { (hasAccount, token) ->
// 🌟 修正後的黃金判斷式:
// 如果判定「已經沒有帳號登入」或者「當前畫面在主分頁但 Token 卻是空值」
if (!hasAccount || (token.isNullOrEmpty() && _currentScreen.value == AppScreen.ScreensTab)) {
Log.e("AuthDebug", "🚨 觸發強制跳轉條件!")
// 為了避免干擾正在註冊(PreReg, Register, Login)的使用者,
// 只有在當前不在這些認證頁面時,才強制踢回 PreReg
if (_currentScreen.value != AppScreen.PreReg &&
_currentScreen.value != AppScreen.Login &&
_currentScreen.value != AppScreen.Register) {
Log.d("NavTest", "偵測到 Token 或帳號失效,強制回歡迎頁")
_currentScreen.value = AppScreen.PreReg _currentScreen.value = AppScreen.PreReg
} }
} }
} }
} }
}
fun goTo(screen: AppScreen) { fun goTo(screen: AppScreen) {
Log.d("NavTest", "Attempting to go to: $screen") Log.d("NavTest", "Attempting to go to: $screen")

View file

@ -43,7 +43,11 @@ fun AppNavGraph(
) )
AppScreen.Login -> LoginScreen( AppScreen.Login -> LoginScreen(
paddingValues = paddingValues paddingValues = paddingValues,
// 🌟 在這裡呼叫 AppViewModel 跳轉到主頁分頁
onLoginSuccess = {
appViewModel.goTo(AppScreen.ScreensTab)
}
) )
AppScreen.AddAccount -> { AppScreen.AddAccount -> {