diff --git a/examples/todoapp/android/build.gradle.kts b/examples/todoapp/android/build.gradle.kts index 6b240e109b..6d17b3ac4d 100755 --- a/examples/todoapp/android/build.gradle.kts +++ b/examples/todoapp/android/build.gradle.kts @@ -36,4 +36,5 @@ dependencies { implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinLogging) implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinTimeTravel) implementation(Deps.ArkIvanov.Decompose.decompose) + implementation(Deps.ArkIvanov.Decompose.extensionsCompose) } diff --git a/examples/todoapp/android/src/main/java/example/todo/android/MainActivity.kt b/examples/todoapp/android/src/main/java/example/todo/android/MainActivity.kt index 8b4d6a9026..b9faacd8c5 100755 --- a/examples/todoapp/android/src/main/java/example/todo/android/MainActivity.kt +++ b/examples/todoapp/android/src/main/java/example/todo/android/MainActivity.kt @@ -5,11 +5,8 @@ import androidx.appcompat.app.AppCompatActivity import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.ui.platform.setContent -import com.arkivanov.decompose.DefaultComponentContext -import com.arkivanov.decompose.backpressed.toBackPressedDispatched -import com.arkivanov.decompose.instancekeeper.toInstanceKeeper -import com.arkivanov.decompose.lifecycle.asDecomposeLifecycle -import com.arkivanov.decompose.statekeeper.toStateKeeper +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.jetbrains.rootComponent import com.arkivanov.mvikotlin.core.store.StoreFactory import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory @@ -22,27 +19,22 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val todoRoot = - TodoRoot( - componentContext = DefaultComponentContext( - lifecycle = lifecycle.asDecomposeLifecycle(), - stateKeeper = savedStateRegistry.toStateKeeper(), - instanceKeeper = viewModelStore.toInstanceKeeper(), - backPressedDispatcher = onBackPressedDispatcher.toBackPressedDispatched(lifecycle) - ), - dependencies = object : TodoRoot.Dependencies { - // You can play with time travel using IDEA plugin: https://arkivanov.github.io/MVIKotlin/time_travel.html - override val storeFactory: StoreFactory = LoggingStoreFactory(TimeTravelStoreFactory(DefaultStoreFactory)) - override val database: TodoDatabase = TodoDatabase(TodoDatabaseDriver(this@MainActivity)) - } - ) - setContent { ComposeAppTheme { Surface(color = MaterialTheme.colors.background) { - todoRoot() + rootComponent(::todoRoot).invoke() } } } } + + private fun todoRoot(componentContext: ComponentContext): TodoRoot = + TodoRoot( + componentContext = componentContext, + dependencies = object : TodoRoot.Dependencies { + // You can play with time travel using IDEA plugin: https://arkivanov.github.io/MVIKotlin/time_travel.html + override val storeFactory: StoreFactory = LoggingStoreFactory(TimeTravelStoreFactory(DefaultStoreFactory)) + override val database: TodoDatabase = TodoDatabase(TodoDatabaseDriver(this@MainActivity)) + } + ) } diff --git a/examples/todoapp/buildSrc/buildSrc/src/main/kotlin/Deps.kt b/examples/todoapp/buildSrc/buildSrc/src/main/kotlin/Deps.kt index dcb821dcef..30ac0e33ba 100644 --- a/examples/todoapp/buildSrc/buildSrc/src/main/kotlin/Deps.kt +++ b/examples/todoapp/buildSrc/buildSrc/src/main/kotlin/Deps.kt @@ -35,8 +35,9 @@ object Deps { } object Decompose { - private const val VERSION = "0.1.0" + private const val VERSION = "0.1.1" const val decompose = "com.arkivanov.decompose:decompose:$VERSION" + const val extensionsCompose = "com.arkivanov.decompose:extensions-compose-jetbrains:$VERSION" } } diff --git a/examples/todoapp/common/edit/src/commonMain/kotlin/example/todo/common/edit/ui/TodoEditUi.kt b/examples/todoapp/common/edit/src/commonMain/kotlin/example/todo/common/edit/ui/TodoEditUi.kt index ed0c97b1e4..63811e39f6 100644 --- a/examples/todoapp/common/edit/src/commonMain/kotlin/example/todo/common/edit/ui/TodoEditUi.kt +++ b/examples/todoapp/common/edit/src/commonMain/kotlin/example/todo/common/edit/ui/TodoEditUi.kt @@ -1,6 +1,5 @@ package example.todo.common.edit.ui -import androidx.compose.foundation.Icon import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -8,6 +7,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.Checkbox +import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.material.TextField diff --git a/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt b/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt index 527f104100..80457f823c 100644 --- a/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt +++ b/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumnFor import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.Button import androidx.compose.material.Checkbox import androidx.compose.material.Divider import androidx.compose.material.Icon @@ -19,6 +18,7 @@ import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Delete import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -147,8 +147,10 @@ private fun TodoInput( label = { Text(text = "Add a todo") } ) - Button(modifier = Modifier.padding(start = 8.dp), onClick = onAddClicked) { - Text(text = "+") + Spacer(modifier = Modifier.width(8.dp)) + + IconButton(onClick = onAddClicked) { + Icon(Icons.Default.Add) } } } diff --git a/examples/todoapp/common/root/build.gradle.kts b/examples/todoapp/common/root/build.gradle.kts index 372d253717..8eddad4b52 100755 --- a/examples/todoapp/common/root/build.gradle.kts +++ b/examples/todoapp/common/root/build.gradle.kts @@ -17,6 +17,7 @@ kotlin { implementation(project(":common:main")) implementation(project(":common:edit")) implementation(Deps.ArkIvanov.Decompose.decompose) + implementation(Deps.ArkIvanov.Decompose.extensionsCompose) implementation(Deps.Badoo.Reaktive.reaktive) } } diff --git a/examples/todoapp/common/root/src/commonMain/kotlin/example/todo/common/root/integration/TodoRootImpl.kt b/examples/todoapp/common/root/src/commonMain/kotlin/example/todo/common/root/integration/TodoRootImpl.kt index 413e65fdf5..53a38f3e96 100644 --- a/examples/todoapp/common/root/src/commonMain/kotlin/example/todo/common/root/integration/TodoRootImpl.kt +++ b/examples/todoapp/common/root/src/commonMain/kotlin/example/todo/common/root/integration/TodoRootImpl.kt @@ -2,6 +2,7 @@ package example.todo.common.root.integration import androidx.compose.runtime.Composable import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.jetbrains.children import com.arkivanov.decompose.router import com.arkivanov.decompose.statekeeper.Parcelable import com.arkivanov.decompose.statekeeper.Parcelize @@ -13,7 +14,6 @@ import example.todo.common.root.TodoRoot.Dependencies import example.todo.common.utils.Component import example.todo.common.utils.Consumer import example.todo.common.utils.Crossfade -import example.todo.common.utils.children internal class TodoRootImpl( componentContext: ComponentContext, diff --git a/examples/todoapp/common/utils/build.gradle.kts b/examples/todoapp/common/utils/build.gradle.kts index a248f52c6e..4eac17a3a6 100755 --- a/examples/todoapp/common/utils/build.gradle.kts +++ b/examples/todoapp/common/utils/build.gradle.kts @@ -10,6 +10,7 @@ kotlin { implementation(Deps.ArkIvanov.MVIKotlin.mvikotlin) implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinExtensionsReaktive) implementation(Deps.ArkIvanov.Decompose.decompose) + implementation(Deps.ArkIvanov.Decompose.extensionsCompose) } } } diff --git a/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/RouterStateComposable.kt b/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/RouterStateComposable.kt deleted file mode 100644 index f323de4d93..0000000000 --- a/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/RouterStateComposable.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copied from Decompose - */ - -package example.todo.common.utils - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Providers -import androidx.compose.runtime.onDispose -import androidx.compose.runtime.remember -import androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry -import androidx.compose.runtime.savedinstancestate.UiSavedStateRegistryAmbient -import com.arkivanov.decompose.RouterState -import com.arkivanov.decompose.statekeeper.Parcelable -import com.arkivanov.decompose.value.Value - -private typealias SavedState = Map> - -@Composable -fun Value>.children(render: @Composable() (child: T, configuration: C) -> Unit) { - val parentRegistry: UiSavedStateRegistry? = UiSavedStateRegistryAmbient.current - val children = remember(value) { Children() } - - if (parentRegistry != null) { - onDispose { - children.inactive.entries.forEach { (key, value) -> - parentRegistry.unregisterProvider(key, value.provider) - } - children.active?.also { - parentRegistry.unregisterProvider(it.key, it.provider) - } - } - } - - observe { state -> - val activeChildConfiguration = state.activeChild.configuration - - val currentChild: ActiveChild? = children.active - if ((currentChild != null) && state.backStack.any { it.configuration === currentChild.configuration }) { - parentRegistry?.unregisterProvider(currentChild.key, currentChild.provider) - val inactiveChild = InactiveChild(configuration = currentChild.configuration, savedState = currentChild.provider()) - children.inactive[currentChild.key] = inactiveChild - parentRegistry?.registerProvider(currentChild.key, inactiveChild.provider) - } - - val activeChildRegistry: UiSavedStateRegistry - - if (currentChild?.configuration === activeChildConfiguration) { - activeChildRegistry = currentChild.registry - } else { - val key = activeChildConfiguration.toString() - - val savedChild: InactiveChild? = children.inactive.remove(key) - if (savedChild != null) { - parentRegistry?.unregisterProvider(key, savedChild.provider) - } - @Suppress("UNCHECKED_CAST") - val savedState: SavedState? = savedChild?.savedState ?: parentRegistry?.consumeRestored(key) as SavedState? - - activeChildRegistry = UiSavedStateRegistry(savedState) { true } - - val newActiveChild = ActiveChild(configuration = activeChildConfiguration, key = key, registry = activeChildRegistry) - children.active = newActiveChild - parentRegistry?.registerProvider(key, newActiveChild.provider) - } - - children.inactive.entries.removeAll { (key, value) -> - val remove = state.backStack.none { it.configuration === value.configuration } - if (remove) { - parentRegistry?.unregisterProvider(key, value.provider) - } - remove - } - - Providers(UiSavedStateRegistryAmbient provides activeChildRegistry) { - render(state.activeChild.component, activeChildConfiguration) - } - } -} - -private class Children { - val inactive: MutableMap> = HashMap() - var active: ActiveChild? = null -} - -private class ActiveChild( - val configuration: C, - val key: String, - val registry: UiSavedStateRegistry -) { - val provider: () -> SavedState = registry::performSave -} - -private class InactiveChild( - val configuration: C, - val savedState: SavedState -) { - val provider: () -> SavedState = ::savedState -} diff --git a/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/StoreExt.kt b/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/StoreExt.kt index 8e91d84670..99e7451399 100644 --- a/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/StoreExt.kt +++ b/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/StoreExt.kt @@ -13,7 +13,7 @@ import com.badoo.reaktive.observable.subscribe val Store<*, T, *>.composeState: State get() { val composeState = remember(this) { mutableStateOf(state) } - val disposable = states.subscribe(onNext = { composeState.value = it }) + val disposable = remember(this) { states.subscribe(onNext = { composeState.value = it }) } onDispose(disposable::dispose) return composeState diff --git a/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/ValueComposable.kt b/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/ValueComposable.kt deleted file mode 100644 index 528831c193..0000000000 --- a/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/ValueComposable.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copied from Decompose - */ - -package example.todo.common.utils - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.onDispose -import androidx.compose.runtime.remember -import com.arkivanov.decompose.value.Value -import com.arkivanov.decompose.value.ValueObserver - -@Composable -fun Value.asState(): State { - val composeState = remember(this) { mutableStateOf(value) } - - val observer = - remember(this) { - val observer: ValueObserver = { composeState.value = it } - subscribe(observer) - observer - } - - onDispose { unsubscribe(observer) } - - return composeState -} - -@Composable -fun Value.observe(observer: @Composable() (T) -> Unit) { - observer(asState().value) -} diff --git a/examples/todoapp/desktop/build.gradle.kts b/examples/todoapp/desktop/build.gradle.kts index 8d2ebe1944..3eb6ab586d 100755 --- a/examples/todoapp/desktop/build.gradle.kts +++ b/examples/todoapp/desktop/build.gradle.kts @@ -19,6 +19,7 @@ kotlin { implementation(project(":common:database")) implementation(project(":common:root")) implementation(Deps.ArkIvanov.Decompose.decompose) + implementation(Deps.ArkIvanov.Decompose.extensionsCompose) implementation(Deps.ArkIvanov.MVIKotlin.mvikotlin) implementation(Deps.ArkIvanov.MVIKotlin.mvikotlinMain) implementation(Deps.Badoo.Reaktive.reaktive) diff --git a/examples/todoapp/desktop/src/jvmMain/kotlin/example/todo/desktop/Main.kt b/examples/todoapp/desktop/src/jvmMain/kotlin/example/todo/desktop/Main.kt index 5969f242d6..53f4e8611e 100644 --- a/examples/todoapp/desktop/src/jvmMain/kotlin/example/todo/desktop/Main.kt +++ b/examples/todoapp/desktop/src/jvmMain/kotlin/example/todo/desktop/Main.kt @@ -6,7 +6,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.ui.Modifier -import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.jetbrains.rootComponent import com.arkivanov.decompose.lifecycle.LifecycleRegistry import com.arkivanov.decompose.lifecycle.resume import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory @@ -23,21 +24,22 @@ fun main() { val lifecycle = LifecycleRegistry() lifecycle.resume() - val todoRoot = TodoRoot( - componentContext = DefaultComponentContext(lifecycle), - dependencies = object : TodoRoot.Dependencies { - override val storeFactory = DefaultStoreFactory - override val database = TodoDatabase(TodoDatabaseDriver()) - } - ) - Window("Todo") { Surface(modifier = Modifier.fillMaxSize()) { MaterialTheme { DesktopTheme { - todoRoot() + rootComponent(factory = ::todoRoot).invoke() } } } } } + +private fun todoRoot(componentContext: ComponentContext): TodoRoot = + TodoRoot( + componentContext = componentContext, + dependencies = object : TodoRoot.Dependencies { + override val storeFactory = DefaultStoreFactory + override val database = TodoDatabase(TodoDatabaseDriver()) + } + )