### Initialize Store with Events in Kotlin Source: https://github.com/yumemi-inc/tart/blob/main/README.md Initializes a Tart Store with a specific state, action, and event type. The example shows the basic structure for creating a store, with event handling logic to be defined within action blocks. ```kotlin val store: Store = Store(CounterState(count = 0)) { // ... } ``` -------------------------------- ### Define CounterStore Source: https://github.com/yumemi-inc/tart/blob/main/README.md Example of defining a CounterStore, which is a fundamental building block for managing state and actions in the application. This snippet shows the basic structure of a Store definition. ```kotlin fun CounterStore( coroutineContext: CoroutineContext, counterRepository: CounterRepository, ): Store = Store { // ... coroutineContext(coroutineContext) } ``` -------------------------------- ### Interact with the Store Interface for State Management in Kotlin Source: https://context7.com/yumemi-inc/tart/llms.txt Demonstrates how to use the `Store` interface in Tart to manage application state. It shows how to observe state changes using `StateFlow`, dispatch actions to modify state, and collect one-time events for UI feedback. Includes examples for platforms with and without direct Flow support. ```kotlin import io.yumemi.tart.core.Store import kotlinx.coroutines.flow.collect // Store interface provides: // - state: StateFlow - observable state stream // - event: Flow - event stream for one-time notifications // - currentState: S - current state value // - dispatch(action: A) - trigger state changes // - collectState/collectEvent - callbacks for non-Flow platforms // - dispose() - release resources val store: Store = createCounterStore() // Access current state val currentCount = store.currentState.count // Observe state changes via StateFlow store.state.collect { state -> println("Count changed to: ${state.count}") } // Dispatch actions to trigger state changes store.dispatch(CounterAction.Increment) // Collect events for one-time notifications store.event.collect { when (it) { is CounterEvent.ShowToast -> showToast(it.message) is CounterEvent.NavigateBack -> navigateBack() } } // For platforms without Flow support (e.g., iOS) store.collectState { state -> updateUI(state) } store.collectEvent { handleEvent(it) } // Cleanup when done store.dispose() ``` -------------------------------- ### Define CounterStore with StateSaver Source: https://github.com/yumemi-inc/tart/blob/main/README.md Example of defining a CounterStore that includes a StateSaver. This is important for persisting state across configuration changes, such as screen rotations. ```kotlin fun CounterStore( coroutineContext: CoroutineContext, stateSaver: StateSaver, counterRepository: CounterRepository, ): Store = Store { // ... coroutineContext(coroutineContext) stateSaver(stateSaver) } ``` -------------------------------- ### Implement State Persistence with StateSaver for Tart Stores (Kotlin) Source: https://context7.com/yumemi-inc/tart/llms.txt Explains how to use the `StateSaver` interface for automatic state persistence and restoration in Tart stores. It provides examples of creating `StateSaver` instances using a factory function for SharedPreferences and implementing the interface directly. ```kotlin import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.StateSaver data class SettingsState( val theme: String = "light", val notifications: Boolean = true ) : State sealed interface SettingsAction : Action { data class SetTheme(val theme: String) : SettingsAction data class SetNotifications(val enabled: Boolean) : SettingsAction } // Create StateSaver with factory function fun createStateSaver(preferences: SharedPreferences): StateSaver = StateSaver( save = { state -> preferences.edit() .putString("theme", state.theme) .putBoolean("notifications", state.notifications) .apply() }, restore = { val theme = preferences.getString("theme", "light") ?: "light" val notifications = preferences.getBoolean("notifications", true) SettingsState(theme, notifications) } ) // Alternative: implement StateSaver interface directly class SettingsStateSaver(private val prefs: SharedPreferences) : StateSaver { override fun save(state: SettingsState) { prefs.edit() .putString("theme", state.theme) .putBoolean("notifications", state.notifications) .apply() } override fun restore(): SettingsState? { if (!prefs.contains("theme")) return null return SettingsState( theme = prefs.getString("theme", "light") ?: "light", notifications = prefs.getBoolean("notifications", true) ) } } // Use StateSaver in Store fun SettingsStore( stateSaver: StateSaver ): Store = Store(SettingsState()) { stateSaver(stateSaver) state { action { nextState(state.copy(theme = action.theme)) } action { nextState(state.copy(notifications = action.enabled)) } } } ``` -------------------------------- ### Send and Receive Messages Between Stores using Kotlin Source: https://context7.com/yumemi-inc/tart/llms.txt This example demonstrates how to use the `tart-message` module in Kotlin to send messages from one store (`authStore`) and receive them in another (`profileStore`). It includes defining shared message types and handling specific messages by dispatching actions. Ensure the `tart-message` dependency is added to your project. ```kotlin import io.yumemi.tart.message.Message import io.yumemi.tart.message.message import io.yumemi.tart.message.receiveMessages import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event // Add dependency: implementation("io.yumemi.tart:tart-message:") // Define shared message types sealed interface AppMessage : Message { data object UserLoggedOut : AppMessage data class ThemeChanged(val theme: String) : AppMessage data class UserDataUpdated(val userId: String) : AppMessage } // Auth Store - sends messages data class AuthState(val isLoggedIn: Boolean = false) : State sealed interface AuthAction : Action { data object Logout : AuthAction } val authStore: Store = Store(AuthState(isLoggedIn = true)) { state { action { nextState(state.copy(isLoggedIn = false)) } exit { // Send message when leaving logged-in state if (state.isLoggedIn) { message(AppMessage.UserLoggedOut) } } } } // Profile Store - receives messages data class ProfileState(val username: String = "", val needsRefresh: Boolean = false) : State sealed interface ProfileAction : Action { data object ClearData : ProfileAction data object Refresh : ProfileAction } val profileStore: Store = Store(ProfileState()) { // Receive and handle messages from other stores middleware( receiveMessages { message -> when (message) { AppMessage.UserLoggedOut -> { // Dispatch action in response to message dispatch(ProfileAction.ClearData) } is AppMessage.UserDataUpdated -> { dispatch(ProfileAction.Refresh) } else -> { /* ignore other messages */ } } } ) state { action { nextState(ProfileState()) // Reset to default } action { nextState(state.copy(needsRefresh = true)) } } } ``` -------------------------------- ### Manage Multiple Explicit States and Transitions in Kotlin Source: https://context7.com/yumemi-inc/tart/llms.txt Illustrates managing multiple explicit states using Kotlin sealed classes within the Tart framework. It defines different states for UI conditions like Idle, Loading, LoggedIn, and Error. The example shows how to handle each state independently using `state{}` blocks and manage transitions with `nextState()`, including `enter` and `exit` blocks for state lifecycle events. ```kotlin import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event // Define multiple states using sealed interface sealed interface LoginState : State { data object Idle : LoginState data object Loading : LoginState data class LoggedIn(val username: String, val token: String) : LoginState data class Error(val message: String) : LoginState } sealed interface LoginAction : Action { data class Login(val username: String, val password: String) : LoginAction data object Retry : LoginAction data object Logout : LoginAction } sealed interface LoginEvent : Event { data object NavigateToHome : LoginEvent data object NavigateToLogin : LoginEvent } fun LoginStore(authRepository: AuthRepository): Store = Store(LoginState.Idle) { // Handler for Idle state state { action { nextState(LoginState.Loading) } } // Handler for Loading state with enter block state { enter { // Runs automatically when entering this state try { val result = authRepository.authenticate() nextState(LoginState.LoggedIn(result.username, result.token)) } catch (e: Exception) { nextState(LoginState.Error(e.message ?: "Unknown error")) } } } // Handler for LoggedIn state state { enter { event(LoginEvent.NavigateToHome) } action { nextState(LoginState.Idle) } exit { // Cleanup when leaving this state event(LoginEvent.NavigateToLogin) } } // Handler for Error state state { action { nextState(LoginState.Loading) } } } ``` -------------------------------- ### Create Preview with ViewStore and State Source: https://github.com/yumemi-inc/tart/blob/main/README.md Illustrates how to create a ViewStore instance with a specific state (e.g., CounterState.Loading) for UI previews and testing. This approach allows for isolated UI development by providing only the necessary state. ```kotlin @Preview @Composable fun LoadingPreview() { MyApplicationTheme { CounterScreen( viewStore = ViewStore( state = CounterState.Loading, ), ) } } ``` -------------------------------- ### Build Store Instances with DSL in Kotlin Source: https://context7.com/yumemi-inc/tart/llms.txt Demonstrates creating a `Store` instance using the `Store()` DSL function in Kotlin. This involves defining initial state, state handlers using `state{}` blocks, and action handlers using `action{}` blocks. It shows how to transition states using `nextState()` and `nextStateBy{}`, and emit events. ```kotlin import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event data class CounterState(val count: Int) : State sealed interface CounterAction : Action { data object Increment : CounterAction data object Decrement : CounterAction } sealed interface CounterEvent : Event { data class ShowError(val message: String) : CounterEvent } // Create store with initial state and configuration val store: Store = Store(CounterState(count = 0)) { state { action { // Access current state via 'state' property nextState(state.copy(count = state.count + 1)) } action { if (state.count > 0) { nextState(state.copy(count = state.count - 1)) } else { // Emit event when action cannot be performed event(CounterEvent.ShowError("Cannot decrement below zero")) // State remains unchanged if nextState() not called } } } } // Alternative: use initialState() inside the block val store2: Store = Store { initialState(CounterState(count = 0)) state { action { // Use nextStateBy for computed state nextStateBy { val newCount = state.count + 1 state.copy(count = newCount) } } } } ``` -------------------------------- ### Initialize Tart Store with Initial State Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates two ways to initialize a Tart Store with an initial state. The first uses a direct constructor call, while the second utilizes the `initialState()` method within the Store DSL for more explicit configuration. ```kotlin val store: Store = Store(CounterState(count = 0)) {} // or, use the initialState() specification val store: Store = Store { initialState(CounterState(count = 0)) } ``` -------------------------------- ### Mock ViewStore for Compose Previews and Tests (Kotlin) Source: https://context7.com/yumemi-inc/tart/llms.txt Demonstrates creating ViewStore instances with mock state for Compose previews and UI tests. This allows for rapid UI development and isolated component testing without a full Store implementation. It requires the tart-compose and tart-core libraries. ```kotlin import io.yumemi.tart.compose.ViewStore import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview sealed interface ProfileState : State { data object Loading : ProfileState data class Loaded(val name: String, val email: String) : ProfileState data class Error(val message: String) : ProfileState } sealed interface ProfileAction : Action { data object Refresh : ProfileAction } sealed interface ProfileEvent : Event @Composable fun ProfileScreen( viewStore: ViewStore ) { viewStore.render { LoadingSpinner() } viewStore.render { ProfileContent(name = state.name, email = state.email) } viewStore.render { ErrorMessage(message = state.message) } } // Preview with Loading state @Preview @Composable fun ProfileLoadingPreview() { ProfileScreen( viewStore = ViewStore(state = ProfileState.Loading) ) } // Preview with Loaded state @Preview @Composable fun ProfileLoadedPreview() { ProfileScreen( viewStore = ViewStore( state = ProfileState.Loaded( name = "John Doe", email = "john@example.com" ) ) ) } // Preview with Error state @Preview @Composable fun ProfileErrorPreview() { ProfileScreen( viewStore = ViewStore( state = ProfileState.Error(message = "Network error") ) ) } // Mock for UI testing @Test fun testProfileLoadedState() { composeTestRule.setContent { ProfileScreen( viewStore = ViewStore( state = ProfileState.Loaded("Test User", "test@example.com"), dispatch = { action -> // Verify actions dispatched assertEquals(ProfileAction.Refresh, action) } ) ) } composeTestRule.onNodeWithText("Test User").assertIsDisplayed() } ``` -------------------------------- ### Apply Custom Middleware to Store Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to apply a custom middleware instance, like YourMiddleware, to a Store. It also shows how to apply middleware directly using an anonymous object. ```kotlin val store: Store = Store { // ... // add Middleware instance middleware(YourMiddleware()) // or, implement Middleware directly here middleware( object : Middleware { override suspend fun afterStateChange(state: CounterState, prevState: CounterState) { // do something.. } }, ) // add multiple Middlewares middlewares(..., ...) } ``` -------------------------------- ### Transition Between States in Store (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates state transitions within a Tart Store using multiple `state` blocks. The `CounterAction.Load` action transitions from the `Loading` state to the `Main` state, passing the loaded count. ```kotlin fun CounterStore( counterRepository: CounterRepository, ): Store = Store(CounterState.Loading) { state { // for Loading state action { val count = counterRepository.get() nextState(CounterState.Main(count = count)) // transition to Main state } } state { // for Main state action { // ... ``` -------------------------------- ### Use Enter Block for State Initialization (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Shows how to use the `enter` block within a `state` definition in a Tart Store. The `enter` block executes logic when the state becomes active, such as performing an initial data load and transitioning to another state. ```kotlin fun CounterStore( counterRepository: CounterRepository, ): Store = Store(CounterState.Loading) { state { enter { val count = counterRepository.get() nextState(CounterState.Main(count = count)) // transition to Main state } } state { action { // ... ``` -------------------------------- ### Handle Specific Events with ViewStore Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to use ViewStore's .handle() method to react to specific event types like CounterEvent.ShowToast. This allows for targeted event processing within the application. ```kotlin viewStore.handle { event -> // do something.. } ``` -------------------------------- ### Build Store with StoreBuilder Class Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates creating a reusable Store builder class (`CounterStoreContainer`) to encapsulate the logic for building a Store. This promotes code organization and reusability, especially when dependencies like repositories are involved. ```kotlin class CounterStoreContainer( private val counterRepository: CounterRepository, ) { fun build(coroutineContext: CoroutineContext, stateSaver: StateSaver): Store = Store { // ... coroutineContext(coroutineContext) stateSaver(stateSaver) } } ``` -------------------------------- ### Access Repository in Store Actions (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Shows how to inject and use a `CounterRepository` within a Tart Store's `action` blocks. This allows actions to perform data loading and saving operations by interacting with the repository. ```kotlin fun CounterStore( counterRepository: CounterRepository, ): Store = Store(CounterState(count = 0)) { state { action { val count = counterRepository.get() // load nextState(state.copy(count = count)) } action { val count = state.count + 1 counterRepository.set(count) // save nextState(state.copy(count = count)) } // ... ``` -------------------------------- ### Apply Simple Logging Middleware Source: https://github.com/yumemi-inc/tart/blob/main/README.md Shows how to apply the simpleLogging() middleware factory function to a Store to automatically log actions, events, state changes, and errors. This is useful for debugging and monitoring Store operations. ```kotlin val store: Store = Store { // ... middleware(simpleLogging()) } ``` -------------------------------- ### Create ViewStore with rememberViewStore (ViewModel) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to create a ViewStore instance within a Composable function using `rememberViewStore` and a Hilt ViewModel. This approach is recommended for managing store instances within the Compose lifecycle. ```kotlin @HiltViewModel class CounterViewModel @Inject constructor( counterRepository: CounterRepository, ) : ViewModel() { val store = CounterStore( coroutineContext = viewModelScope.coroutineContext, counterRepository = counterRepository, ) } @Composable fun CounterScreen( // create an instance of ViewStore viewStore: ViewStore = rememberViewStore { hiltViewModel().store }, ) { // ... // pass the ViewStore instance to lower components if necessary YourComposable( viewStore = viewStore, ) } ``` -------------------------------- ### Create Custom Logging Middleware Source: https://github.com/yumemi-inc/tart/blob/main/README.md Illustrates how to create a custom logging middleware by extending the LoggingMiddleware class. This allows for more granular control over logging behavior, such as overriding specific methods like beforeStateEnter. ```kotlin middleware( object : LoggingMiddleware( logger = YourLogger() // specify your logger ) { // override methods override suspend fun beforeStateEnter(state: CounterState) { log(...) } }, ) ``` -------------------------------- ### Dispatch Action with ViewStore Source: https://github.com/yumemi-inc/tart/blob/main/README.md Shows how to trigger state changes by dispatching actions using the ViewStore's `.dispatch()` method. This is the primary way to interact with the store from the UI. ```kotlin Button( onClick = { viewStore.dispatch(CounterAction.Increment) }, ) { Text( text = "increment" ) } ``` -------------------------------- ### Dispatch Actions and Render State in UI Source: https://github.com/yumemi-inc/tart/blob/main/README.md Illustrates how to dispatch actions to the Tart Store from a UI component, such as a Compose button, and how to render the updated state exposed by the Store's `.state` (StateFlow) in the UI. ```kotlin // example in Compose Button( onClick = { store.dispatch(CounterAction.Increment) }, ) { Text(text = "increment") } ``` -------------------------------- ### Simple Logging with Tart Source: https://context7.com/yumemi-inc/tart/llms.txt Demonstrates the basic usage of the simpleLogging() middleware in Tart. It logs actions and state changes with default settings. The output format is 'Action: [Action], State: [NewState] <- [OldState]'. ```kotlin import io.yumemi.tart.logging.simpleLogging import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event data class AppState(val count: Int = 0) : State sealed interface AppAction : Action { data object Increment : AppAction } sealed interface AppEvent : Event // Simple logging with default settings val store1: Store = Store(AppState()) { middleware(simpleLogging()) // Logs: Action: Increment, State: AppState(count=1) <- AppState(count=0) state { action { nextState(state.copy(count = state.count + 1)) } } } ``` -------------------------------- ### Receive Messages with Middleware Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to use the receiveMessages() middleware factory function to set up a Store to receive messages. It shows how to handle different message types within the provided lambda. ```kotlin val myPageStore: Store = Store { // ... middleware( receiveMessages { message -> when (message) { MainMessage.LoggedOut -> dispatch(MyPageAction.doLogoutProcess) // ... } } ) } ``` -------------------------------- ### Create ViewStore with rememberViewStore (Standalone) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Shows how to create a ViewStore instance directly within a Composable function without relying on a ViewModel. This is useful for simpler scenarios or when ViewModel injection is not available. It also demonstrates state persistence using `rememberStateSaver`. ```kotlin @Composable fun CounterScreen( viewStore: ViewStore = rememberViewStore { CounterStore( coroutineContext = rememberCoroutineScope().coroutineContext, // or, specify the autoDispose option in rememberViewStore {} stateSaver = rememberStateSaver(), // state persistence during screen rotation, etc. counterRepository = CounterRepositoryImpl(), ) }, ) { // ... } ``` -------------------------------- ### Render Specific State with ViewStore.render() Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to conditionally render UI based on specific states using the `.render()` method. This is useful for handling different states like Loading or Main content. ```kotlin viewStore.render { Text( text = "loading..", ) } viewStore.render { Text( text = state.count.toString(), ) } ``` -------------------------------- ### Define Store using Store DSL in ViewModel Source: https://github.com/yumemi-inc/tart/blob/main/README.md An alternative approach to defining a Store directly within a ViewModel using the `Store{}` DSL. While concise, it may make sharing Stores across platforms more challenging and requires thorough testing. ```kotlin @HiltViewModel class MainViewModel @Inject constructor( counterRepository: CounterRepository, ) : ViewModel() { val store: Store = Store { // ... coroutineContext(viewModelScope.coroutineContext) } } ``` -------------------------------- ### Compose UI with ViewStore for State Management Source: https://context7.com/yumemi-inc/tart/llms.txt This Kotlin code demonstrates how to use ViewStore within a Jetpack Compose screen to manage application state and handle user interactions. It utilizes `rememberViewStore` for creating the ViewStore, `render` for displaying UI based on different states (Loading, Ready), and `handle` for processing events like showing toast notifications. Dependencies include tart-compose, androidx.compose.runtime, and androidx.compose.material. ```kotlin import io.yumemi.tart.compose.ViewStore import io.yumemi.tart.compose.rememberViewStore import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event import androidx.compose.runtime.Composable import androidx.compose.material.Text import androidx.compose.material.Button import androidx.compose.material.CircularProgressIndicator import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.runtime.rememberCoroutineScope import androidx.hilt.navigation.compose.hiltViewModel // Add dependency: implementation("io.yumemi.tart:tart-compose:") sealed interface CounterState : State { data object Loading : CounterState data class Ready(val count: Int) : CounterState } sealed interface CounterAction : Action { data object Increment : CounterAction data object Decrement : CounterAction } sealed interface CounterEvent : Event { data class ShowToast(val message: String) : CounterEvent } @Composable fun CounterScreen( viewModel: CounterViewModel = hiltViewModel() ) { // Create ViewStore from Store in ViewModel val viewStore: ViewStore = rememberViewStore { viewModel.store } // Handle events viewStore.handle { // Show toast notification showToast(event.message) } // Render based on state type viewStore.render { CircularProgressIndicator() } viewStore.render { // 'state' is typed as CounterState.Ready here CounterContent( count = state.count, onIncrement = { viewStore.dispatch(CounterAction.Increment) }, onDecrement = { viewStore.dispatch(CounterAction.Decrement) } ) } } @Composable fun CounterContent( count: Int, onIncrement: () -> Unit, onDecrement: () -> Unit ) { Column { Text(text = "Count: $count") Row { Button(onClick = onDecrement) { Text("-") } Button(onClick = onIncrement) { Text("+") } } } } // With autoDispose for automatic cleanup @Composable fun StandaloneCounterScreen() { val viewStore = rememberViewStore(autoDispose = true) { CounterStore( coroutineContext = rememberCoroutineScope().coroutineContext ) } // ViewStore automatically disposed when composable leaves composition } // Dummy implementations for compilation class CounterViewModel { val store: Store = TODO() } class CounterStore(private val coroutineContext: kotlin.coroutines.CoroutineContext) : Store { override suspend fun process(action: CounterAction, state: CounterState): State = TODO() override suspend fun process(event: CounterEvent, state: CounterState): State = TODO() } fun showToast(message: String) { println("Showing toast: $message") } ``` -------------------------------- ### Handle Parent Event Types with ViewStore Source: https://github.com/yumemi-inc/tart/blob/main/README.md Shows how to subscribe to a parent event type (e.g., CounterEvent) using ViewStore's .handle() method. This enables handling multiple related events within a single block using a when statement. ```kotlin viewStore.handle { event -> when (event) { is CounterEvent.ShowToast -> // do something.. is CounterEvent.GoBack -> // do something.. // ... } } ``` -------------------------------- ### State Lifecycle Handlers: Enter, Exit, and Error in Kotlin Source: https://context7.com/yumemi-inc/tart/llms.txt Demonstrates the use of enter, exit, and error handlers for managing state transitions and errors in a Kotlin application using the Tart library. The enter block executes upon state entry, exit upon leaving, and error catches exceptions, allowing for graceful error handling and recovery. Dependencies include Tart core, Kotlin coroutines, and custom state, action, and event interfaces. ```kotlin import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event import kotlinx.coroutines.Dispatchers sealed interface DataState : State { data object Loading : DataState data class Ready(val items: List) : DataState data class Error(val error: Throwable) : DataState } sealed interface DataAction : Action { data object Refresh : DataAction } sealed interface DataEvent : Event { data class ShowError(val message: String) : DataEvent } fun DataStore(repository: DataRepository): Store = Store(DataState.Loading) { state { // enter block runs when state is entered // Optional dispatcher parameter for thread control enter(Dispatchers.IO) { val items = repository.fetchItems() nextState(DataState.Ready(items)) } // Catch specific exceptions first error { event(DataEvent.ShowError("Invalid state: ${error.message}")) nextState(DataState.Error(error)) } // Catch general exceptions last error { event(DataEvent.ShowError("Failed to load: ${error.message}")) nextState(DataState.Error(error)) } } state { action { nextState(DataState.Loading) } exit { // Cleanup when leaving Ready state repository.cancelPendingRequests() } } state { action { nextState(DataState.Loading) } } } ``` -------------------------------- ### Configure State Transitions in Tart Store Source: https://github.com/yumemi-inc/tart/blob/main/README.md Configures how the Tart Store handles actions to transition between states. It uses `state<>()` and `action<>()` blocks to define logic, specifying the next state using `nextState()` or `nextStateBy{}` for computed state updates. ```kotlin val store: Store = Store(CounterState(count = 0)) { state { action { nextState(state.copy(count = state.count + 1)) } action { if (0 < state.count) { nextState(state.copy(count = state.count - 1)) } else { // do not change State } } } } ``` -------------------------------- ### Implementing Middleware for Store Lifecycle Events in Kotlin Source: https://context7.com/yumemi-inc/tart/llms.txt Shows how to use middleware to intercept Store lifecycle events for cross-cutting concerns like logging, analytics, or debugging. Middleware can be implemented by creating a class that extends the `Middleware` interface or by using the `Middleware()` factory function for inline creation. ```kotlin import io.yumemi.tart.core.Middleware import io.yumemi.tart.core.MiddlewareScope import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event import io.yumemi.tart.core.Store // Custom middleware by implementing interface class AnalyticsMiddleware( private val analytics: AnalyticsService ) : Middleware { override suspend fun onStart(middlewareScope: MiddlewareScope, state: S) { analytics.trackEvent("store_started") } override suspend fun beforeActionDispatch(state: S, action: A) { analytics.trackAction(action.toString()) } override suspend fun afterStateChange(state: S, prevState: S) { analytics.trackStateChange(prevState.toString(), state.toString()) } override suspend fun beforeError(state: S, error: Throwable) { analytics.trackError(error) } } // Quick middleware creation with factory function val debugMiddleware = Middleware( beforeActionDispatch = { state, action -> println("Action dispatched: $action in state: $state") }, afterStateChange = { state, prevState -> println("State changed: $prevState -> $state") }, beforeEventEmit = { state, event -> println("Event emitted: $event") } ) // Apply middleware to Store val store: Store = Store(AppState()) { // Add single middleware middleware(AnalyticsMiddleware(analyticsService)) // Add multiple middlewares middlewares(debugMiddleware, loggingMiddleware, crashMiddleware) state { // ... state handlers } } ``` -------------------------------- ### State Persistence with StateSaver in Tart (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to enable automatic state persistence for a Tart Store by providing a 'StateSaver' implementation. This allows the store's state to be saved and restored across application restarts or configuration changes. ```kotlin val store: Store = Store { // ... stateSaver(...) } ``` -------------------------------- ### Define Multiple States for a Store (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Defines a sealed interface `CounterState` with multiple distinct states: `Loading` and `Main`. The `Main` state includes a count property, enabling the store to represent different UI or data loading phases. ```kotlin sealed interface CounterState : State { data object Loading : CounterState data class Main(val count: Int) : CounterState } ``` -------------------------------- ### Render Single State with ViewStore Source: https://github.com/yumemi-inc/tart/blob/main/README.md Illustrates how to display a single state property from the ViewStore using its `.state` property. This is suitable when your state is a simple data class. ```kotlin Text( text = viewStore.state.count.toString(), ) ``` -------------------------------- ### Customized Simple Logging with Tart Source: https://context7.com/yumemi-inc/tart/llms.txt Shows how to customize the simpleLogging() middleware by providing a specific tag and log severity level. This allows for more granular control over log output. ```kotlin import io.yumemi.tart.logging.simpleLogging import io.yumemi.tart.logging.Logger import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event data class AppState(val count: Int = 0) : State sealed interface AppAction : Action { data object Increment : AppAction } sealed interface AppEvent : Event // Customized simple logging val store2: Store = Store(AppState()) { middleware(simpleLogging( tag = "MyApp", severity = Logger.Severity.Info )) state { /* ... */ } } ``` -------------------------------- ### Implement Custom Middleware Interface Source: https://github.com/yumemi-inc/tart/blob/main/README.md Provides a template for creating custom middleware by implementing the Middleware interface. It shows how to override the afterStateChange method to perform actions after a state change. ```kotlin class YourMiddleware : Middleware { override suspend fun afterStateChange(state: S, prevState: S) { // do something.. } } ``` -------------------------------- ### Reactive Flow Collection with launch{} in Kotlin Source: https://context7.com/yumemi-inc/tart/llms.txt Illustrates how to collect external Flows reactively within enter handlers using the launch{} block in Kotlin with the Tart library. Launched coroutines are automatically cancelled when the state changes, ensuring proper resource management for subscriptions. This enables dynamic state updates based on asynchronous data streams. Dependencies include Tart core, Kotlin coroutines, and a WebSocketService interface. ```kotlin import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow data class RealtimeState( val messages: List = emptyList(), val connectionStatus: String = "disconnected" ) : State sealed interface RealtimeAction : Action { data object Connect : RealtimeAction data object Disconnect : RealtimeAction } sealed interface RealtimeEvent : Event { data class NewMessage(val message: String) : RealtimeEvent } interface WebSocketService { fun connect(): Flow fun connectionStatus(): Flow } fun RealtimeStore(webSocket: WebSocketService): Store = Store(RealtimeState()) { state { enter { // Launch coroutine for message collection // Automatically cancelled when state changes launch(Dispatchers.IO) { webSocket.connect().collect { // Use transaction{} for state updates inside launch transaction { nextState(state.copy( messages = state.messages + message )) event(RealtimeEvent.NewMessage(message)) } } } // Multiple launch blocks for different streams launch { webSocket.connectionStatus().collect { transaction { nextState(state.copy(connectionStatus = status)) } } } } } } ``` -------------------------------- ### Raise an Event in a Store Action (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to raise a `CounterEvent.ShowToast` within an `action` block in a Tart Store. This is used to trigger UI feedback, such as displaying a toast message, based on the application's state and actions. ```kotlin action { if (0 < state.count) { nextState(state.copy(count = state.count - 1)) } else { event(CounterEvent.ShowToast("Can not Decrement.")) // raise event } } ``` -------------------------------- ### Pass ViewStore to Lower Components Source: https://github.com/yumemi-inc/tart/blob/main/README.md Illustrates how to pass a ViewStore instance down to child composable functions. This allows lower-level components to access and interact with the state and actions managed by the ViewStore. ```kotlin viewStore.render { YourComposable( viewStore = this, // ViewStore instance for CounterState.Main ) } @Composable fun YourComposable( // Main state is confirmed viewStore: ViewStore, ) { Text( text = viewStore.state.count.toString() ) } ``` -------------------------------- ### Configure CoroutineContext and Dispatchers for Tart Stores (Kotlin) Source: https://context7.com/yumemi-inc/tart/llms.txt Demonstrates how to configure the CoroutineContext for Tart stores to tie their lifecycle to a ViewModel scope or specify custom dispatchers. It shows how to use `coroutineContext()` and `withContext` for managing execution. ```kotlin import io.yumemi.tart.core.Store import io.yumemi.tart.core.State import io.yumemi.tart.core.Action import io.yumemi.tart.core.Event import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlin.coroutines.CoroutineContext data class ProcessingState(val result: String = "") : State sealed interface ProcessingAction : Action { data class Process(val input: String) : ProcessingAction } // Factory function accepting coroutineContext for lifecycle management fun ProcessingStore( coroutineContext: CoroutineContext, processor: DataProcessor ): Store = Store(ProcessingState()) { // Tie store lifecycle to external scope (e.g., viewModelScope) coroutineContext(coroutineContext) state { // Specify dispatcher for enter handler enter(Dispatchers.Default) { val initial = processor.initialize() nextState(state.copy(result = initial)) } // Specify dispatcher for action handler action(Dispatchers.Default) { // Heavy computation on Default dispatcher val processed = processor.process(action.input) nextState(state.copy(result = processed)) } } } // Usage in ViewModel class MyViewModel : ViewModel() { val store = ProcessingStore( coroutineContext = viewModelScope.coroutineContext, processor = DataProcessorImpl() ) } // Alternative: using withContext inside handlers val store2: Store = Store(ProcessingState()) { state { action { val result = withContext(Dispatchers.IO) { // IO-bound work fetchDataFromNetwork() } nextState(state.copy(result = result)) } } } ``` -------------------------------- ### Compute Next State with nextStateBy Source: https://github.com/yumemi-inc/tart/blob/main/README.md Shows how to use the `nextStateBy{}` block within a Tart Store configuration for conditional or complex state updates. This allows for computing the new state based on arbitrary logic before returning the updated state object. ```kotlin nextStateBy { // ... val newCount = ... state.copy(count = newCount) } ``` -------------------------------- ### Error Handling in Tart State Enter Block (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to handle errors within the 'enter' block of a Tart state. It shows catching exceptions and transitioning to an error state. This approach requires manual try-catch blocks within the 'enter' function. ```kotlin sealed interface CounterState : State { // ... data class Error(val error: Exception) : CounterState } val store: Store = Store { // ... state { enter { try { val count = counterRepository.get() nextState(CounterState.Main(count = count)) } catch (e: Exception) { nextState(CounterState.Error(error = e)) } } } } ``` -------------------------------- ### Collecting Flows in Tart State Enter Block (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Demonstrates how to collect data from flows within the 'enter' block of a Tart state using coroutines. This allows the store to react to external data streams and update its state accordingly. The coroutine is automatically cancelled when the state is exited. ```kotlin state { enter { // launch a coroutine that lives as long as this state is active launch { // collect from an external data source dataRepository.observeData().collect { newData -> // update state with the new data in a transaction transaction { nextState(state.copy(data = newData)) } } } } } ``` -------------------------------- ### Add Tart Compose Dependency Source: https://github.com/yumemi-inc/tart/blob/main/README.md Include the tart-compose dependency in your project's build.gradle file to enable Compose integration. This is the first step to using Tart's Compose features. ```gradle implementation("io.yumemi.tart:tart-compose:") ``` -------------------------------- ### Specifying CoroutineDispatchers Locally in Tart Blocks (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Shows how to specify CoroutineDispatchers for individual blocks like 'enter', 'exit', 'action', 'error', and 'launch' within a Tart Store. This enables fine-grained control over which thread specific operations are executed on. ```kotlin enter(Dispatchers.Default) { // work on CPU thread.. launch(Dispatchers.IO) { // This code runs on IO thread val updates = dataRepository.observeUpdates() updates.collect { newData -> // ... } } } // Alternatively, using withContext() enter { withContext(Dispatchers.Default) { // work on CPU thread.. withContext(Dispatchers.IO) { // This code runs on IO thread launch { val updates = dataRepository.observeUpdates() updates.collect { newData -> // ... } } } } } ``` -------------------------------- ### Define State and Action for Counter App Source: https://github.com/yumemi-inc/tart/blob/main/README.md Defines the data classes for the state and sealed interface for actions in a simple counter application. `CounterState` holds the current count, and `CounterAction` defines possible state-changing events like Increment and Decrement. ```kotlin data class CounterState(val count: Int) : State sealed interface CounterAction : Action { data object Increment : CounterAction data object Decrement : CounterAction } ``` -------------------------------- ### Define Helper Function in Store (Kotlin) Source: https://github.com/yumemi-inc/tart/blob/main/README.md Illustrates defining a suspend helper function `loadCount` within the scope of a Tart Store creation. This function encapsulates repository interaction, making the `action` blocks cleaner and more focused on state transitions. ```kotlin fun CounterStore( counterRepository: CounterRepository, ): Store = Store(CounterState(count = 0)) { // define as a function suspend fun loadCount(): Int { return counterRepository.get() } state { action { nextState(state.copy(count = loadCount())) // call the function } // ... ```