feat: 「我的」页退出登录/登录接入会话状态

This commit is contained in:
AndyChen1024
2026-09-09 22:16:25 +08:00
parent c41c04ff23
commit 03410d7ea2
5 changed files with 53 additions and 34 deletions

View File

@@ -96,7 +96,9 @@ fun AppNavHost() {
) {
PlanSubmitScreen(onBack = { navController.popBackStack() })
}
composable(PROFILE_ROUTE) { ProfileScreen() }
composable(PROFILE_ROUTE) {
ProfileScreen(onLoggedOut = { navigateToTab(HOME_ROUTE) })
}
}
}
}

View File

@@ -21,19 +21,6 @@ class FakeProfileRepository @Inject constructor() : ProfileRepository {
emitAll(profile)
}
override suspend fun deleteAccount() {
delay(300)
profile.value = seedProfile().copy(
displayName = "游客",
name = "",
studentId = "",
phone = null,
group = null,
avatarUrl = null,
classGroups = emptyList(),
)
}
private fun seedProfile() = UserProfile(
displayName = "张三",
name = "张三",

View File

@@ -5,6 +5,4 @@ import kotlinx.coroutines.flow.Flow
interface ProfileRepository {
fun observeProfile(): Flow<UserProfile>
suspend fun deleteAccount()
}

View File

@@ -21,6 +21,7 @@ import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -36,6 +37,7 @@ import com.mingyuan.android_client.core.designsystem.component.Avatar
import com.mingyuan.android_client.core.designsystem.component.ButtonVariant
import com.mingyuan.android_client.core.designsystem.component.ErrorState
import com.mingyuan.android_client.core.designsystem.component.LabeledRow
import com.mingyuan.android_client.core.designsystem.component.LoggedOutState
import com.mingyuan.android_client.core.designsystem.component.MingyuanButton
import com.mingyuan.android_client.core.designsystem.component.SectionCard
import com.mingyuan.android_client.core.designsystem.component.SkeletonBox
@@ -53,14 +55,20 @@ const val PROFILE_ROUTE = "profile"
@Composable
fun ProfileScreen(
onLoggedOut: () -> Unit,
modifier: Modifier = Modifier,
viewModel: ProfileViewModel = hiltViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
var showDeleteDialog by remember { mutableStateOf(false) }
var showLogoutDialog by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
viewModel.logoutEvents.collect { onLoggedOut() }
}
when (val state = uiState) {
is ProfileUiState.Loading -> ProfileSkeleton(modifier)
is ProfileUiState.LoggedOut -> LoggedOutState(onLogin = viewModel::login, modifier = modifier)
is ProfileUiState.Error -> ErrorState(
message = state.message,
onRetry = {},
@@ -69,28 +77,28 @@ fun ProfileScreen(
is ProfileUiState.Success -> ProfileContent(
profile = state.profile,
onDelete = { showDeleteDialog = true },
onLogout = { showLogoutDialog = true },
modifier = modifier,
)
}
if (showDeleteDialog) {
if (showLogoutDialog) {
AlertDialog(
onDismissRequest = { showDeleteDialog = false },
title = { Text("注销账号") },
text = { Text("确定要注销账号吗?此操作不可恢复。") },
onDismissRequest = { showLogoutDialog = false },
title = { Text("退出登录") },
text = { Text("确定要退出登录吗?退出后需要重新登录。") },
confirmButton = {
TextButton(
onClick = {
showDeleteDialog = false
viewModel.deleteAccount()
showLogoutDialog = false
viewModel.logout()
},
) {
Text("确定", color = Danger)
}
},
dismissButton = {
TextButton(onClick = { showDeleteDialog = false }) { Text("取消") }
TextButton(onClick = { showLogoutDialog = false }) { Text("取消") }
},
)
}
@@ -99,7 +107,7 @@ fun ProfileScreen(
@Composable
private fun ProfileContent(
profile: UserProfile,
onDelete: () -> Unit,
onLogout: () -> Unit,
modifier: Modifier = Modifier,
) {
LazyColumn(
@@ -114,8 +122,8 @@ private fun ProfileContent(
}
item {
MingyuanButton(
text = "注销账号",
onClick = onDelete,
text = "退出登录",
onClick = onLogout,
variant = ButtonVariant.DANGER,
fullWidth = true,
)

View File

@@ -3,11 +3,16 @@ package com.mingyuan.android_client.feature.profile
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mingyuan.android_client.core.domain.repository.ProfileRepository
import com.mingyuan.android_client.core.domain.repository.SessionRepository
import com.mingyuan.android_client.core.model.UserProfile
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
@@ -17,27 +22,46 @@ import javax.inject.Inject
sealed interface ProfileUiState {
data object Loading : ProfileUiState
data class Success(val profile: UserProfile) : ProfileUiState
data object LoggedOut : ProfileUiState
data class Error(val message: String) : ProfileUiState
}
@HiltViewModel
class ProfileViewModel @Inject constructor(
private val profileRepository: ProfileRepository,
private val sessionRepository: SessionRepository,
) : ViewModel() {
val uiState: StateFlow<ProfileUiState> = profileRepository.observeProfile()
.map<UserProfile, ProfileUiState>(ProfileUiState::Success)
.onStart { emit(ProfileUiState.Loading) }
val uiState: StateFlow<ProfileUiState> = sessionRepository.isLoggedIn
.flatMapLatest { loggedIn ->
if (loggedIn) {
profileRepository.observeProfile()
.map<UserProfile, ProfileUiState>(ProfileUiState::Success)
.onStart { emit(ProfileUiState.Loading) }
} else {
flowOf(ProfileUiState.LoggedOut)
}
}
.catch { emit(ProfileUiState.Error(it.message ?: "加载失败,请稍后再试")) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
started = SharingStarted.Eagerly,
initialValue = ProfileUiState.Loading,
)
fun deleteAccount() {
private val _logoutEvents = MutableSharedFlow<Unit>()
val logoutEvents: SharedFlow<Unit> = _logoutEvents
fun login() {
viewModelScope.launch {
profileRepository.deleteAccount()
sessionRepository.login()
}
}
fun logout() {
viewModelScope.launch {
sessionRepository.logout()
_logoutEvents.emit(Unit)
}
}
}