auth to server-V24
This commit is contained in:
parent
a400f115a9
commit
acba929b6d
4 changed files with 71 additions and 12 deletions
|
|
@ -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()}")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 -> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue