commit 9879566cf26cc936e5c470826e8ca2d88a9e5e32
parent 304ab41943581ccc97cb9b629482934206ca9612
Author: triesap <tyson@radroots.org>
Date: Sat, 1 Aug 2026 18:36:21 +0000
ui: add explicit account status controls
- render login or logout from each account state
- dispatch status actions with the owning account ID
- update status details through observable store state
- cover component callbacks and integrated status changes
Diffstat:
2 files changed, 73 insertions(+), 0 deletions(-)
diff --git a/app/desktop/src/main/kotlin/org/radroots/studio/accounts/ui/AccountsScreen.kt b/app/desktop/src/main/kotlin/org/radroots/studio/accounts/ui/AccountsScreen.kt
@@ -23,6 +23,7 @@ import org.radroots.studio.accounts.model.Account
import org.radroots.studio.accounts.model.AccountsAction
import org.radroots.studio.accounts.model.AccountsState
import org.radroots.studio.accounts.model.AccountsProblem
+import org.radroots.studio.accounts.model.LoginStatus
@Composable
fun AccountsScreen(
@@ -73,6 +74,12 @@ fun AccountsScreen(
onSelect = {
onAction(AccountsAction.SelectAccount(account.id))
},
+ onLogIn = {
+ onAction(AccountsAction.LogIn(account.id))
+ },
+ onLogOut = {
+ onAction(AccountsAction.LogOut(account.id))
+ },
)
}
}
@@ -167,6 +174,8 @@ private fun AccountRow(
account: Account,
isSelected: Boolean,
onSelect: () -> Unit,
+ onLogIn: () -> Unit,
+ onLogOut: () -> Unit,
) {
Column(
modifier = Modifier
@@ -184,6 +193,18 @@ private fun AccountRow(
modifier = Modifier.testTag("account-selected:${account.id.value}"),
)
}
+ when (account.loginStatus) {
+ LoginStatus.LoggedOut -> TextAction(
+ text = "Login",
+ testTag = "account-login:${account.id.value}",
+ onClick = onLogIn,
+ )
+ LoginStatus.LoggedIn -> TextAction(
+ text = "Logout",
+ testTag = "account-logout:${account.id.value}",
+ onClick = onLogOut,
+ )
+ }
}
}
diff --git a/app/desktop/src/test/kotlin/org/radroots/studio/accounts/ui/AccountsScreenTest.kt b/app/desktop/src/test/kotlin/org/radroots/studio/accounts/ui/AccountsScreenTest.kt
@@ -20,6 +20,7 @@ import org.radroots.studio.accounts.model.AccountsProblem
import org.radroots.studio.accounts.model.AccountsState
import org.radroots.studio.accounts.state.AccountsReducer
import org.radroots.studio.accounts.state.AccountsStore
+import org.radroots.studio.accounts.model.LoginStatus
import org.radroots.studio.accounts.testAccount
import org.radroots.studio.accounts.testAccountsState
@@ -134,4 +135,55 @@ class AccountsScreenTest {
onNodeWithTag("account-row:account-1").assertIsDisplayed()
onNodeWithTag("account-row:account-1").assertIsSelected()
}
+
+ @Test
+ fun statusControlsEmitExplicitAccountActions() = runComposeUiTest {
+ val loggedOut = testAccount()
+ val loggedIn = testAccount(
+ id = "account-2",
+ displayName = "Second Account",
+ loginStatus = LoginStatus.LoggedIn,
+ )
+ val actions = mutableListOf<AccountsAction>()
+ setContent {
+ AccountsScreen(
+ state = testAccountsState(loggedOut, loggedIn),
+ onAction = actions::add,
+ )
+ }
+
+ onNodeWithTag("account-login:${loggedOut.id.value}", useUnmergedTree = true)
+ .performClick()
+ onNodeWithTag("account-logout:${loggedIn.id.value}", useUnmergedTree = true)
+ .performClick()
+
+ assertEquals(
+ listOf(
+ AccountsAction.LogIn(loggedOut.id),
+ AccountsAction.LogOut(loggedIn.id),
+ ),
+ actions,
+ )
+ }
+
+ @Test
+ fun integratedStatusControlUpdatesAvailableAction() = runComposeUiTest {
+ val account = testAccount()
+ val store = AccountsStore(
+ initialState = testAccountsState(account),
+ reducer = AccountsReducer { AccountId("unused") },
+ )
+ setContent {
+ AccountsScreen(
+ state = store.state.value,
+ onAction = store::dispatch,
+ )
+ }
+
+ onNodeWithTag("account-login:${account.id.value}", useUnmergedTree = true)
+ .performClick()
+ onNodeWithTag("account-logout:${account.id.value}", useUnmergedTree = true)
+ .assertIsDisplayed()
+ onNodeWithText("Status: LoggedIn").assertIsDisplayed()
+ }
}