auth to server-V24

This commit is contained in:
Eric85710 2026-05-01 14:18:18 +08:00
parent a400f115a9
commit acba929b6d
4 changed files with 71 additions and 12 deletions

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import com.example.login_v3.data.api.api_class.LoginRequest import com.example.login_v3.data.api.api_class.LoginRequest
import com.example.login_v3.data.api.api_class.UserInfo import com.example.login_v3.data.api.api_class.UserInfo
import com.example.login_v3.data.repository.basic.AuthRepository import com.example.login_v3.data.repository.basic.AuthRepository
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.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -21,27 +22,28 @@ 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
) : ViewModel() { ) : ViewModel() {
// 用於內部修改的私有狀態
private val _loginState = MutableStateFlow<LoginUiState>(LoginUiState.Idle) private val _loginState = MutableStateFlow<LoginUiState>(LoginUiState.Idle)
// 暴露給 Compose 觀察的公開狀態
val loginState: StateFlow<LoginUiState> = _loginState.asStateFlow() val loginState: StateFlow<LoginUiState> = _loginState.asStateFlow()
fun login(username: String, password: String) { fun login(username: String, password: String) {
viewModelScope.launch { viewModelScope.launch {
_loginState.value = LoginUiState.Loading _loginState.value = LoginUiState.Loading
try { try {
val request = LoginRequest(username = username, password = password) val request = LoginRequest(username = username, password = password)
val response = repository.login(request) val response = repository.login(request)
if (response.isSuccessful && response.body() != null) { if (response.isSuccessful && response.body() != null) {
// 登入成功,儲存 User 資訊 val loginResponse = response.body()!!
_loginState.value = LoginUiState.Success(response.body()!!.user)
// 提示:通常這裡還會處理 Token 持久化(儲存在 DataStore 或 EncryptedSharedPreferences) // 1. 先把 Token 存入 DataStore
tokenManager.saveAuthData(loginResponse.access_token)
// 2. 更新 UI 狀態
_loginState.value = LoginUiState.Success(loginResponse.user)
} else { } else {
_loginState.value = LoginUiState.Error("登入失敗:${response.code()}") _loginState.value = LoginUiState.Error("登入失敗:${response.code()}")
} }

View file

@ -6,6 +6,7 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import com.example.login_v3.data.local.UserPreferences.UserPreferencesRepository import com.example.login_v3.data.local.UserPreferences.UserPreferencesRepository
import com.example.login_v3.data.repository.basic.TokenManager
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -21,12 +22,24 @@ private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(na
// 修正 2: 移除 private,讓 Hilt 可以訪問這個模組 // 修正 2: 移除 private,讓 Hilt 可以訪問這個模組
object DataStoreModule { object DataStoreModule {
//data store instance
@Provides
@Singleton
fun provideDataStore(@ApplicationContext context: Context): DataStore<Preferences> {
return context.dataStore
}
//UserPreferencesRepository //UserPreferencesRepository
@Provides @Provides
@Singleton @Singleton
fun provideUserPreferencesRepository( fun provideUserPreferencesRepository(dataStore: DataStore<Preferences>): UserPreferencesRepository {
@ApplicationContext context: Context return UserPreferencesRepository(dataStore)
): UserPreferencesRepository { }
return UserPreferencesRepository(context.dataStore)
@Provides
@Singleton
fun provideTokenManager(dataStore: DataStore<Preferences>): TokenManager {
return TokenManager(dataStore)
} }
} }

View file

@ -0,0 +1,44 @@
package com.example.login_v3.data.repository.basic
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
class TokenManager @Inject constructor(
private val dataStore: DataStore<Preferences>
) {
companion object {
// 建議 Key 名稱與變數名稱一致
private val ACCESS_TOKEN = stringPreferencesKey("access_token")
private val IS_LOGIN = booleanPreferencesKey("is_login")
}
// 1.2.1 版本依然使用 suspend edit
suspend fun saveAuthData(token: String) {
dataStore.edit { preferences ->
preferences[ACCESS_TOKEN] = token
preferences[IS_LOGIN] = true
}
}
// 使用 Flow 讀取資料
val accessToken: Flow<String?> = dataStore.data.map { preferences ->
preferences[ACCESS_TOKEN]
}
val isLogin: Flow<Boolean> = dataStore.data.map { preferences ->
preferences[IS_LOGIN] ?: false
}
suspend fun clearAuthData() {
dataStore.edit { preferences ->
preferences.clear()
}
}
}

View file

@ -28,7 +28,7 @@ fun AppNavGraph(
AppScreen.Login -> LoginScreen( AppScreen.Login -> LoginScreen(
paddingValues = paddingValues, paddingValues = paddingValues,
onLoginSuccess = { appViewModel.onLoginSuccess() } onLoginSuccess = { appViewModel.goTo(AppScreen.ScreensTab) }
) )
AppScreen.Register -> { AppScreen.Register -> {