Merge pull request #271 from ouchadam/tech/state-submodule

screen-state submodule
This commit is contained in:
Adam Brown 2022-11-28 19:20:06 +00:00 committed by GitHub
commit 4d829ac84e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 102 additions and 715 deletions

View File

@ -17,6 +17,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions/setup-java@v3 - uses: actions/setup-java@v3
with: with:
distribution: 'adopt' distribution: 'adopt'

View File

@ -14,6 +14,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions/setup-java@v2 - uses: actions/setup-java@v2
with: with:
distribution: 'adopt' distribution: 'adopt'

View File

@ -16,6 +16,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions/setup-java@v2 - uses: actions/setup-java@v2
with: with:
distribution: 'adopt' distribution: 'adopt'

View File

@ -16,6 +16,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:

View File

@ -17,6 +17,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: 'recursive'
- uses: actions/setup-java@v2 - uses: actions/setup-java@v2
with: with:
distribution: 'adopt' distribution: 'adopt'

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "screen-state"]
path = screen-state
url = git@github.com:ouchadam/screen-state.git

View File

@ -163,7 +163,6 @@ ext.firebase = { dependencies, name ->
} }
} }
if (launchTask.contains("codeCoverageReport".toLowerCase())) { if (launchTask.contains("codeCoverageReport".toLowerCase())) {
apply from: 'tools/coverage.gradle' apply from: 'tools/coverage.gradle'
} }

View File

@ -1,64 +0,0 @@
package app.dapk.st.design.components
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@Composable
fun <T : Any> Spider(currentPage: SpiderPage<T>, onNavigate: (SpiderPage<out T>?) -> Unit, graph: SpiderScope.() -> Unit) {
val pageCache = remember { mutableMapOf<Route<*>, SpiderPage<out T>>() }
pageCache[currentPage.route] = currentPage
val navigateAndPopStack = {
pageCache.remove(currentPage.route)
onNavigate(pageCache[currentPage.parent])
}
val itemScope = object : SpiderItemScope {
override fun goBack() {
navigateAndPopStack()
}
}
val computedWeb = remember(true) {
mutableMapOf<Route<*>, @Composable (T) -> Unit>().also { computedWeb ->
val scope = object : SpiderScope {
override fun <T> item(route: Route<T>, content: @Composable SpiderItemScope.(T) -> Unit) {
computedWeb[route] = { content(itemScope, it as T) }
}
}
graph.invoke(scope)
}
}
Column {
if (currentPage.hasToolbar) {
Toolbar(
onNavigate = navigateAndPopStack,
title = currentPage.label
)
}
BackHandler(onBack = navigateAndPopStack)
computedWeb[currentPage.route]!!.invoke(currentPage.state)
}
}
interface SpiderScope {
fun <T> item(route: Route<T>, content: @Composable SpiderItemScope.(T) -> Unit)
}
interface SpiderItemScope {
fun goBack()
}
data class SpiderPage<T>(
val route: Route<T>,
val label: String,
val parent: Route<*>?,
val state: T,
val hasToolbar: Boolean = true,
)
@JvmInline
value class Route<out S>(val value: String)

View File

@ -5,5 +5,4 @@ dependencies {
implementation project(":features:navigator") implementation project(":features:navigator")
implementation project(":design-library") implementation project(":design-library")
api project(":domains:android:core") api project(":domains:android:core")
api project(":domains:state")
} }

View File

@ -21,53 +21,3 @@ inline fun <reified VM : ViewModel> ComponentActivity.viewModel(
} }
return ViewModelLazy(VM::class, { viewModelStore }, { factoryPromise }) return ViewModelLazy(VM::class, { viewModelStore }, { factoryPromise })
} }
inline fun <reified S, E> ComponentActivity.state(
noinline factory: () -> State<S, E>
): Lazy<State<S, E>> {
val factoryPromise = object : Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return when(modelClass) {
StateViewModel::class.java -> factory() as T
else -> throw Error()
}
}
}
return KeyedViewModelLazy(
key = S::class.java.canonicalName!!,
StateViewModel::class,
{ viewModelStore },
{ factoryPromise }
) as Lazy<State<S, E>>
}
class KeyedViewModelLazy<VM : ViewModel> @JvmOverloads constructor(
private val key: String,
private val viewModelClass: KClass<VM>,
private val storeProducer: () -> ViewModelStore,
private val factoryProducer: () -> ViewModelProvider.Factory,
) : Lazy<VM> {
private var cached: VM? = null
override val value: VM
get() {
val viewModel = cached
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
ViewModelProvider(
store,
factory,
CreationExtras.Empty
).get(key, viewModelClass.java).also {
cached = it
}
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}

View File

@ -1,48 +0,0 @@
package app.dapk.st.core
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.dapk.state.Action
import app.dapk.state.ReducerFactory
import app.dapk.state.Store
import app.dapk.state.createStore
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
class StateViewModel<S, E>(
reducerFactory: ReducerFactory<S>,
eventSource: MutableSharedFlow<E>,
) : ViewModel(), State<S, E> {
private val store: Store<S> = createStore(reducerFactory, viewModelScope)
override val events: SharedFlow<E> = eventSource
override val current
get() = _state!!
private var _state: S by mutableStateOf(store.getState())
init {
_state = store.getState()
store.subscribe {
_state = it
}
}
override fun dispatch(action: Action) {
store.dispatch(action)
}
}
fun <S, E> createStateViewModel(block: (suspend (E) -> Unit) -> ReducerFactory<S>): StateViewModel<S, E> {
val eventSource = MutableSharedFlow<E>(extraBufferCapacity = 1)
val reducer = block { eventSource.emit(it) }
return StateViewModel(reducer, eventSource)
}
interface State<S, E> {
fun dispatch(action: Action)
val events: SharedFlow<E>
val current: S
}

View File

@ -1,95 +0,0 @@
package app.dapk.st.core.page
import app.dapk.st.design.components.SpiderPage
import app.dapk.state.*
import kotlin.reflect.KClass
sealed interface PageAction<out P> : Action {
data class GoTo<P : Any>(val page: SpiderPage<P>) : PageAction<P>
}
sealed interface PageStateChange : Action {
data class ChangePage<P : Any>(val previous: SpiderPage<out P>, val newPage: SpiderPage<out P>) : PageAction<P>
data class UpdatePage<P : Any>(val pageContent: P) : PageAction<P>
}
data class PageContainer<P>(
val page: SpiderPage<out P>
)
interface PageReducerScope<P> {
fun <PC : Any> withPageContent(page: KClass<PC>, block: PageDispatchScope<PC>.() -> Unit)
fun rawPage(): SpiderPage<out P>
}
interface PageDispatchScope<PC> {
fun ReducerScope<*>.pageDispatch(action: PageAction<PC>)
fun getPageState(): PC?
}
fun <P : Any, S : Any> createPageReducer(
initialPage: SpiderPage<out P>,
factory: PageReducerScope<P>.() -> ReducerFactory<S>,
): ReducerFactory<Combined2<PageContainer<P>, S>> = shareState {
combineReducers(createPageReducer(initialPage), factory(pageReducerScope()))
}
private fun <P : Any, S : Any> SharedStateScope<Combined2<PageContainer<P>, S>>.pageReducerScope() = object : PageReducerScope<P> {
override fun <PC : Any> withPageContent(page: KClass<PC>, block: PageDispatchScope<PC>.() -> Unit) {
val currentPage = getSharedState().state1.page.state
if (currentPage::class == page) {
val pageDispatchScope = object : PageDispatchScope<PC> {
override fun ReducerScope<*>.pageDispatch(action: PageAction<PC>) {
val currentPageGuard = getSharedState().state1.page.state
if (currentPageGuard::class == page) {
dispatch(action)
}
}
override fun getPageState() = getSharedState().state1.page.state as? PC
}
block(pageDispatchScope)
}
}
override fun rawPage() = getSharedState().state1.page
}
@Suppress("UNCHECKED_CAST")
private fun <P : Any> createPageReducer(
initialPage: SpiderPage<out P>
): ReducerFactory<PageContainer<P>> {
return createReducer(
initialState = PageContainer(
page = initialPage
),
async(PageAction.GoTo::class) { action ->
val state = getState()
if (state.page.state::class != action.page.state::class) {
dispatch(PageStateChange.ChangePage(previous = state.page, newPage = action.page))
} else {
dispatch(PageStateChange.UpdatePage(action.page.state))
}
},
change(PageStateChange.ChangePage::class) { action, state ->
state.copy(page = action.newPage as SpiderPage<out P>)
},
change(PageStateChange.UpdatePage::class) { action, state ->
val isSamePage = state.page.state::class == action.pageContent::class
if (isSamePage) {
val updatedPageContent = (state.page as SpiderPage<Any>).copy(state = action.pageContent)
state.copy(page = updatedPageContent as SpiderPage<out P>)
} else {
state
}
},
)
}
inline fun <reified PC : Any> PageReducerScope<*>.withPageContext(crossinline block: PageDispatchScope<PC>.(PC) -> Unit) {
withPageContent(PC::class) { getPageState()?.let { block(it) } }
}

View File

@ -1,14 +0,0 @@
plugins {
id 'kotlin'
id 'java-test-fixtures'
}
dependencies {
implementation Dependencies.mavenCentral.kotlinCoroutinesCore
testFixturesImplementation testFixtures(project(":core"))
testFixturesImplementation Dependencies.mavenCentral.kotlinCoroutinesCore
testFixturesImplementation Dependencies.mavenCentral.kluent
testFixturesImplementation Dependencies.mavenCentral.mockk
testFixturesImplementation Dependencies.mavenCentral.kotlinCoroutinesTest
}

View File

@ -1,194 +0,0 @@
package app.dapk.state
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlin.reflect.KClass
fun <S> createStore(reducerFactory: ReducerFactory<S>, coroutineScope: CoroutineScope): Store<S> {
val subscribers = mutableListOf<(S) -> Unit>()
var state: S = reducerFactory.initialState()
return object : Store<S> {
private val scope = createScope(coroutineScope, this)
private val reducer = reducerFactory.create(scope)
override fun dispatch(action: Action) {
coroutineScope.launch {
state = reducer.reduce(action).also { nextState ->
if (nextState != state) {
subscribers.forEach { it.invoke(nextState) }
}
}
}
}
override fun getState() = state
override fun subscribe(subscriber: (S) -> Unit) {
subscribers.add(subscriber)
}
}
}
interface ReducerFactory<S> {
fun create(scope: ReducerScope<S>): Reducer<S>
fun initialState(): S
}
fun interface Reducer<S> {
fun reduce(action: Action): S
}
private fun <S> createScope(coroutineScope: CoroutineScope, store: Store<S>) = object : ReducerScope<S> {
override val coroutineScope = coroutineScope
override fun dispatch(action: Action) = store.dispatch(action)
override fun getState(): S = store.getState()
}
interface Store<S> {
fun dispatch(action: Action)
fun getState(): S
fun subscribe(subscriber: (S) -> Unit)
}
interface ReducerScope<S> {
val coroutineScope: CoroutineScope
fun dispatch(action: Action)
fun getState(): S
}
sealed interface ActionHandler<S> {
val key: KClass<Action>
class Async<S>(override val key: KClass<Action>, val handler: suspend ReducerScope<S>.(Action) -> Unit) : ActionHandler<S>
class Sync<S>(override val key: KClass<Action>, val handler: (Action, S) -> S) : ActionHandler<S>
class Delegate<S>(override val key: KClass<Action>, val handler: ReducerScope<S>.(Action) -> ActionHandler<S>) : ActionHandler<S>
}
data class Combined2<S1, S2>(val state1: S1, val state2: S2)
fun interface SharedStateScope<C> {
fun getSharedState(): C
}
fun <S> shareState(block: SharedStateScope<S>.() -> ReducerFactory<S>): ReducerFactory<S> {
var internalScope: ReducerScope<S>? = null
val scope = SharedStateScope { internalScope!!.getState() }
val combinedFactory = block(scope)
return object : ReducerFactory<S> {
override fun create(scope: ReducerScope<S>) = combinedFactory.create(scope).also { internalScope = scope }
override fun initialState() = combinedFactory.initialState()
}
}
fun <S1, S2> combineReducers(r1: ReducerFactory<S1>, r2: ReducerFactory<S2>): ReducerFactory<Combined2<S1, S2>> {
return object : ReducerFactory<Combined2<S1, S2>> {
override fun create(scope: ReducerScope<Combined2<S1, S2>>): Reducer<Combined2<S1, S2>> {
val r1Scope = createReducerScope(scope) { scope.getState().state1 }
val r2Scope = createReducerScope(scope) { scope.getState().state2 }
val r1Reducer = r1.create(r1Scope)
val r2Reducer = r2.create(r2Scope)
return Reducer {
Combined2(r1Reducer.reduce(it), r2Reducer.reduce(it))
}
}
override fun initialState(): Combined2<S1, S2> = Combined2(r1.initialState(), r2.initialState())
}
}
private fun <S> createReducerScope(scope: ReducerScope<*>, state: () -> S) = object : ReducerScope<S> {
override val coroutineScope: CoroutineScope = scope.coroutineScope
override fun dispatch(action: Action) = scope.dispatch(action)
override fun getState() = state.invoke()
}
fun <S> createReducer(
initialState: S,
vararg reducers: (ReducerScope<S>) -> ActionHandler<S>,
): ReducerFactory<S> {
return object : ReducerFactory<S> {
override fun create(scope: ReducerScope<S>): Reducer<S> {
val reducersMap = reducers
.map { it.invoke(scope) }
.groupBy { it.key }
return Reducer { action ->
val result = reducersMap.keys
.filter { it.java.isAssignableFrom(action::class.java) }
.fold(scope.getState()) { acc, key ->
val actionHandlers = reducersMap[key]!!
actionHandlers.fold(acc) { acc, handler ->
when (handler) {
is ActionHandler.Async -> {
scope.coroutineScope.launch {
handler.handler.invoke(scope, action)
}
acc
}
is ActionHandler.Sync -> handler.handler.invoke(action, acc)
is ActionHandler.Delegate -> when (val next = handler.handler.invoke(scope, action)) {
is ActionHandler.Async -> {
scope.coroutineScope.launch {
next.handler.invoke(scope, action)
}
acc
}
is ActionHandler.Sync -> next.handler.invoke(action, acc)
is ActionHandler.Delegate -> error("is not possible")
}
}
}
}
result
}
}
override fun initialState(): S = initialState
}
}
fun <A : Action, S> sideEffect(klass: KClass<A>, block: suspend (A, S) -> Unit): (ReducerScope<S>) -> ActionHandler<S> {
return {
ActionHandler.Async(key = klass as KClass<Action>) { action -> block(action as A, getState()) }
}
}
fun <A : Action, S> change(klass: KClass<A>, block: (A, S) -> S): (ReducerScope<S>) -> ActionHandler<S> {
return {
ActionHandler.Sync(key = klass as KClass<Action>, block as (Action, S) -> S)
}
}
fun <A : Action, S> async(klass: KClass<A>, block: suspend ReducerScope<S>.(A) -> Unit): (ReducerScope<S>) -> ActionHandler<S> {
return {
ActionHandler.Async(key = klass as KClass<Action>, block as suspend ReducerScope<S>.(Action) -> Unit)
}
}
fun <A : Action, S> multi(klass: KClass<A>, block: Multi<A, S>.(A) -> (ReducerScope<S>) -> ActionHandler<S>): (ReducerScope<S>) -> ActionHandler<S> {
val multiScope = object : Multi<A, S> {
override fun sideEffect(block: suspend (S) -> Unit): (ReducerScope<S>) -> ActionHandler<S> = sideEffect(klass) { _, state -> block(state) }
override fun change(block: (A, S) -> S): (ReducerScope<S>) -> ActionHandler<S> = change(klass, block)
override fun async(block: suspend ReducerScope<S>.(A) -> Unit): (ReducerScope<S>) -> ActionHandler<S> = async(klass, block)
override fun nothing() = sideEffect { }
}
return {
ActionHandler.Delegate(key = klass as KClass<Action>) { action ->
block(multiScope, action as A).invoke(this)
}
}
}
interface Multi<A : Action, S> {
fun sideEffect(block: suspend (S) -> Unit): (ReducerScope<S>) -> ActionHandler<S>
fun nothing(): (ReducerScope<S>) -> ActionHandler<S>
fun change(block: (A, S) -> S): (ReducerScope<S>) -> ActionHandler<S>
fun async(block: suspend ReducerScope<S>.(A) -> Unit): (ReducerScope<S>) -> ActionHandler<S>
}
interface Action

View File

@ -1,20 +0,0 @@
package fake
import org.amshove.kluent.internal.assertEquals
class FakeEventSource<E> : (E) -> Unit {
private val captures = mutableListOf<E>()
override fun invoke(event: E) {
captures.add(event)
}
fun assertEvents(expected: List<E>) {
assertEquals(expected, captures)
}
fun assertNoEvents() {
assertEquals(emptyList(), captures)
}
}

View File

@ -1,153 +0,0 @@
package test
import app.dapk.state.Action
import app.dapk.state.Reducer
import app.dapk.state.ReducerFactory
import app.dapk.state.ReducerScope
import fake.FakeEventSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.internal.assertEquals
import org.amshove.kluent.shouldBeEqualTo
interface ReducerTest<S, E> {
operator fun invoke(block: suspend ReducerTestScope<S, E>.() -> Unit)
}
fun <S, E> testReducer(block: ((E) -> Unit) -> ReducerFactory<S>): ReducerTest<S, E> {
val fakeEventSource = FakeEventSource<E>()
val reducerFactory = block(fakeEventSource)
return object : ReducerTest<S, E> {
override fun invoke(block: suspend ReducerTestScope<S, E>.() -> Unit) {
runReducerTest(reducerFactory, fakeEventSource, block)
}
}
}
fun <S, E> runReducerTest(reducerFactory: ReducerFactory<S>, fakeEventSource: FakeEventSource<E>, block: suspend ReducerTestScope<S, E>.() -> Unit) {
runTest {
val expectTestScope = ExpectTest(coroutineContext)
block(ReducerTestScope(reducerFactory, fakeEventSource, expectTestScope))
expectTestScope.verifyExpects()
}
}
class ReducerTestScope<S, E>(
private val reducerFactory: ReducerFactory<S>,
private val fakeEventSource: FakeEventSource<E>,
private val expectTestScope: ExpectTestScope
) : ExpectTestScope by expectTestScope, Reducer<S> {
private var invalidateCapturedState: Boolean = false
private val actionSideEffects = mutableMapOf<Action, () -> S>()
private var manualState: S? = null
private var capturedResult: S? = null
private val actionCaptures = mutableListOf<Action>()
private val reducerScope = object : ReducerScope<S> {
override val coroutineScope = CoroutineScope(UnconfinedTestDispatcher())
override fun dispatch(action: Action) {
actionCaptures.add(action)
if (actionSideEffects.containsKey(action)) {
setState(actionSideEffects.getValue(action).invoke(), invalidateCapturedState = true)
}
}
override fun getState() = manualState ?: reducerFactory.initialState()
}
private val reducer: Reducer<S> = reducerFactory.create(reducerScope)
override fun reduce(action: Action) = reducer.reduce(action).also {
capturedResult = if (invalidateCapturedState) manualState else it
}
fun actionSideEffect(action: Action, handler: () -> S) {
actionSideEffects[action] = handler
}
fun setState(state: S, invalidateCapturedState: Boolean = false) {
manualState = state
this.invalidateCapturedState = invalidateCapturedState
}
fun setState(block: (S) -> S) {
setState(block(reducerScope.getState()))
}
fun assertInitialState(expected: S) {
reducerFactory.initialState() shouldBeEqualTo expected
}
fun assertEvents(events: List<E>) {
fakeEventSource.assertEvents(events)
}
fun assertOnlyStateChange(expected: S) {
assertStateChange(expected)
assertNoDispatches()
fakeEventSource.assertNoEvents()
}
fun assertOnlyStateChange(block: (S) -> S) {
val expected = block(reducerScope.getState())
assertStateChange(expected)
assertNoDispatches()
fakeEventSource.assertNoEvents()
}
fun assertStateChange(expected: S) {
capturedResult shouldBeEqualTo expected
}
fun assertDispatches(expected: List<Action>) {
assertEquals(expected, actionCaptures)
}
fun assertNoDispatches() {
assertEquals(emptyList(), actionCaptures)
}
fun assertNoStateChange() {
assertEquals(reducerScope.getState(), capturedResult)
}
fun assertNoEvents() {
fakeEventSource.assertNoEvents()
}
fun assertOnlyDispatches(expected: List<Action>) {
assertDispatches(expected)
fakeEventSource.assertNoEvents()
assertNoStateChange()
}
fun assertOnlyEvents(events: List<E>) {
fakeEventSource.assertEvents(events)
assertNoDispatches()
assertNoStateChange()
}
fun assertNoChanges() {
assertNoStateChange()
assertNoEvents()
assertNoDispatches()
}
}
fun <S, E> ReducerTestScope<S, E>.assertOnlyDispatches(vararg action: Action) {
this.assertOnlyDispatches(action.toList())
}
fun <S, E> ReducerTestScope<S, E>.assertDispatches(vararg action: Action) {
this.assertDispatches(action.toList())
}
fun <S, E> ReducerTestScope<S, E>.assertEvents(vararg event: E) {
this.assertEvents(event.toList())
}
fun <S, E> ReducerTestScope<S, E>.assertOnlyEvents(vararg event: E) {
this.assertOnlyEvents(event.toList())
}

View File

@ -3,7 +3,7 @@ applyAndroidComposeLibraryModule(project)
dependencies { dependencies {
implementation project(":chat-engine") implementation project(":chat-engine")
implementation project(":domains:android:compose-core") implementation project(":domains:android:compose-core")
implementation project(":domains:state") implementation 'screen-state:screen-android'
implementation project(":features:messenger") implementation project(":features:messenger")
implementation project(":core") implementation project(":core")
implementation project(":design-library") implementation project(":design-library")
@ -11,9 +11,9 @@ dependencies {
kotlinTest(it) kotlinTest(it)
testImplementation 'screen-state:state-test'
androidImportFixturesWorkaround(project, project(":matrix:common")) androidImportFixturesWorkaround(project, project(":matrix:common"))
androidImportFixturesWorkaround(project, project(":core")) androidImportFixturesWorkaround(project, project(":core"))
androidImportFixturesWorkaround(project, project(":domains:state"))
androidImportFixturesWorkaround(project, project(":domains:store")) androidImportFixturesWorkaround(project, project(":domains:store"))
androidImportFixturesWorkaround(project, project(":domains:android:stub")) androidImportFixturesWorkaround(project, project(":domains:android:stub"))
androidImportFixturesWorkaround(project, project(":chat-engine")) androidImportFixturesWorkaround(project, project(":chat-engine"))

View File

@ -2,7 +2,7 @@ package app.dapk.st.directory
import android.content.Context import android.content.Context
import app.dapk.st.core.ProvidableModule import app.dapk.st.core.ProvidableModule
import app.dapk.st.core.createStateViewModel import app.dapk.st.state.createStateViewModel
import app.dapk.st.core.JobBag import app.dapk.st.core.JobBag
import app.dapk.st.directory.state.DirectoryState import app.dapk.st.directory.state.DirectoryState
import app.dapk.st.directory.state.directoryReducer import app.dapk.st.directory.state.directoryReducer

View File

@ -1,6 +1,6 @@
package app.dapk.st.directory.state package app.dapk.st.directory.state
import app.dapk.st.core.State import app.dapk.st.state.State
import app.dapk.st.engine.DirectoryState import app.dapk.st.engine.DirectoryState
typealias DirectoryState = State<DirectoryScreenState, DirectoryEvent> typealias DirectoryState = State<DirectoryScreenState, DirectoryEvent>

View File

@ -9,7 +9,7 @@ dependencies {
implementation project(":domains:android:compose-core") implementation project(":domains:android:compose-core")
implementation project(":domains:android:viewmodel") implementation project(":domains:android:viewmodel")
implementation project(':domains:store') implementation project(':domains:store')
implementation project(':domains:state') implementation 'screen-state:screen-android'
implementation project(":core") implementation project(":core")
implementation project(":design-library") implementation project(":design-library")
implementation Dependencies.mavenCentral.coil implementation Dependencies.mavenCentral.coil

View File

@ -13,11 +13,11 @@ import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.dapk.st.core.DapkActivity import app.dapk.st.core.DapkActivity
import app.dapk.st.core.module import app.dapk.st.core.module
import app.dapk.st.core.state
import app.dapk.st.core.viewModel import app.dapk.st.core.viewModel
import app.dapk.st.directory.DirectoryModule import app.dapk.st.directory.DirectoryModule
import app.dapk.st.login.LoginModule import app.dapk.st.login.LoginModule
import app.dapk.st.profile.ProfileModule import app.dapk.st.profile.ProfileModule
import app.dapk.st.state.state
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach

View File

@ -6,7 +6,7 @@ dependencies {
implementation project(":domains:android:compose-core") implementation project(":domains:android:compose-core")
implementation project(":domains:android:viewmodel") implementation project(":domains:android:viewmodel")
implementation project(":domains:store") implementation project(":domains:store")
implementation project(":domains:state") implementation 'screen-state:screen-android'
implementation project(":core") implementation project(":core")
implementation project(":features:navigator") implementation project(":features:navigator")
implementation project(":design-library") implementation project(":design-library")
@ -14,10 +14,10 @@ dependencies {
kotlinTest(it) kotlinTest(it)
testImplementation 'screen-state:state-test'
androidImportFixturesWorkaround(project, project(":matrix:common")) androidImportFixturesWorkaround(project, project(":matrix:common"))
androidImportFixturesWorkaround(project, project(":core")) androidImportFixturesWorkaround(project, project(":core"))
androidImportFixturesWorkaround(project, project(":domains:store")) androidImportFixturesWorkaround(project, project(":domains:store"))
androidImportFixturesWorkaround(project, project(":domains:state"))
androidImportFixturesWorkaround(project, project(":domains:android:viewmodel")) androidImportFixturesWorkaround(project, project(":domains:android:viewmodel"))
androidImportFixturesWorkaround(project, project(":domains:android:stub")) androidImportFixturesWorkaround(project, project(":domains:android:stub"))
androidImportFixturesWorkaround(project, project(":chat-engine")) androidImportFixturesWorkaround(project, project(":chat-engine"))

View File

@ -10,15 +10,17 @@ import androidx.compose.material3.Surface
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import app.dapk.st.core.* import app.dapk.st.core.AndroidUri
import app.dapk.st.core.DapkActivity
import app.dapk.st.core.MimeType
import app.dapk.st.core.extensions.unsafeLazy import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.core.module
import app.dapk.st.matrix.common.RoomId import app.dapk.st.matrix.common.RoomId
import app.dapk.st.messenger.gallery.GetImageFromGallery import app.dapk.st.messenger.gallery.GetImageFromGallery
import app.dapk.st.messenger.state.ComposerStateChange import app.dapk.st.messenger.state.ComposerStateChange
import app.dapk.st.messenger.state.MessengerEvent
import app.dapk.st.messenger.state.MessengerScreenState
import app.dapk.st.messenger.state.MessengerState import app.dapk.st.messenger.state.MessengerState
import app.dapk.st.navigator.MessageAttachment import app.dapk.st.navigator.MessageAttachment
import app.dapk.st.state.state
import coil.request.ImageRequest import coil.request.ImageRequest
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -27,7 +29,7 @@ val LocalImageRequestFactory = staticCompositionLocalOf<ImageRequest.Builder> {
class MessengerActivity : DapkActivity() { class MessengerActivity : DapkActivity() {
private val module by unsafeLazy { module<MessengerModule>() } private val module by unsafeLazy { module<MessengerModule>() }
private val state by state { module.messengerState(readPayload()) } private val state: MessengerState by state { module.messengerState(readPayload()) }
companion object { companion object {

View File

@ -5,7 +5,7 @@ import android.content.Context
import app.dapk.st.core.DeviceMeta import app.dapk.st.core.DeviceMeta
import app.dapk.st.core.JobBag import app.dapk.st.core.JobBag
import app.dapk.st.core.ProvidableModule import app.dapk.st.core.ProvidableModule
import app.dapk.st.core.createStateViewModel import app.dapk.st.state.createStateViewModel
import app.dapk.st.domain.application.message.MessageOptionsStore import app.dapk.st.domain.application.message.MessageOptionsStore
import app.dapk.st.engine.ChatEngine import app.dapk.st.engine.ChatEngine
import app.dapk.st.matrix.common.RoomId import app.dapk.st.matrix.common.RoomId

View File

@ -15,16 +15,17 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.dapk.st.core.* import app.dapk.st.core.*
import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.design.components.GenericError import app.dapk.st.design.components.GenericError
import app.dapk.st.messenger.gallery.state.ImageGalleryState
import app.dapk.st.state.state
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
class ImageGalleryActivity : DapkActivity() { class ImageGalleryActivity : DapkActivity() {
private val module by unsafeLazy { module<ImageGalleryModule>() } private val imageGalleryState: ImageGalleryState by state {
private val imageGalleryState by state {
val payload = intent.getParcelableExtra("key") as? ImageGalleryActivityPayload val payload = intent.getParcelableExtra("key") as? ImageGalleryActivityPayload
val module = module<ImageGalleryModule>()
module.imageGalleryState(payload!!.roomName) module.imageGalleryState(payload!!.roomName)
} }

View File

@ -6,7 +6,7 @@ import android.provider.MediaStore
import app.dapk.st.core.CoroutineDispatchers import app.dapk.st.core.CoroutineDispatchers
import app.dapk.st.core.JobBag import app.dapk.st.core.JobBag
import app.dapk.st.core.ProvidableModule import app.dapk.st.core.ProvidableModule
import app.dapk.st.core.createStateViewModel import app.dapk.st.state.createStateViewModel
import app.dapk.st.messenger.gallery.state.ImageGalleryState import app.dapk.st.messenger.gallery.state.ImageGalleryState
import app.dapk.st.messenger.gallery.state.imageGalleryReducer import app.dapk.st.messenger.gallery.state.imageGalleryReducer

View File

@ -21,13 +21,14 @@ import androidx.compose.ui.unit.sp
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.LifecycleEffect import app.dapk.st.core.LifecycleEffect
import app.dapk.st.core.components.CenteredLoading import app.dapk.st.core.components.CenteredLoading
import app.dapk.st.core.page.PageAction
import app.dapk.st.design.components.GenericError import app.dapk.st.design.components.GenericError
import app.dapk.st.design.components.Spider import app.dapk.st.design.components.Spider
import app.dapk.st.design.components.SpiderPage import app.dapk.st.design.components.Toolbar
import app.dapk.st.messenger.gallery.state.ImageGalleryActions import app.dapk.st.messenger.gallery.state.ImageGalleryActions
import app.dapk.st.messenger.gallery.state.ImageGalleryPage import app.dapk.st.messenger.gallery.state.ImageGalleryPage
import app.dapk.st.messenger.gallery.state.ImageGalleryState import app.dapk.st.messenger.gallery.state.ImageGalleryState
import app.dapk.state.SpiderPage
import app.dapk.state.page.PageAction
import coil.compose.rememberAsyncImagePainter import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest import coil.request.ImageRequest
@ -44,7 +45,7 @@ fun ImageGalleryScreen(state: ImageGalleryState, onTopLevelBack: () -> Unit, onI
} }
} }
Spider(currentPage = state.current.state1.page, onNavigate = onNavigate) { Spider(currentPage = state.current.state1.page, onNavigate = onNavigate, toolbar = { navigate, title -> Toolbar(navigate, title) }) {
item(ImageGalleryPage.Routes.folders) { item(ImageGalleryPage.Routes.folders) {
ImageGalleryFolders( ImageGalleryFolders(
it, it,

View File

@ -2,15 +2,15 @@ package app.dapk.st.messenger.gallery.state
import app.dapk.st.core.JobBag import app.dapk.st.core.JobBag
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.page.PageAction
import app.dapk.st.core.page.PageStateChange
import app.dapk.st.core.page.createPageReducer
import app.dapk.st.core.page.withPageContext
import app.dapk.st.design.components.SpiderPage
import app.dapk.st.messenger.gallery.FetchMediaFoldersUseCase import app.dapk.st.messenger.gallery.FetchMediaFoldersUseCase
import app.dapk.st.messenger.gallery.FetchMediaUseCase import app.dapk.st.messenger.gallery.FetchMediaUseCase
import app.dapk.state.SpiderPage
import app.dapk.state.async import app.dapk.state.async
import app.dapk.state.createReducer import app.dapk.state.createReducer
import app.dapk.state.page.PageAction
import app.dapk.state.page.PageStateChange
import app.dapk.state.page.createPageReducer
import app.dapk.state.page.withPageContext
import app.dapk.state.sideEffect import app.dapk.state.sideEffect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -1,12 +1,12 @@
package app.dapk.st.messenger.gallery.state package app.dapk.st.messenger.gallery.state
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.State import app.dapk.st.state.State
import app.dapk.st.design.components.Route
import app.dapk.st.messenger.gallery.Folder import app.dapk.st.messenger.gallery.Folder
import app.dapk.st.messenger.gallery.Media import app.dapk.st.messenger.gallery.Media
import app.dapk.st.core.page.PageContainer
import app.dapk.state.Combined2 import app.dapk.state.Combined2
import app.dapk.state.Route
import app.dapk.state.page.PageContainer
typealias ImageGalleryState = State<Combined2<PageContainer<ImageGalleryPage>, Unit>, Unit> typealias ImageGalleryState = State<Combined2<PageContainer<ImageGalleryPage>, Unit>, Unit>

View File

@ -1,7 +1,7 @@
package app.dapk.st.messenger.state package app.dapk.st.messenger.state
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.State import app.dapk.st.state.State
import app.dapk.st.design.components.BubbleModel import app.dapk.st.design.components.BubbleModel
import app.dapk.st.engine.MessengerPageState import app.dapk.st.engine.MessengerPageState
import app.dapk.st.engine.RoomEvent import app.dapk.st.engine.RoomEvent

View File

@ -2,15 +2,15 @@ package app.dapk.st.messenger.gallery.state
import android.net.Uri import android.net.Uri
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.page.PageAction
import app.dapk.st.core.page.PageContainer
import app.dapk.st.core.page.PageStateChange
import app.dapk.st.design.components.SpiderPage
import app.dapk.st.messenger.gallery.FetchMediaFoldersUseCase import app.dapk.st.messenger.gallery.FetchMediaFoldersUseCase
import app.dapk.st.messenger.gallery.FetchMediaUseCase import app.dapk.st.messenger.gallery.FetchMediaUseCase
import app.dapk.st.messenger.gallery.Folder import app.dapk.st.messenger.gallery.Folder
import app.dapk.st.messenger.gallery.Media import app.dapk.st.messenger.gallery.Media
import app.dapk.state.Combined2 import app.dapk.state.Combined2
import app.dapk.state.SpiderPage
import app.dapk.state.page.PageAction
import app.dapk.state.page.PageContainer
import app.dapk.state.page.PageStateChange
import fake.FakeJobBag import fake.FakeJobBag
import fake.FakeUri import fake.FakeUri
import io.mockk.coEvery import io.mockk.coEvery
@ -18,7 +18,6 @@ import io.mockk.mockk
import org.junit.Test import org.junit.Test
import test.assertOnlyDispatches import test.assertOnlyDispatches
import test.delegateReturn import test.delegateReturn
import test.expect
import test.testReducer import test.testReducer
private const val A_ROOM_NAME = "a room name" private const val A_ROOM_NAME = "a room name"

View File

@ -4,16 +4,16 @@ dependencies {
implementation project(":chat-engine") implementation project(":chat-engine")
implementation project(":features:settings") implementation project(":features:settings")
implementation project(':domains:store') implementation project(':domains:store')
implementation project(':domains:state') implementation 'screen-state:screen-android'
implementation project(":domains:android:compose-core") implementation project(":domains:android:compose-core")
implementation project(":design-library") implementation project(":design-library")
implementation project(":core") implementation project(":core")
kotlinTest(it) kotlinTest(it)
testImplementation 'screen-state:state-test'
androidImportFixturesWorkaround(project, project(":matrix:common")) androidImportFixturesWorkaround(project, project(":matrix:common"))
androidImportFixturesWorkaround(project, project(":core")) androidImportFixturesWorkaround(project, project(":core"))
androidImportFixturesWorkaround(project, project(":domains:state"))
androidImportFixturesWorkaround(project, project(":domains:store")) androidImportFixturesWorkaround(project, project(":domains:store"))
androidImportFixturesWorkaround(project, project(":domains:android:stub")) androidImportFixturesWorkaround(project, project(":domains:android:stub"))
androidImportFixturesWorkaround(project, project(":chat-engine")) androidImportFixturesWorkaround(project, project(":chat-engine"))

View File

@ -2,7 +2,7 @@ package app.dapk.st.profile
import app.dapk.st.core.JobBag import app.dapk.st.core.JobBag
import app.dapk.st.core.ProvidableModule import app.dapk.st.core.ProvidableModule
import app.dapk.st.core.createStateViewModel import app.dapk.st.state.createStateViewModel
import app.dapk.st.core.extensions.ErrorTracker import app.dapk.st.core.extensions.ErrorTracker
import app.dapk.st.engine.ChatEngine import app.dapk.st.engine.ChatEngine
import app.dapk.st.profile.state.ProfileState import app.dapk.st.profile.state.ProfileState

View File

@ -20,7 +20,6 @@ import androidx.compose.ui.unit.dp
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.LifecycleEffect import app.dapk.st.core.LifecycleEffect
import app.dapk.st.core.components.CenteredLoading import app.dapk.st.core.components.CenteredLoading
import app.dapk.st.core.page.PageAction
import app.dapk.st.design.components.* import app.dapk.st.design.components.*
import app.dapk.st.engine.RoomInvite import app.dapk.st.engine.RoomInvite
import app.dapk.st.engine.RoomInvite.InviteMeta import app.dapk.st.engine.RoomInvite.InviteMeta
@ -28,6 +27,8 @@ import app.dapk.st.profile.state.Page
import app.dapk.st.profile.state.ProfileAction import app.dapk.st.profile.state.ProfileAction
import app.dapk.st.profile.state.ProfileState import app.dapk.st.profile.state.ProfileState
import app.dapk.st.settings.SettingsActivity import app.dapk.st.settings.SettingsActivity
import app.dapk.state.SpiderPage
import app.dapk.state.page.PageAction
@Composable @Composable
fun ProfileScreen(viewModel: ProfileState, onTopLevelBack: () -> Unit) { fun ProfileScreen(viewModel: ProfileState, onTopLevelBack: () -> Unit) {
@ -47,7 +48,7 @@ fun ProfileScreen(viewModel: ProfileState, onTopLevelBack: () -> Unit) {
} }
} }
Spider(currentPage = viewModel.current.state1.page, onNavigate = onNavigate) { Spider(currentPage = viewModel.current.state1.page, onNavigate = onNavigate, toolbar = { navigate, title -> Toolbar(navigate, title) }) {
item(Page.Routes.profile) { item(Page.Routes.profile) {
ProfilePage(context, viewModel, it) ProfilePage(context, viewModel, it)
} }

View File

@ -3,14 +3,14 @@ package app.dapk.st.profile.state
import app.dapk.st.core.JobBag import app.dapk.st.core.JobBag
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.extensions.ErrorTracker import app.dapk.st.core.extensions.ErrorTracker
import app.dapk.st.core.page.PageAction
import app.dapk.st.core.page.PageStateChange
import app.dapk.st.core.page.createPageReducer
import app.dapk.st.core.page.withPageContext
import app.dapk.st.design.components.SpiderPage
import app.dapk.st.engine.ChatEngine import app.dapk.st.engine.ChatEngine
import app.dapk.state.SpiderPage
import app.dapk.state.async import app.dapk.state.async
import app.dapk.state.createReducer import app.dapk.state.createReducer
import app.dapk.state.page.PageAction
import app.dapk.state.page.PageStateChange
import app.dapk.state.page.createPageReducer
import app.dapk.state.page.withPageContext
import app.dapk.state.sideEffect import app.dapk.state.sideEffect
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach

View File

@ -1,12 +1,12 @@
package app.dapk.st.profile.state package app.dapk.st.profile.state
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.State import app.dapk.st.state.State
import app.dapk.st.core.page.PageContainer
import app.dapk.st.design.components.Route
import app.dapk.st.engine.Me import app.dapk.st.engine.Me
import app.dapk.st.engine.RoomInvite import app.dapk.st.engine.RoomInvite
import app.dapk.state.Combined2 import app.dapk.state.Combined2
import app.dapk.state.Route
import app.dapk.state.page.PageContainer
typealias ProfileState = State<Combined2<PageContainer<Page>, Unit>, Unit> typealias ProfileState = State<Combined2<PageContainer<Page>, Unit>, Unit>

View File

@ -1,13 +1,13 @@
package app.dapk.st.profile.state package app.dapk.st.profile.state
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.page.PageAction
import app.dapk.st.core.page.PageContainer
import app.dapk.st.core.page.PageStateChange
import app.dapk.st.design.components.SpiderPage
import app.dapk.st.engine.Me import app.dapk.st.engine.Me
import app.dapk.st.matrix.common.HomeServerUrl import app.dapk.st.matrix.common.HomeServerUrl
import app.dapk.state.Combined2 import app.dapk.state.Combined2
import app.dapk.state.SpiderPage
import app.dapk.state.page.PageAction
import app.dapk.state.page.PageContainer
import app.dapk.state.page.PageStateChange
import fake.FakeChatEngine import fake.FakeChatEngine
import fake.FakeErrorTracker import fake.FakeErrorTracker
import fake.FakeJobBag import fake.FakeJobBag

View File

@ -7,15 +7,16 @@ dependencies {
implementation project(':domains:android:push') implementation project(':domains:android:push')
implementation project(":domains:android:compose-core") implementation project(":domains:android:compose-core")
implementation project(":domains:android:viewmodel") implementation project(":domains:android:viewmodel")
implementation 'screen-state:screen-android'
implementation project(":design-library") implementation project(":design-library")
implementation project(":core") implementation project(":core")
kotlinTest(it) kotlinTest(it)
testImplementation 'screen-state:state-test'
androidImportFixturesWorkaround(project, project(":matrix:common")) androidImportFixturesWorkaround(project, project(":matrix:common"))
androidImportFixturesWorkaround(project, project(":core")) androidImportFixturesWorkaround(project, project(":core"))
androidImportFixturesWorkaround(project, project(":domains:store")) androidImportFixturesWorkaround(project, project(":domains:store"))
androidImportFixturesWorkaround(project, project(":domains:state"))
androidImportFixturesWorkaround(project, project(":domains:android:viewmodel")) androidImportFixturesWorkaround(project, project(":domains:android:viewmodel"))
androidImportFixturesWorkaround(project, project(":domains:android:stub")) androidImportFixturesWorkaround(project, project(":domains:android:stub"))
androidImportFixturesWorkaround(project, project(":chat-engine")) androidImportFixturesWorkaround(project, project(":chat-engine"))

View File

@ -4,11 +4,15 @@ import android.os.Bundle
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import app.dapk.st.core.* import app.dapk.st.core.DapkActivity
import app.dapk.st.core.module
import app.dapk.st.core.resetModules
import app.dapk.st.settings.state.SettingsState
import app.dapk.st.state.state
class SettingsActivity : DapkActivity() { class SettingsActivity : DapkActivity() {
private val settingsState by state { module<SettingsModule>().settingsState() } private val settingsState: SettingsState by state { module<SettingsModule>().settingsState() }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -10,6 +10,7 @@ import app.dapk.st.push.PushModule
import app.dapk.st.settings.eventlogger.EventLoggerViewModel import app.dapk.st.settings.eventlogger.EventLoggerViewModel
import app.dapk.st.settings.state.SettingsState import app.dapk.st.settings.state.SettingsState
import app.dapk.st.settings.state.settingsReducer import app.dapk.st.settings.state.settingsReducer
import app.dapk.st.state.createStateViewModel
class SettingsModule( class SettingsModule(
private val chatEngine: ChatEngine, private val chatEngine: ChatEngine,

View File

@ -41,7 +41,6 @@ import app.dapk.st.core.components.CenteredLoading
import app.dapk.st.core.components.Header import app.dapk.st.core.components.Header
import app.dapk.st.core.extensions.takeAs import app.dapk.st.core.extensions.takeAs
import app.dapk.st.core.getActivity import app.dapk.st.core.getActivity
import app.dapk.st.core.page.PageAction
import app.dapk.st.design.components.* import app.dapk.st.design.components.*
import app.dapk.st.engine.ImportResult import app.dapk.st.engine.ImportResult
import app.dapk.st.navigator.Navigator import app.dapk.st.navigator.Navigator
@ -51,6 +50,8 @@ import app.dapk.st.settings.state.ComponentLifecycle
import app.dapk.st.settings.state.RootActions import app.dapk.st.settings.state.RootActions
import app.dapk.st.settings.state.ScreenAction import app.dapk.st.settings.state.ScreenAction
import app.dapk.st.settings.state.SettingsState import app.dapk.st.settings.state.SettingsState
import app.dapk.state.SpiderPage
import app.dapk.state.page.PageAction
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) @OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
@ -66,7 +67,7 @@ internal fun SettingsScreen(settingsState: SettingsState, onSignOut: () -> Unit,
else -> settingsState.dispatch(PageAction.GoTo(it)) else -> settingsState.dispatch(PageAction.GoTo(it))
} }
} }
Spider(currentPage = settingsState.current.state1.page, onNavigate = onNavigate) { Spider(currentPage = settingsState.current.state1.page, onNavigate = onNavigate, toolbar = { navigate, title -> Toolbar(navigate, title) }) {
item(Page.Routes.root) { item(Page.Routes.root) {
RootSettings( RootSettings(
it, it,

View File

@ -2,10 +2,10 @@ package app.dapk.st.settings
import android.net.Uri import android.net.Uri
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.design.components.Route
import app.dapk.st.design.components.SpiderPage
import app.dapk.st.engine.ImportResult import app.dapk.st.engine.ImportResult
import app.dapk.st.push.Registrar import app.dapk.st.push.Registrar
import app.dapk.state.Route
import app.dapk.state.SpiderPage
internal data class SettingsScreenState( internal data class SettingsScreenState(
val page: SpiderPage<out Page>, val page: SpiderPage<out Page>,
@ -26,9 +26,9 @@ internal sealed interface Page {
object Routes { object Routes {
val root = Route<Root>("Settings") val root = Route<Root>("Settings")
val encryption = Route<Page.Security>("Encryption") val encryption = Route<Security>("Encryption")
val pushProviders = Route<Page.PushProviders>("PushProviders") val pushProviders = Route<PushProviders>("PushProviders")
val importRoomKeys = Route<Page.ImportRoomKey>("ImportRoomKey") val importRoomKeys = Route<ImportRoomKey>("ImportRoomKey")
} }
} }

View File

@ -3,10 +3,8 @@ package app.dapk.st.settings.state
import android.content.ContentResolver import android.content.ContentResolver
import app.dapk.st.core.JobBag import app.dapk.st.core.JobBag
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.State import app.dapk.st.state.State
import app.dapk.st.core.ThemeStore import app.dapk.st.core.ThemeStore
import app.dapk.st.core.page.*
import app.dapk.st.design.components.SpiderPage
import app.dapk.st.domain.StoreCleaner import app.dapk.st.domain.StoreCleaner
import app.dapk.st.domain.application.eventlog.LoggingStore import app.dapk.st.domain.application.eventlog.LoggingStore
import app.dapk.st.domain.application.message.MessageOptionsStore import app.dapk.st.domain.application.message.MessageOptionsStore
@ -16,10 +14,8 @@ import app.dapk.st.push.PushTokenRegistrars
import app.dapk.st.settings.* import app.dapk.st.settings.*
import app.dapk.st.settings.SettingItem.Id.* import app.dapk.st.settings.SettingItem.Id.*
import app.dapk.st.settings.SettingsEvent.* import app.dapk.st.settings.SettingsEvent.*
import app.dapk.state.Combined2 import app.dapk.state.*
import app.dapk.state.async import app.dapk.state.page.*
import app.dapk.state.createReducer
import app.dapk.state.multi
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -1,10 +1,6 @@
package app.dapk.st.settings package app.dapk.st.settings
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.page.PageAction
import app.dapk.st.core.page.PageContainer
import app.dapk.st.core.page.PageStateChange
import app.dapk.st.design.components.SpiderPage
import app.dapk.st.engine.ImportResult import app.dapk.st.engine.ImportResult
import app.dapk.st.push.Registrar import app.dapk.st.push.Registrar
import app.dapk.st.settings.state.ComponentLifecycle import app.dapk.st.settings.state.ComponentLifecycle
@ -12,6 +8,10 @@ import app.dapk.st.settings.state.RootActions
import app.dapk.st.settings.state.ScreenAction import app.dapk.st.settings.state.ScreenAction
import app.dapk.st.settings.state.settingsReducer import app.dapk.st.settings.state.settingsReducer
import app.dapk.state.Combined2 import app.dapk.state.Combined2
import app.dapk.state.SpiderPage
import app.dapk.state.page.PageAction
import app.dapk.state.page.PageContainer
import app.dapk.state.page.PageStateChange
import fake.* import fake.*
import fixture.aRoomId import fixture.aRoomId
import internalfake.FakeSettingsItemFactory import internalfake.FakeSettingsItemFactory

View File

@ -1,7 +1,7 @@
package internalfixture package internalfixture
import app.dapk.st.design.components.SpiderPage
import app.dapk.st.settings.Page import app.dapk.st.settings.Page
import app.dapk.state.SpiderPage
internal fun aImportRoomKeysPage( internal fun aImportRoomKeysPage(
state: Page.ImportRoomKey = Page.ImportRoomKey() state: Page.ImportRoomKey = Page.ImportRoomKey()

1
screen-state Submodule

@ -0,0 +1 @@
Subproject commit 4e92f14031cc8be907cba09b3bfc1d9dbd380072

View File

@ -6,6 +6,9 @@ dependencyResolutionManagement {
} }
} }
rootProject.name = "SmallTalk" rootProject.name = "SmallTalk"
includeBuild 'screen-state'
include ':app' include ':app'
include ':design-library' include ':design-library'