commit 1796610a3fee2bae4d41e7f967d18a9233f06108
parent b38ccc3e956fe6303d661e44fb89211214fcf1cc
Author: triesap <tyson@radroots.org>
Date: Sat, 1 Aug 2026 18:29:54 +0000
accounts: add the observable account store
- own one validated mutable state inside each store instance
- expose account state through a read-only Compose handle
- dispatch actions synchronously through the reducer
- prove state changes through the public store boundary
Diffstat:
2 files changed, 47 insertions(+), 0 deletions(-)
diff --git a/app/desktop/src/main/kotlin/org/radroots/studio/accounts/state/AccountsStore.kt b/app/desktop/src/main/kotlin/org/radroots/studio/accounts/state/AccountsStore.kt
@@ -0,0 +1,21 @@
+package org.radroots.studio.accounts.state
+
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
+import org.radroots.studio.accounts.model.AccountsAction
+import org.radroots.studio.accounts.model.AccountsState
+import org.radroots.studio.accounts.model.requireValid
+
+class AccountsStore(
+ initialState: AccountsState,
+ private val reducer: AccountsReducer,
+) {
+ private val mutableState = mutableStateOf(initialState.requireValid())
+
+ val state: State<AccountsState>
+ get() = mutableState
+
+ fun dispatch(action: AccountsAction) {
+ mutableState.value = reducer.reduce(mutableState.value, action)
+ }
+}
diff --git a/app/desktop/src/test/kotlin/org/radroots/studio/accounts/state/AccountsStoreTest.kt b/app/desktop/src/test/kotlin/org/radroots/studio/accounts/state/AccountsStoreTest.kt
@@ -0,0 +1,26 @@
+package org.radroots.studio.accounts.state
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import org.radroots.studio.accounts.model.AccountId
+import org.radroots.studio.accounts.model.AccountsAction
+import org.radroots.studio.accounts.model.AccountsState
+
+class AccountsStoreTest {
+ @Test
+ fun storeExposesInitialStateAndDispatchesThroughReducer() {
+ val store = AccountsStore(
+ initialState = AccountsState(),
+ reducer = AccountsReducer { AccountId("account-1") },
+ )
+
+ assertEquals(AccountsState(), store.state.value)
+
+ store.dispatch(AccountsAction.EditAddDisplayName("Farm Account"))
+ store.dispatch(AccountsAction.EditAddServerUrl("https://farm.example.test"))
+ store.dispatch(AccountsAction.SubmitAddAccount)
+
+ assertEquals(AccountId("account-1"), store.state.value.selectedAccountId)
+ assertEquals("Farm Account", store.state.value.accounts.single().displayName)
+ }
+}