auth to server-V90
This commit is contained in:
parent
b843518d8b
commit
321be95ff0
4 changed files with 52 additions and 13 deletions
|
|
@ -77,7 +77,7 @@ fun LoginScreen(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//background
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@ import com.example.login_v3.data.repository.basic.AuthRepository
|
||||||
import com.example.login_v3.data.repository.basic.TokenManager
|
import com.example.login_v3.data.repository.basic.TokenManager
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
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.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -23,12 +25,20 @@ sealed class LoginUiState {
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class AuthViewModel @Inject constructor(
|
class AuthViewModel @Inject constructor(
|
||||||
private val repository: AuthRepository,
|
private val repository: AuthRepository,
|
||||||
private val tokenManager: TokenManager // 注入 TokenManager
|
private val tokenManager: TokenManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _loginState = MutableStateFlow<LoginUiState>(LoginUiState.Idle)
|
private val _loginState = MutableStateFlow<LoginUiState>(LoginUiState.Idle)
|
||||||
val loginState: StateFlow<LoginUiState> = _loginState.asStateFlow()
|
val loginState: StateFlow<LoginUiState> = _loginState.asStateFlow()
|
||||||
|
|
||||||
|
// --- 新增:供 UI 顯示所有已登入帳號與當前帳號 ---
|
||||||
|
val allUserIds: StateFlow<Set<String>> = tokenManager.allUserIds
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptySet())
|
||||||
|
|
||||||
|
val currentUserId: StateFlow<String?> = tokenManager.currentUserId
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
|
||||||
|
// ------------------------------------------
|
||||||
|
|
||||||
fun login(username: String, password: String) {
|
fun login(username: String, password: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_loginState.value = LoginUiState.Loading
|
_loginState.value = LoginUiState.Loading
|
||||||
|
|
@ -39,10 +49,15 @@ class AuthViewModel @Inject constructor(
|
||||||
if (response.isSuccessful && response.body() != null) {
|
if (response.isSuccessful && response.body() != null) {
|
||||||
val loginResponse = response.body()!!
|
val loginResponse = response.body()!!
|
||||||
|
|
||||||
// 1. 先把 Token 存入 DataStore
|
// 修改:存入 token 時同時存入 userId (假設你的 user 物件裡有 id)
|
||||||
tokenManager.saveAuthData(loginResponse.access_token)
|
// 如果 LoginResponse 沒給 id,也可以用 username 作為唯一識別碼
|
||||||
|
val userId = loginResponse.user.id.toString()
|
||||||
|
|
||||||
|
tokenManager.saveAuthData(
|
||||||
|
userId = userId,
|
||||||
|
token = loginResponse.access_token
|
||||||
|
)
|
||||||
|
|
||||||
// 2. 更新 UI 狀態
|
|
||||||
_loginState.value = LoginUiState.Success(loginResponse.user)
|
_loginState.value = LoginUiState.Success(loginResponse.user)
|
||||||
} else {
|
} else {
|
||||||
_loginState.value = LoginUiState.Error("登入失敗:${response.code()}")
|
_loginState.value = LoginUiState.Error("登入失敗:${response.code()}")
|
||||||
|
|
@ -52,4 +67,19 @@ class AuthViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 新增:切換帳號功能 ---
|
||||||
|
fun switchAccount(userId: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
tokenManager.switchAccount(userId)
|
||||||
|
// 切換後,通常會需要重新初始化某些頁面資料,或發送一個事件通知 UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 新增:登出功能 ---
|
||||||
|
fun logout(userId: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
tokenManager.logout(userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,20 +46,30 @@ object NetworkModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideOkHttpClient(tokenManager: TokenManager): OkHttpClient {
|
fun provideOkHttpClient(tokenManager: TokenManager): OkHttpClient {
|
||||||
return OkHttpClient.Builder()
|
return OkHttpClient.Builder()
|
||||||
.connectTimeout(15, TimeUnit.SECONDS) // 連線逾時
|
.connectTimeout(15, TimeUnit.SECONDS)
|
||||||
.readTimeout(15, TimeUnit.SECONDS) // 讀取逾時
|
.readTimeout(15, TimeUnit.SECONDS)
|
||||||
.addInterceptor { chain ->
|
.addInterceptor { chain ->
|
||||||
val token = runBlocking { tokenManager.accessToken.first() }
|
// 1. 動態獲取「目前選中帳號」的 Token
|
||||||
val requestBuilder = chain.request().newBuilder()
|
// 使用 first() 確保只取目前最新的那一個值
|
||||||
|
val token = runBlocking { tokenManager.currentAccessToken.first() }
|
||||||
|
|
||||||
|
val requestBuilder = chain.request().newBuilder()
|
||||||
token?.let {
|
token?.let {
|
||||||
requestBuilder.addHeader("Authorization", "Bearer $it")
|
requestBuilder.addHeader("Authorization", "Bearer $it")
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = chain.proceed(requestBuilder.build())
|
val response = chain.proceed(requestBuilder.build())
|
||||||
|
|
||||||
|
// 2. 處理 Token 失效 (401)
|
||||||
if (response.code == 401) {
|
if (response.code == 401) {
|
||||||
runBlocking { tokenManager.clearAuthData() }
|
runBlocking {
|
||||||
|
// 獲取目前是哪個 ID 失效了
|
||||||
|
val currentId = tokenManager.currentUserId.first()
|
||||||
|
currentId?.let {
|
||||||
|
// 只登出當前失效的帳號,不影響其他帳號
|
||||||
|
tokenManager.logout(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response
|
response
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,10 @@ class TokenManager @Inject constructor(
|
||||||
preferences[USER_ID_LIST] ?: emptySet()
|
preferences[USER_ID_LIST] ?: emptySet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TokenManager 內部建議改動
|
||||||
companion object {
|
companion object {
|
||||||
// 1. 記錄目前正在使用的 UserId
|
private val USER_ID_LIST = stringSetPreferencesKey("user_id_list") // 移到這裡
|
||||||
private val CURRENT_USER_ID = stringPreferencesKey("current_user_id")
|
private val CURRENT_USER_ID = stringPreferencesKey("current_user_id")
|
||||||
|
|
||||||
// 2. 輔助函式:根據 userId 動態產生 Token 的 Key
|
|
||||||
private fun accessTokenKey(userId: String) = stringPreferencesKey("access_token_$userId")
|
private fun accessTokenKey(userId: String) = stringPreferencesKey("access_token_$userId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue