auth to server-V91

This commit is contained in:
Eric85710 2026-05-12 16:15:35 +08:00
parent 321be95ff0
commit fae89ee041
2 changed files with 200 additions and 9 deletions

View file

@ -0,0 +1,178 @@
package com.example.login_v3.auth.SwitchAccount
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.foundation.lazy.items // 確保匯入這個
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ExitToApp
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.example.login_v3.auth.login.AuthViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AccountSwitchScreen(
viewModel: AuthViewModel = hiltViewModel<AuthViewModel>(),
onAddAccountClick: () -> Unit,
onBack: () -> Unit
) {
// 觀察 ViewModel 中的狀態
val allUsers by viewModel.allUserIds.collectAsState()
val currentUserId by viewModel.currentUserId.collectAsState()
Scaffold(
topBar = {
TopAppBar(
title = { Text("切換帳號") },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, contentDescription = null)
}
}
)
}
) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(16.dp)
) {
Text(
text = "已登入的帳號",
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(8.dp))
// 帳號清單
LazyColumn(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// 這裡加上明確的 items(items = ...)
items(items = allUsers.toList()) { userId ->
val isCurrent = (userId == currentUserId) // 這裡解決 == 類型問題
AccountItem(
userId = userId,
isCurrent = isCurrent,
onSelect = { viewModel.switchAccount(userId) },
onLogout = { viewModel.logout(userId) }
)
}
}
Spacer(modifier = Modifier.height(16.dp))
// 新增帳號按鈕
Button(
onClick = onAddAccountClick,
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp)
) {
Icon(Icons.Default.Add, contentDescription = null)
Spacer(modifier = Modifier.width(8.dp))
Text("登入另一個帳號")
}
}
}
}
@Composable
fun AccountItem(
userId: String,
isCurrent: Boolean,
onSelect: () -> Unit,
onLogout: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { onSelect() },
border = if (isCurrent) BorderStroke(2.dp, MaterialTheme.colorScheme.primary) else null,
colors = CardDefaults.cardColors(
containerColor = if (isCurrent)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.surfaceVariant
)
) {
Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
// 模擬頭像
Surface(
shape = CircleShape,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier.size(40.dp)
) {
Box(contentAlignment = Alignment.Center) {
Text(userId.take(1).uppercase(), color = Color.White)
}
}
Spacer(modifier = Modifier.width(12.dp))
Column {
Text(text = userId, style = MaterialTheme.typography.bodyLarge)
if (isCurrent) {
Text(
text = "使用中",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary
)
}
}
}
// 登出小按鈕
IconButton(onClick = onLogout) {
Icon(
imageVector = Icons.Default.ExitToApp,
contentDescription = "登出",
tint = MaterialTheme.colorScheme.error
)
}
}
}
}

View file

@ -7,10 +7,13 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.login_v3.data.repository.basic.TokenManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -36,25 +39,30 @@ class AppViewModel @Inject constructor(
private val tokenManager: TokenManager
) : ViewModel() {
private val _currentScreen = MutableStateFlow<AppScreen>(AppScreen.Loading) // 增加一個 Loading 狀態
private val _currentScreen = MutableStateFlow<AppScreen>(AppScreen.Loading)
val currentScreen: StateFlow<AppScreen> = _currentScreen.asStateFlow()
// 暴露 Token 給其他需要的 Screen 使用
val userToken: StateFlow<String?> = tokenManager.accessToken
// 修改:監聽當前活躍帳號的 Token
val userToken: StateFlow<String?> = tokenManager.currentAccessToken
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
// 修改:監聽「是否至少有一個帳號登入中」
private val hasAccountLoggedIn: Flow<Boolean> = tokenManager.allUserIds.map { it.isNotEmpty() }
init {
checkLoginStatus()
}
private fun checkLoginStatus() {
viewModelScope.launch {
// 讀取 DataStore 中的 isLogin 狀態
tokenManager.isLogin.collect { loggedIn ->
if (loggedIn) {
// 修改:根據「帳號清單是否為空」來決定初始頁面
hasAccountLoggedIn.collect { hasAccount ->
if (hasAccount) {
// 只要清單不為空,就進入主頁面
_currentScreen.value = AppScreen.ScreensTab
} else {
_currentScreen.value = AppScreen.PreReg // 或 AppScreen.Register
// 如果清單空了(所有人都登出了),回首頁/註冊頁
_currentScreen.value = AppScreen.PreReg
}
}
}
@ -64,10 +72,15 @@ class AppViewModel @Inject constructor(
_currentScreen.value = screen
}
// 修改:登出當前帳號
fun onLogout() {
viewModelScope.launch {
tokenManager.clearAuthData()
// 清除後,checkLoginStatus 的 collect 會自動觸發切換回 PreReg
// 先獲取目前的 ID
val currentId = tokenManager.currentUserId.first()
currentId?.let {
tokenManager.logout(it)
}
// logout 完後,hasAccountLoggedIn 的 collect 會自動觸發頁面跳轉邏輯
}
}
}