app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

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:
Mapp/desktop/src/main/kotlin/org/radroots/studio/accounts/ui/AccountsScreen.kt | 21+++++++++++++++++++++
Mapp/desktop/src/test/kotlin/org/radroots/studio/accounts/ui/AccountsScreenTest.kt | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
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() + } }