feat: redesign account and provider dialog
This commit is contained in:
+73
@@ -0,0 +1,73 @@
|
||||
package com.flagship.abox.manager.ui.shell
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.flagship.abox.manager.designsystem.ABoxTheme
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AccountProviderDialogTest {
|
||||
@get:Rule
|
||||
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun dialogExposesAccountActionsAndClose() {
|
||||
var closed = 0
|
||||
composeRule.setContent {
|
||||
ABoxTheme {
|
||||
AccountProviderDialog(
|
||||
userName = "user",
|
||||
providerName = "ABox",
|
||||
onDismissRequest = { closed++ },
|
||||
onOpenSettings = {},
|
||||
onSwitchProvider = {},
|
||||
onEndSession = {},
|
||||
onForgetCredentials = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composeRule.onNodeWithText("账户与提供方").assertIsDisplayed()
|
||||
composeRule.onNodeWithTag("account_dialog_close").performClick()
|
||||
|
||||
assertEquals(1, closed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun destructiveActionsRequireSeparateConfirmation() {
|
||||
var ended = 0
|
||||
var forgotten = 0
|
||||
composeRule.setContent {
|
||||
ABoxTheme {
|
||||
AccountProviderDialog(
|
||||
userName = "user",
|
||||
providerName = "ABox",
|
||||
onDismissRequest = {},
|
||||
onOpenSettings = {},
|
||||
onSwitchProvider = {},
|
||||
onEndSession = { ended++ },
|
||||
onForgetCredentials = { forgotten++ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composeRule.onNodeWithTag("account_dialog_end_session").performClick()
|
||||
composeRule.onNodeWithText("退出当前会话").assertIsDisplayed()
|
||||
composeRule.onNodeWithTag("account_dialog_confirm").performClick()
|
||||
assertEquals(1, ended)
|
||||
assertEquals(0, forgotten)
|
||||
|
||||
composeRule.onNodeWithTag("account_dialog_forget_credentials").performClick()
|
||||
composeRule.onNodeWithText("清除保存的凭据").assertIsDisplayed()
|
||||
composeRule.onNodeWithTag("account_dialog_confirm").performClick()
|
||||
assertEquals(1, forgotten)
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -24,12 +24,14 @@ import com.flagship.abox.manager.domain.Username
|
||||
import com.flagship.abox.manager.ui.login.LoginRoute
|
||||
import com.flagship.abox.manager.ui.login.LoginViewModel
|
||||
import com.flagship.abox.manager.ui.navigation.AuthenticatedNavigation
|
||||
import com.flagship.abox.manager.ui.preferences.SettingsRoute
|
||||
import com.flagship.abox.manager.ui.preferences.SettingsViewModel
|
||||
import com.flagship.abox.manager.ui.provider.ProviderSelectionRoute
|
||||
import com.flagship.abox.manager.ui.session.AppSessionUiState
|
||||
import com.flagship.abox.manager.ui.session.AppSessionViewModel
|
||||
import com.flagship.abox.manager.ui.session.SessionActionsViewModel
|
||||
import com.flagship.abox.manager.ui.shell.AccountProviderBottomSheet
|
||||
import com.flagship.abox.manager.ui.shell.AccountProviderDialog
|
||||
import com.flagship.abox.manager.ui.session.SessionUiAction
|
||||
|
||||
@Composable
|
||||
fun ABoxManagerApp(
|
||||
@@ -74,42 +76,58 @@ fun ABoxManagerApp(
|
||||
is AppSessionUiState.Authenticated -> AuthenticatedNavHost(
|
||||
username = state.username,
|
||||
profile = state.profile,
|
||||
actionInProgress = actionState.inProgress,
|
||||
errorMessage = actionError,
|
||||
onLogout = sessionActionsViewModel::logout,
|
||||
onSessionAction = sessionActionsViewModel::dispatch,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun AuthenticatedNavHost(
|
||||
username: Username,
|
||||
profile: ProviderProfile,
|
||||
actionInProgress: Boolean,
|
||||
errorMessage: String?,
|
||||
onLogout: () -> Unit,
|
||||
onSessionAction: (SessionUiAction) -> Unit,
|
||||
) {
|
||||
var showAccountSheet by remember { mutableStateOf(false) }
|
||||
var showAccountDialog by remember { mutableStateOf(false) }
|
||||
var showSettings by remember { mutableStateOf(false) }
|
||||
|
||||
if (showSettings) {
|
||||
BackHandler { showSettings = false }
|
||||
SettingsRoute()
|
||||
} else {
|
||||
AuthenticatedNavigation(
|
||||
username = username,
|
||||
profile = profile,
|
||||
onLogout = onLogout,
|
||||
onProviderClick = { showAccountSheet = true },
|
||||
onAvatarClick = { showAccountSheet = true },
|
||||
onProviderClick = { showAccountDialog = true },
|
||||
onAvatarClick = { showAccountDialog = true },
|
||||
)
|
||||
}
|
||||
|
||||
if (showAccountSheet) {
|
||||
AccountProviderBottomSheet(
|
||||
if (showAccountDialog) {
|
||||
AccountProviderDialog(
|
||||
userName = username.value,
|
||||
providerName = profile.displayName,
|
||||
onDismissRequest = { showAccountSheet = false },
|
||||
onLogout = onLogout,
|
||||
actionInProgress = actionInProgress,
|
||||
errorMessage = errorMessage,
|
||||
onDismissRequest = { showAccountDialog = false },
|
||||
onOpenSettings = {
|
||||
showAccountDialog = false
|
||||
showSettings = true
|
||||
},
|
||||
onSwitchProvider = {
|
||||
showAccountDialog = false
|
||||
onSessionAction(SessionUiAction.SwitchProvider)
|
||||
},
|
||||
onEndSession = {
|
||||
showAccountDialog = false
|
||||
onSessionAction(SessionUiAction.EndSession)
|
||||
},
|
||||
onForgetCredentials = {
|
||||
showAccountDialog = false
|
||||
onSessionAction(SessionUiAction.ForgetSavedCredentials)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -42,6 +42,7 @@ import com.flagship.abox.manager.infrared.panel.RemotePanelRoute
|
||||
import com.flagship.abox.manager.infrared.ui.InfraredControlRoute
|
||||
import com.flagship.abox.manager.ui.devicecontrollist.DeviceListScreen
|
||||
import com.flagship.abox.manager.ui.scene.SceneListScreen
|
||||
import com.flagship.abox.manager.ui.preferences.SettingsRoute
|
||||
import com.flagship.abox.manager.ui.scene.SceneRoutes
|
||||
import com.flagship.abox.manager.ui.scene.sceneEditorDestination
|
||||
import com.flagship.abox.manager.ui.scene.sceneExecutionDestination
|
||||
@@ -221,6 +222,7 @@ fun AuthenticatedNavigation(
|
||||
onLogout: () -> Unit,
|
||||
onProviderClick: () -> Unit,
|
||||
onAvatarClick: () -> Unit,
|
||||
onOpenSettings: () -> Unit = {},
|
||||
windowSizeClass: ABoxWindowSizeClass? = null,
|
||||
) {
|
||||
val resolvedWindowSizeClass = windowSizeClass ?: widthClass(LocalConfiguration.current.screenWidthDp)
|
||||
@@ -262,6 +264,7 @@ fun AuthenticatedNavigation(
|
||||
onSelectDevice = onSelectDevice,
|
||||
onClearDeviceSelection = onClearDeviceSelection,
|
||||
onLogout = onLogout,
|
||||
onOpenSettings = onOpenSettings,
|
||||
)
|
||||
},
|
||||
)
|
||||
@@ -279,6 +282,7 @@ private fun AuthenticatedGraphContent(
|
||||
onSelectDevice: (DeviceName, DeviceType) -> Unit,
|
||||
onClearDeviceSelection: () -> Unit,
|
||||
onLogout: () -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -294,6 +298,7 @@ private fun AuthenticatedGraphContent(
|
||||
onSelectDevice = onSelectDevice,
|
||||
onClearDeviceSelection = onClearDeviceSelection,
|
||||
onLogout = onLogout,
|
||||
onOpenSettings = onOpenSettings,
|
||||
)
|
||||
DeviceDetailPane(
|
||||
modifier = Modifier.weight(1f),
|
||||
@@ -309,6 +314,7 @@ private fun AuthenticatedGraphContent(
|
||||
onSelectDevice = onSelectDevice,
|
||||
onClearDeviceSelection = onClearDeviceSelection,
|
||||
onLogout = onLogout,
|
||||
onOpenSettings = onOpenSettings,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -322,6 +328,7 @@ private fun AuthenticatedNavHost(
|
||||
onSelectDevice: (DeviceName, DeviceType) -> Unit,
|
||||
onClearDeviceSelection: () -> Unit,
|
||||
onLogout: () -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
@@ -342,6 +349,9 @@ private fun AuthenticatedNavHost(
|
||||
composable(MainTab.ACTIVITY.route) {
|
||||
ActivityDestination()
|
||||
}
|
||||
composable(Routes.SETTINGS) {
|
||||
SettingsRoute()
|
||||
}
|
||||
deviceDetailGraph(
|
||||
onBackClick = {
|
||||
onClearDeviceSelection()
|
||||
|
||||
@@ -5,6 +5,7 @@ object Routes {
|
||||
const val PROVIDER_SELECTION = "provider_selection"
|
||||
const val LOGIN = "login"
|
||||
const val AUTHENTICATED = "authenticated"
|
||||
const val SETTINGS = "settings"
|
||||
|
||||
const val INFRARED_HUB = "infrared_hub"
|
||||
const val INFRARED_PANEL = "infrared_panel"
|
||||
|
||||
-183
@@ -1,183 +0,0 @@
|
||||
package com.flagship.abox.manager.ui.shell
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
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.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ExitToApp
|
||||
import androidx.compose.material.icons.filled.CloudDone
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.flagship.abox.manager.R
|
||||
import com.flagship.abox.manager.designsystem.ABoxButton
|
||||
import com.flagship.abox.manager.designsystem.ABoxUserAvatar
|
||||
|
||||
/**
|
||||
* Account and Provider management bottom sheet triggered by clicking the top bar user avatar.
|
||||
* Allows viewing current session info and signing out.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AccountProviderBottomSheet(
|
||||
userName: String,
|
||||
providerName: String,
|
||||
onDismissRequest: () -> Unit,
|
||||
onLogout: () -> Unit,
|
||||
actionInProgress: Boolean,
|
||||
errorMessage: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
sheetState: SheetState = rememberModalBottomSheetState(),
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
sheetState = sheetState,
|
||||
modifier = modifier,
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 24.dp)
|
||||
.padding(bottom = 32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
// Header with large avatar & user info
|
||||
ABoxUserAvatar(
|
||||
name = userName,
|
||||
onClick = {},
|
||||
size = 56.dp,
|
||||
)
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = userName.ifBlank { stringResource(R.string.account_sheet_title) },
|
||||
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.account_sheet_signed_in_as),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f),
|
||||
)
|
||||
|
||||
// Provider Info Card
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
|
||||
),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.primaryContainer),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CloudDone,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringResource(R.string.account_sheet_provider),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Text(
|
||||
text = providerName,
|
||||
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Action button
|
||||
ABoxButton(
|
||||
onClick = onLogout,
|
||||
enabled = !actionInProgress,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ExitToApp,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.size(8.dp))
|
||||
Text(stringResource(R.string.account_sheet_logout))
|
||||
}
|
||||
|
||||
errorMessage?.let { error ->
|
||||
Text(
|
||||
text = error,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
|
||||
TextButton(
|
||||
onClick = onDismissRequest,
|
||||
enabled = !actionInProgress,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.account_sheet_close),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
package com.flagship.abox.manager.ui.shell
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.CloudDone
|
||||
import androidx.compose.material.icons.outlined.Logout
|
||||
import androidx.compose.material.icons.outlined.Refresh
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.flagship.abox.manager.designsystem.ABoxButton
|
||||
import com.flagship.abox.manager.designsystem.ABoxIconButton
|
||||
import com.flagship.abox.manager.designsystem.ABoxUserAvatar
|
||||
import com.flagship.abox.manager.designsystem.icon.ABoxIconKey
|
||||
|
||||
private enum class DestructiveAction {
|
||||
END_SESSION,
|
||||
FORGET_CREDENTIALS,
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AccountProviderDialog(
|
||||
userName: String,
|
||||
providerName: String,
|
||||
onDismissRequest: () -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
onSwitchProvider: () -> Unit,
|
||||
onEndSession: () -> Unit,
|
||||
onForgetCredentials: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var confirmation by remember { mutableStateOf<DestructiveAction?>(null) }
|
||||
|
||||
if (confirmation == null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
modifier = modifier.testTag("account_dialog"),
|
||||
title = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text("账户与提供方")
|
||||
ABoxIconButton(
|
||||
key = ABoxIconKey.CLOSE,
|
||||
contentDescription = "关闭账户对话框",
|
||||
onClick = onDismissRequest,
|
||||
modifier = Modifier.testTag("account_dialog_close"),
|
||||
)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
ABoxUserAvatar(name = userName, onClick = {}, size = 64.dp)
|
||||
Text(
|
||||
text = userName.ifBlank { "当前账户" },
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = providerName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.55f),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.CloudDone,
|
||||
contentDescription = "连接方式",
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text("当前连接方式", style = MaterialTheme.typography.labelMedium)
|
||||
Text(providerName, style = MaterialTheme.typography.bodyLarge)
|
||||
}
|
||||
TextButton(
|
||||
onClick = onSwitchProvider,
|
||||
modifier = Modifier.testTag("account_dialog_switch_provider"),
|
||||
) {
|
||||
Icon(Icons.Outlined.Refresh, "切换提供方")
|
||||
Text("切换")
|
||||
}
|
||||
}
|
||||
}
|
||||
AccountActionButton(
|
||||
icon = Icons.Outlined.Settings,
|
||||
label = "界面设置",
|
||||
description = "调整主题、渲染器和交互偏好",
|
||||
tag = "account_dialog_open_settings",
|
||||
onClick = onOpenSettings,
|
||||
)
|
||||
AccountActionButton(
|
||||
icon = Icons.Outlined.Logout,
|
||||
label = "退出当前会话",
|
||||
description = "结束当前提供方会话",
|
||||
tag = "account_dialog_end_session",
|
||||
onClick = { confirmation = DestructiveAction.END_SESSION },
|
||||
)
|
||||
TextButton(
|
||||
onClick = { confirmation = DestructiveAction.FORGET_CREDENTIALS },
|
||||
modifier = Modifier.fillMaxWidth().testTag("account_dialog_forget_credentials"),
|
||||
) {
|
||||
Text("清除保存的凭据", color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {},
|
||||
)
|
||||
} else {
|
||||
val action = confirmation ?: return
|
||||
val isForget = action == DestructiveAction.FORGET_CREDENTIALS
|
||||
AlertDialog(
|
||||
onDismissRequest = { confirmation = null },
|
||||
title = { Text(if (isForget) "清除保存的凭据" else "退出当前会话") },
|
||||
text = {
|
||||
Text(
|
||||
if (isForget) {
|
||||
"确定清除当前提供方保存的登录凭据吗?"
|
||||
} else {
|
||||
"确定退出当前会话吗?"
|
||||
},
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
ABoxButton(
|
||||
onClick = {
|
||||
confirmation = null
|
||||
if (isForget) onForgetCredentials() else onEndSession()
|
||||
},
|
||||
modifier = Modifier.testTag("account_dialog_confirm"),
|
||||
) {
|
||||
Text("确定")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { confirmation = null }) { Text("取消") }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountActionButton(
|
||||
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
label: String,
|
||||
description: String,
|
||||
tag: String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onClick,
|
||||
modifier = Modifier.fillMaxWidth().testTag(tag),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(icon, contentDescription = label)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(label)
|
||||
Text(description, style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user