From e56784050aa1ec7882276b7b1a1712247a2a7f1d Mon Sep 17 00:00:00 2001 From: Diego Beraldin Date: Sat, 22 Jul 2023 22:07:55 +0200 Subject: [PATCH] chore(architecture): introduce MVI architecture --- core-architecture/build.gradle.kts | 54 +++++++++++++++++ core-architecture/core-architecture.podspec | 42 +++++++++++++ core-architecture/core_architecture.podspec | 39 ++++++++++++ .../core_architecture/DefaultMviModel.kt | 59 +++++++++++++++++++ .../core_architecture/MviModel.kt | 38 ++++++++++++ .../core_architecture/Utils.kt | 12 ++++ feature-home/build.gradle.kts | 1 + .../feature_home/HomeModule.kt | 18 ++++++ .../feature_home/HomeScreenModel.kt | 8 --- .../feature_home/HomeModule.kt | 9 ++- .../feature_home/HomeScreenModel.kt | 12 ++-- .../feature_home/HomeScreenMviModel.kt | 15 +++++ .../raccoonforlemmy/feature_home/HomeTab.kt | 7 +-- .../{HomeScreenModel.kt => HomeModule.kt} | 10 ++++ feature-inbox/build.gradle.kts | 1 + .../feature_inbox/InboxModule.kt | 18 ++++++ .../feature_inbox/InboxScreenModel.kt | 8 --- .../feature_inbox/InboxModule.kt | 9 ++- .../feature_inbox/InboxScreenModel.kt | 9 ++- .../feature_inbox/InboxScreenMviModel.kt | 13 ++++ .../raccoonforlemmy/feature_inbox/InboxTab.kt | 3 + ...boxScreenModel.kt => InboxScreenModule.kt} | 10 ++++ feature-profile/build.gradle.kts | 1 + .../feature_profile/ProfileModule.kt | 18 ++++++ .../feature_profile/ProfileScreenModel.kt | 8 --- .../feature_profile/ProfileModule.kt | 9 ++- .../feature_profile/ProfileScreenModel.kt | 9 ++- .../feature_profile/ProfileScreenMviModel.kt | 13 ++++ .../feature_profile/ProfileTab.kt | 3 + ...ProfileScreenModel.kt => ProfileModule.kt} | 10 ++++ feature-search/build.gradle.kts | 1 + .../feature_search/SearchModule.kt | 18 ++++++ .../feature_search/SearchScreenModel.kt | 8 --- .../feature_search/SearchModule.kt | 11 ++-- .../feature_search/SearchScreenModel.kt | 11 ++-- .../feature_search/SearchScreenMviModel.kt | 12 ++++ .../feature_search/SearchTab.kt | 3 + .../{SearchScreenModel.kt => SearchModule.kt} | 10 ++++ feature-settings/build.gradle.kts | 2 +- .../feature_settings/SettingsModule.kt | 25 ++++++++ .../feature_settings/SettingsScreenModel.kt | 8 --- .../feature_settings/SettingsModule.kt | 9 ++- .../feature_settings/SettingsScreenModel.kt | 37 ++++++------ .../SettingsScreenMviModel.kt | 17 ++++++ .../feature_settings/SettingsScreenUiState.kt | 5 -- .../feature_settings/SettingsTab.kt | 6 +- .../feature_settings/SettingsModule.kt | 24 ++++++++ .../feature_settings/SettingsScreenModel.kt | 10 ---- settings.gradle.kts | 1 + 49 files changed, 563 insertions(+), 121 deletions(-) create mode 100644 core-architecture/build.gradle.kts create mode 100644 core-architecture/core-architecture.podspec create mode 100644 core-architecture/core_architecture.podspec create mode 100644 core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/DefaultMviModel.kt create mode 100644 core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/MviModel.kt create mode 100644 core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/Utils.kt create mode 100644 feature-home/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt delete mode 100644 feature-home/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt create mode 100644 feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenMviModel.kt rename feature-home/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/{HomeScreenModel.kt => HomeModule.kt} (52%) create mode 100644 feature-inbox/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxModule.kt delete mode 100644 feature-inbox/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt create mode 100644 feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenMviModel.kt rename feature-inbox/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/{InboxScreenModel.kt => InboxScreenModule.kt} (52%) create mode 100644 feature-profile/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt delete mode 100644 feature-profile/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt create mode 100644 feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenMviModel.kt rename feature-profile/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/{ProfileScreenModel.kt => ProfileModule.kt} (53%) create mode 100644 feature-search/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt delete mode 100644 feature-search/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt create mode 100644 feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenMviModel.kt rename feature-search/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/{SearchScreenModel.kt => SearchModule.kt} (53%) create mode 100644 feature-settings/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt delete mode 100644 feature-settings/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt create mode 100644 feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenMviModel.kt delete mode 100644 feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenUiState.kt create mode 100644 feature-settings/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt delete mode 100644 feature-settings/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt diff --git a/core-architecture/build.gradle.kts b/core-architecture/build.gradle.kts new file mode 100644 index 000000000..15a180a96 --- /dev/null +++ b/core-architecture/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.android.library) + alias(libs.plugins.compose) + alias(libs.plugins.native.cocoapods) +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) +kotlin { + targetHierarchy.default() + + android { + compilations.all { + kotlinOptions { + jvmTarget = "1.8" + } + } + } + iosX64() + iosArm64() + iosSimulatorArm64() + + cocoapods { + summary = "Some description for the Shared Module" + homepage = "Link to the Shared Module homepage" + version = "1.0" + ios.deploymentTarget = "14.1" + framework { + baseName = "core-architecture" + } + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + } +} + +android { + namespace = "com.github.diegoberaldin.raccoonforlemmy.core_architecture" + compileSdk = 33 + defaultConfig { + minSdk = 26 + } +} \ No newline at end of file diff --git a/core-architecture/core-architecture.podspec b/core-architecture/core-architecture.podspec new file mode 100644 index 000000000..c88a1c8a3 --- /dev/null +++ b/core-architecture/core-architecture.podspec @@ -0,0 +1,42 @@ +Pod::Spec.new do |spec| + spec.name = 'core-architecture' + spec.version = '1.0' + spec.homepage = 'Link to the Shared Module homepage' + spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" } + spec.authors = '' + spec.license = '' + spec.summary = 'Some description for the Shared Module' + + spec.vendored_frameworks = "build/cocoapods/framework/core-architecture.framework" + spec.libraries = "c++" + spec.module_name = "#{spec.name}_umbrella" + + spec.ios.deployment_target = '14.1' + + + + spec.pod_target_xcconfig = { + 'KOTLIN_PROJECT_PATH' => ':core-architecture', + 'PRODUCT_MODULE_NAME' => 'core-architecture', + } + + spec.script_phases = [ + { + :name => 'Build core-architecture', + :execution_position => :before_compile, + :shell_path => '/bin/sh', + :script => <<-SCRIPT + if [ "YES" = "$COCOAPODS_SKIP_KOTLIN_BUILD" ]; then + echo "Skipping Gradle build task invocation due to COCOAPODS_SKIP_KOTLIN_BUILD environment variable set to \"YES\"" + exit 0 + fi + set -ev + REPO_ROOT="$PODS_TARGET_SRCROOT" + "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ + -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ + -Pkotlin.native.cocoapods.archs="$ARCHS" \ + -Pkotlin.native.cocoapods.configuration=$CONFIGURATION + SCRIPT + } + ] +end \ No newline at end of file diff --git a/core-architecture/core_architecture.podspec b/core-architecture/core_architecture.podspec new file mode 100644 index 000000000..e1a4d89bc --- /dev/null +++ b/core-architecture/core_architecture.podspec @@ -0,0 +1,39 @@ +Pod::Spec.new do |spec| + spec.name = 'core_architecture' + spec.version = '1.0' + spec.homepage = 'Link to the Shared Module homepage' + spec.source = { :http=> ''} + spec.authors = '' + spec.license = '' + spec.summary = 'Some description for the Shared Module' + spec.vendored_frameworks = 'build/cocoapods/framework/core-architecture.framework' + spec.libraries = 'c++' + spec.ios.deployment_target = '14.1' + + + spec.pod_target_xcconfig = { + 'KOTLIN_PROJECT_PATH' => ':core-architecture', + 'PRODUCT_MODULE_NAME' => 'core-architecture', + } + + spec.script_phases = [ + { + :name => 'Build core_architecture', + :execution_position => :before_compile, + :shell_path => '/bin/sh', + :script => <<-SCRIPT + if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then + echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" + exit 0 + fi + set -ev + REPO_ROOT="$PODS_TARGET_SRCROOT" + "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ + -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ + -Pkotlin.native.cocoapods.archs="$ARCHS" \ + -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" + SCRIPT + } + ] + +end \ No newline at end of file diff --git a/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/DefaultMviModel.kt b/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/DefaultMviModel.kt new file mode 100644 index 000000000..809be97a3 --- /dev/null +++ b/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/DefaultMviModel.kt @@ -0,0 +1,59 @@ +package com.github.diegoberaldin.raccoonforlemmy.core_architecture + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update + +/** + * Basic implementation of the MVI model. This is useful to easily implement the interface by delegation, + * minimizing the amount of code that is needed when integrating the MVI pattern. + * The [updateState] and [emitEffect] methods are shortcuts to easily update the UI state and emit a side effect. + * + * @param Intent class of intents + * @param State class of UI state + * @param Effect class of effects + * @constructor Create [DefaultMviModel] + * + * @param initialState initial UI state + */ +class DefaultMviModel( + initialState: State, +) : MviModel { + + override val uiState = MutableStateFlow(initialState) + override val effects = MutableSharedFlow() + lateinit var scope: CoroutineScope + + /** + * Emit an effect (event). + * + * @param value Value + */ + suspend fun emitEffect(value: Effect) { + effects.emit(value) + } + + /** + * Update the UI state. + * + * @param block Block + */ + inline fun updateState(block: (State) -> State) { + uiState.update { block(uiState.value) } + } + + override fun reduce(intent: Intent) { + // Noop + } + + override fun onStarted() { + scope = CoroutineScope(SupervisorJob()) + } + + override fun onDisposed() { + scope.cancel() + } +} diff --git a/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/MviModel.kt b/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/MviModel.kt new file mode 100644 index 000000000..3a38060ac --- /dev/null +++ b/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/MviModel.kt @@ -0,0 +1,38 @@ +package com.github.diegoberaldin.raccoonforlemmy.core_architecture + +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow + +/** + * Model contract for Model-View-Intent architecture. + */ +interface MviModel { + + /** + * Representation of the state holder's state for the view to consume. + */ + val uiState: StateFlow + + /** + * One-shot events generated by the state holder. + */ + val effects: SharedFlow + + /** + * Reduce a view intent updating the [uiState] accordingly. + * + * @param intent View intent to process + */ + fun reduce(intent: Intent) + + /** + * To be called whenever the view component becomes visible to start listening events, + * initialize the coroutine scope, etc. + */ + fun onStarted() + + /** + * To be called wheneer the view component is not visible any more to cancel ongoing operations. + */ + fun onDisposed() +} diff --git a/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/Utils.kt b/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/Utils.kt new file mode 100644 index 000000000..351c0bc28 --- /dev/null +++ b/core-architecture/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core_architecture/Utils.kt @@ -0,0 +1,12 @@ +package com.github.diegoberaldin.raccoonforlemmy.core_architecture + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect + +@Composable +fun MviModel<*, *, *>.bindToLifecycle(key: Any = Unit) { + DisposableEffect(key) { + onStarted() + onDispose(::onDisposed) + } +} \ No newline at end of file diff --git a/feature-home/build.gradle.kts b/feature-home/build.gradle.kts index 413ca6280..b1de6251b 100644 --- a/feature-home/build.gradle.kts +++ b/feature-home/build.gradle.kts @@ -45,6 +45,7 @@ kotlin { implementation(libs.voyager.tab) implementation(projects.resources) + implementation(projects.coreArchitecture) } } val commonTest by getting { diff --git a/feature-home/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt b/feature-home/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt new file mode 100644 index 000000000..089da6fae --- /dev/null +++ b/feature-home/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt @@ -0,0 +1,18 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_home + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import org.koin.dsl.module +import org.koin.java.KoinJavaComponent.inject + +actual val homeTabModule = module { + factory { + HomeScreenModel( + mvi = DefaultMviModel(HomeScreenMviModel.UiState()) + ) + } +} + +actual fun getHomeScreenModel(): HomeScreenModel { + val res: HomeScreenModel by inject(HomeScreenModel::class.java) + return res +} \ No newline at end of file diff --git a/feature-home/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt b/feature-home/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt deleted file mode 100644 index 4d4e2aa9b..000000000 --- a/feature-home/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.diegoberaldin.raccoonforlemmy.feature_home - -import org.koin.java.KoinJavaComponent.inject - -actual fun getHomeScreenModel(): HomeScreenModel { - val res: HomeScreenModel by inject(HomeScreenModel::class.java) - return res -} \ No newline at end of file diff --git a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt index 4dc8b4fe6..80abb4571 100644 --- a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt +++ b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt @@ -1,8 +1,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_home -import org.koin.core.module.dsl.factoryOf -import org.koin.dsl.module +import org.koin.core.module.Module -val homeTabModule = module { - factoryOf(::HomeScreenModel) -} \ No newline at end of file +expect val homeTabModule: Module + +expect fun getHomeScreenModel(): HomeScreenModel \ No newline at end of file diff --git a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt index 31b1cfdf3..55f1ace48 100644 --- a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt +++ b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt @@ -1,9 +1,11 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_home import cafe.adriel.voyager.core.model.ScreenModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel -class HomeScreenModel : ScreenModel { - -} - -expect fun getHomeScreenModel(): HomeScreenModel \ No newline at end of file +class HomeScreenModel( + private val mvi: DefaultMviModel, +) : ScreenModel, + MviModel by mvi { +} \ No newline at end of file diff --git a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenMviModel.kt b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenMviModel.kt new file mode 100644 index 000000000..050eb6b4e --- /dev/null +++ b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenMviModel.kt @@ -0,0 +1,15 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_home + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel + +interface HomeScreenMviModel : + MviModel { + + sealed interface Intent + + data class UiState( + val loading: Boolean = false, + ) + + sealed interface Effect +} \ No newline at end of file diff --git a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeTab.kt b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeTab.kt index a47f6f0f2..ad8fac08e 100644 --- a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeTab.kt +++ b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeTab.kt @@ -6,9 +6,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Home import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter @@ -16,8 +13,8 @@ import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.TabOptions +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.bindToLifecycle import com.github.diegoberaldin.raccoonforlemmy.resources.MR -import com.github.diegoberaldin.raccoonforlemmy.resources.getLanguageRepository import dev.icerock.moko.resources.compose.stringResource object HomeTab : Tab { @@ -40,6 +37,8 @@ object HomeTab : Tab { @Composable override fun Content() { val model = rememberScreenModel { getHomeScreenModel() } + model.bindToLifecycle(key) + Column(modifier = Modifier.padding(4.dp)) { Text( text = "Posts content" diff --git a/feature-home/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt b/feature-home/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt similarity index 52% rename from feature-home/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt rename to feature-home/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt index 0d3427264..a1b33d0f9 100644 --- a/feature-home/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeScreenModel.kt +++ b/feature-home/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_home/HomeModule.kt @@ -1,7 +1,17 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_home +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import org.koin.dsl.module + +actual val homeTabModule = module { + factory { + HomeScreenModel( + mvi = DefaultMviModel(HomeScreenMviModel.UiState()) + ) + } +} actual fun getHomeScreenModel() = HomeScreenModelHelper.model diff --git a/feature-inbox/build.gradle.kts b/feature-inbox/build.gradle.kts index f01c4562a..316a7219c 100644 --- a/feature-inbox/build.gradle.kts +++ b/feature-inbox/build.gradle.kts @@ -45,6 +45,7 @@ kotlin { implementation(libs.voyager.tab) implementation(projects.resources) + implementation(projects.coreArchitecture) } } val commonTest by getting { diff --git a/feature-inbox/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxModule.kt b/feature-inbox/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxModule.kt new file mode 100644 index 000000000..1ec640fe5 --- /dev/null +++ b/feature-inbox/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxModule.kt @@ -0,0 +1,18 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_inbox + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import org.koin.dsl.module +import org.koin.java.KoinJavaComponent.inject + +actual val inboxTabModule = module { + factory { + InboxScreenModel( + mvi = DefaultMviModel(InboxScreenMviModel.UiState()) + ) + } +} + +actual fun getInboxScreenModel(): InboxScreenModel { + val res: InboxScreenModel by inject(InboxScreenModel::class.java) + return res +} \ No newline at end of file diff --git a/feature-inbox/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt b/feature-inbox/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt deleted file mode 100644 index e2462b959..000000000 --- a/feature-inbox/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.diegoberaldin.raccoonforlemmy.feature_inbox - -import org.koin.java.KoinJavaComponent.inject - -actual fun getInboxScreenModel(): InboxScreenModel { - val res: InboxScreenModel by inject(InboxScreenModel::class.java) - return res -} \ No newline at end of file diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxModule.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxModule.kt index 4f614aa65..49d9d4581 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxModule.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxModule.kt @@ -1,8 +1,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_inbox -import org.koin.core.module.dsl.factoryOf -import org.koin.dsl.module +import org.koin.core.module.Module -val inboxTabModule = module { - factoryOf(::InboxScreenModel) -} \ No newline at end of file +expect val inboxTabModule: Module + +expect fun getInboxScreenModel(): InboxScreenModel \ No newline at end of file diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt index d6d215423..8c22f5566 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt @@ -1,9 +1,12 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_inbox import cafe.adriel.voyager.core.model.ScreenModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel -class InboxScreenModel : ScreenModel { +class InboxScreenModel( + private val mvi: DefaultMviModel, +) : ScreenModel, + MviModel by mvi { } - -expect fun getInboxScreenModel(): InboxScreenModel \ No newline at end of file diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenMviModel.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenMviModel.kt new file mode 100644 index 000000000..04a00fa22 --- /dev/null +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenMviModel.kt @@ -0,0 +1,13 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_inbox + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel + +interface InboxScreenMviModel : + MviModel { + + sealed interface Intent + + data class UiState(val loading: Boolean = false) + + sealed interface Effect +} \ No newline at end of file diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxTab.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxTab.kt index 17811a1cf..1ae1cd473 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxTab.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxTab.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.TabOptions +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.bindToLifecycle import com.github.diegoberaldin.raccoonforlemmy.resources.MR import dev.icerock.moko.resources.compose.stringResource @@ -36,6 +37,8 @@ object InboxTab : Tab { @Composable override fun Content() { val model = rememberScreenModel { getInboxScreenModel() } + model.bindToLifecycle(key) + Column(modifier = Modifier.padding(4.dp)) { Text( text = "Inbox content" diff --git a/feature-inbox/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt b/feature-inbox/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModule.kt similarity index 52% rename from feature-inbox/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt rename to feature-inbox/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModule.kt index 496497c2b..6923ee6d9 100644 --- a/feature-inbox/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModel.kt +++ b/feature-inbox/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_inbox/InboxScreenModule.kt @@ -1,7 +1,17 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_inbox +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import org.koin.dsl.module + +actual val inboxTabModule = module { + factory { + InboxScreenModel( + mvi = DefaultMviModel(InboxScreenMviModel.UiState()) + ) + } +} actual fun getInboxScreenModel() = InboxScreenModelHelper.model diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index c8908a7ce..1df30f096 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -45,6 +45,7 @@ kotlin { implementation(libs.voyager.tab) implementation(projects.resources) + implementation(projects.coreArchitecture) } } val commonTest by getting { diff --git a/feature-profile/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt b/feature-profile/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt new file mode 100644 index 000000000..f8ae7f4f4 --- /dev/null +++ b/feature-profile/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt @@ -0,0 +1,18 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_profile + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import org.koin.dsl.module +import org.koin.java.KoinJavaComponent.inject + +actual val profileTabModule = module { + factory { + ProfileScreenModel( + mvi = DefaultMviModel(ProfileScreenMviModel.UiState()) + ) + } +} + +actual fun getProfileScreenModel(): ProfileScreenModel { + val res: ProfileScreenModel by inject(ProfileScreenModel::class.java) + return res +} \ No newline at end of file diff --git a/feature-profile/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt b/feature-profile/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt deleted file mode 100644 index cfed76442..000000000 --- a/feature-profile/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.diegoberaldin.raccoonforlemmy.feature_profile - -import org.koin.java.KoinJavaComponent.inject - -actual fun getProfileScreenModel(): ProfileScreenModel { - val res: ProfileScreenModel by inject(ProfileScreenModel::class.java) - return res -} \ No newline at end of file diff --git a/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt b/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt index 3e8123916..b06dec621 100644 --- a/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt +++ b/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt @@ -1,8 +1,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_profile -import org.koin.core.module.dsl.factoryOf -import org.koin.dsl.module +import org.koin.core.module.Module -val profileTabModule = module { - factoryOf(::ProfileScreenModel) -} \ No newline at end of file +expect val profileTabModule: Module + +expect fun getProfileScreenModel(): ProfileScreenModel \ No newline at end of file diff --git a/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt b/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt index 7b887ead4..0325e03e4 100644 --- a/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt +++ b/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt @@ -1,9 +1,12 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_profile import cafe.adriel.voyager.core.model.ScreenModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel -class ProfileScreenModel : ScreenModel { +class ProfileScreenModel( + private val mvi: DefaultMviModel, +) : ScreenModel, + MviModel by mvi { } - -expect fun getProfileScreenModel(): ProfileScreenModel \ No newline at end of file diff --git a/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenMviModel.kt b/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenMviModel.kt new file mode 100644 index 000000000..2d4c5b38e --- /dev/null +++ b/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenMviModel.kt @@ -0,0 +1,13 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_profile + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel + +interface ProfileScreenMviModel : + MviModel { + + sealed interface Intent + + data class UiState(val loading: Boolean = false) + + sealed interface Effect +} \ No newline at end of file diff --git a/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileTab.kt b/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileTab.kt index e2decc2e2..fbea9c89e 100644 --- a/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileTab.kt +++ b/feature-profile/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileTab.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.TabOptions +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.bindToLifecycle import com.github.diegoberaldin.raccoonforlemmy.resources.MR import dev.icerock.moko.resources.compose.stringResource @@ -36,6 +37,8 @@ object ProfileTab : Tab { @Composable override fun Content() { val model = rememberScreenModel { getProfileScreenModel() } + model.bindToLifecycle(key) + Column(modifier = Modifier.padding(4.dp)) { Text( text = "Profile content" diff --git a/feature-profile/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt b/feature-profile/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt similarity index 53% rename from feature-profile/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt rename to feature-profile/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt index 22b3eeed2..7699b4f87 100644 --- a/feature-profile/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileScreenModel.kt +++ b/feature-profile/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_profile/ProfileModule.kt @@ -1,7 +1,17 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_profile +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import org.koin.dsl.module + +actual val profileTabModule = module { + factory { + ProfileScreenModel( + mvi = DefaultMviModel(ProfileScreenMviModel.UiState()) + ) + } +} actual fun getProfileScreenModel() = ProfileScreenModelHelper.model diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index 268cbf6b6..fc54669c0 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -45,6 +45,7 @@ kotlin { implementation(libs.voyager.tab) implementation(projects.resources) + implementation(projects.coreArchitecture) } } val commonTest by getting { diff --git a/feature-search/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt b/feature-search/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt new file mode 100644 index 000000000..9ddcb4298 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt @@ -0,0 +1,18 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_search + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import org.koin.dsl.module +import org.koin.java.KoinJavaComponent.inject + +actual val searchTabModule = module { + factory { + SearchScreenModel( + mvi = DefaultMviModel(SearchScreenMviModel.UiState()) + ) + } +} + +actual fun getSearchScreenModel(): SearchScreenModel { + val res: SearchScreenModel by inject(SearchScreenModel::class.java) + return res +} \ No newline at end of file diff --git a/feature-search/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt b/feature-search/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt deleted file mode 100644 index f3373c952..000000000 --- a/feature-search/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.diegoberaldin.raccoonforlemmy.feature_search - -import org.koin.java.KoinJavaComponent.inject - -actual fun getSearchScreenModel(): SearchScreenModel { - val res: SearchScreenModel by inject(SearchScreenModel::class.java) - return res -} \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt b/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt index 393828d44..72a461e46 100644 --- a/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt +++ b/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt @@ -1,8 +1,9 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_search -import org.koin.core.module.dsl.factoryOf -import org.koin.dsl.module +import org.koin.core.module.Module + +expect val searchTabModule: Module + +expect fun getSearchScreenModel(): SearchScreenModel + -val searchTabModule = module { - factoryOf(::SearchScreenModel) -} \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt b/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt index 3bd5c772e..75037980c 100644 --- a/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt +++ b/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt @@ -1,9 +1,12 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_search import cafe.adriel.voyager.core.model.ScreenModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel -class SearchScreenModel : ScreenModel { +class SearchScreenModel( + private val mvi: DefaultMviModel, +) : ScreenModel, + MviModel by mvi { -} - -expect fun getSearchScreenModel(): SearchScreenModel \ No newline at end of file +} \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenMviModel.kt b/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenMviModel.kt new file mode 100644 index 000000000..0cbe24cd8 --- /dev/null +++ b/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenMviModel.kt @@ -0,0 +1,12 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_search + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel + +interface SearchScreenMviModel : + MviModel { + sealed interface Intent + + data class UiState(val loading: Boolean = false) + + sealed interface Effect +} \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchTab.kt b/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchTab.kt index e7ffbb29f..4618e831f 100644 --- a/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchTab.kt +++ b/feature-search/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchTab.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.TabOptions +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.bindToLifecycle import com.github.diegoberaldin.raccoonforlemmy.resources.MR import dev.icerock.moko.resources.compose.stringResource @@ -36,6 +37,8 @@ object SearchTab : Tab { @Composable override fun Content() { val model = rememberScreenModel { getSearchScreenModel() } + model.bindToLifecycle(key) + Column(modifier = Modifier.padding(4.dp)) { Text( text = "Search content" diff --git a/feature-search/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt b/feature-search/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt similarity index 53% rename from feature-search/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt rename to feature-search/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt index 4b94c921a..0a0e3ac36 100644 --- a/feature-search/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchScreenModel.kt +++ b/feature-search/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_search/SearchModule.kt @@ -1,7 +1,17 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_search +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import org.koin.dsl.module + +actual val searchTabModule = module { + factory { + SearchScreenModel( + mvi = DefaultMviModel(SearchScreenMviModel.UiState()) + ) + } +} actual fun getSearchScreenModel() = SearchScreenModelHelper.model diff --git a/feature-settings/build.gradle.kts b/feature-settings/build.gradle.kts index c1b866f80..7710cea02 100644 --- a/feature-settings/build.gradle.kts +++ b/feature-settings/build.gradle.kts @@ -46,7 +46,7 @@ kotlin { implementation(projects.coreAppearance) implementation(projects.corePreferences) - + implementation(projects.coreArchitecture) implementation(projects.resources) } } diff --git a/feature-settings/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt b/feature-settings/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt new file mode 100644 index 000000000..24dd5774d --- /dev/null +++ b/feature-settings/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt @@ -0,0 +1,25 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_settings + +import com.github.diegoberaldin.raccoonforlemmy.core_appearance.repository.ThemeRepository +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import com.github.diegoberaldin.raccoonforlemmy.core_preferences.TemporaryKeyStore +import org.koin.dsl.module +import org.koin.java.KoinJavaComponent.get +import org.koin.java.KoinJavaComponent.inject + +actual val settingsTabModule = module { + factory { + SettingsScreenModel( + themeRepository = get(ThemeRepository::class.java), + keyStore = get(TemporaryKeyStore::class.java), + mvi = DefaultMviModel( + SettingsScreenMviModel.UiState() + ) + ) + } +} + +actual fun getSettingsScreenModel(): SettingsScreenModel { + val res: SettingsScreenModel by inject(SettingsScreenModel::class.java) + return res +} \ No newline at end of file diff --git a/feature-settings/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt b/feature-settings/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt deleted file mode 100644 index 35bae1611..000000000 --- a/feature-settings/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.diegoberaldin.raccoonforlemmy.feature_settings - -import org.koin.java.KoinJavaComponent.inject - -actual fun getSettingsScreenModel(): SettingsScreenModel { - val res: SettingsScreenModel by inject(SettingsScreenModel::class.java) - return res -} \ No newline at end of file diff --git a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt index 4c62e2b79..c221a0c02 100644 --- a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt +++ b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt @@ -1,8 +1,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_settings -import org.koin.core.module.dsl.factoryOf -import org.koin.dsl.module +import org.koin.core.module.Module -val settingsTabModule = module { - factoryOf(::SettingsScreenModel) -} \ No newline at end of file +expect val settingsTabModule: Module + +expect fun getSettingsScreenModel(): SettingsScreenModel diff --git a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt index 192ac537f..3cf315915 100644 --- a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt +++ b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt @@ -3,13 +3,10 @@ package com.github.diegoberaldin.raccoonforlemmy.feature_settings import cafe.adriel.voyager.core.model.ScreenModel import com.github.diegoberaldin.raccoonforlemmy.core_appearance.data.ThemeState import com.github.diegoberaldin.raccoonforlemmy.core_appearance.repository.ThemeRepository +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel import com.github.diegoberaldin.raccoonforlemmy.core_preferences.KeyStoreKeys import com.github.diegoberaldin.raccoonforlemmy.core_preferences.TemporaryKeyStore -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -17,29 +14,31 @@ import kotlinx.coroutines.launch class SettingsScreenModel( private val themeRepository: ThemeRepository, private val keyStore: TemporaryKeyStore, -) : ScreenModel { - private val _uiState = MutableStateFlow(SettingsScreenUiState()) - val uiState = _uiState.asStateFlow() - - val scope = CoroutineScope(SupervisorJob()) - - init { + private val mvi: DefaultMviModel, +) : ScreenModel, + MviModel by mvi { + override fun onStarted() { + mvi.onStarted() themeRepository.state.onEach { val isDarkTheme = when (themeRepository.state.value) { ThemeState.Dark -> true else -> false } - _uiState.getAndUpdate { it.copy(darkTheme = isDarkTheme) } - }.launchIn(scope)// TODO: is this running forever? + mvi.updateState { it.copy(darkTheme = isDarkTheme) } + }.launchIn(mvi.scope) } - fun setDarkTheme(value: Boolean) { + override fun reduce(intent: SettingsScreenMviModel.Intent) { + when (intent) { + is SettingsScreenMviModel.Intent.EnableDarkMode -> setDarkTheme(intent.value) + } + } + + private fun setDarkTheme(value: Boolean) { themeRepository.changeTheme(if (value) ThemeState.Dark else ThemeState.Light) - scope.launch { + mvi.scope.launch { keyStore.save(KeyStoreKeys.EnableDarkTheme, value) } } -} - -expect fun getSettingsScreenModel(): SettingsScreenModel +} \ No newline at end of file diff --git a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenMviModel.kt b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenMviModel.kt new file mode 100644 index 000000000..391c84e9f --- /dev/null +++ b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenMviModel.kt @@ -0,0 +1,17 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_settings + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.MviModel + +interface SettingsScreenMviModel : + MviModel { + + sealed interface Intent { + data class EnableDarkMode(val value: Boolean) : Intent + } + + data class UiState( + val darkTheme: Boolean = false, + ) + + sealed interface Effect +} \ No newline at end of file diff --git a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenUiState.kt b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenUiState.kt deleted file mode 100644 index 28c9f0d92..000000000 --- a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenUiState.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.github.diegoberaldin.raccoonforlemmy.feature_settings - -data class SettingsScreenUiState( - val darkTheme: Boolean = false, -) \ No newline at end of file diff --git a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsTab.kt b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsTab.kt index cd4e00172..96d57370e 100644 --- a/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsTab.kt +++ b/feature-settings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsTab.kt @@ -19,8 +19,8 @@ import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.TabOptions +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.bindToLifecycle import com.github.diegoberaldin.raccoonforlemmy.resources.MR -import com.github.diegoberaldin.raccoonforlemmy.resources.getLanguageRepository import dev.icerock.moko.resources.compose.stringResource object SettingsTab : Tab { @@ -43,6 +43,8 @@ object SettingsTab : Tab { @Composable override fun Content() { val model = rememberScreenModel { getSettingsScreenModel() } + model.bindToLifecycle(key) + val uiState by model.uiState.collectAsState() Column(modifier = Modifier.padding(4.dp)) { @@ -56,7 +58,7 @@ object SettingsTab : Tab { Checkbox( checked = uiState.darkTheme, onCheckedChange = { - model.setDarkTheme(it) + model.reduce(SettingsScreenMviModel.Intent.EnableDarkMode(it)) } ) } diff --git a/feature-settings/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt b/feature-settings/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt new file mode 100644 index 000000000..f9cfcc340 --- /dev/null +++ b/feature-settings/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsModule.kt @@ -0,0 +1,24 @@ +package com.github.diegoberaldin.raccoonforlemmy.feature_settings + +import com.github.diegoberaldin.raccoonforlemmy.core_architecture.DefaultMviModel +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.dsl.module + +actual val settingsTabModule = module { + factory { + SettingsScreenModel( + themeRepository = get(), + keyStore = get(), + mvi = DefaultMviModel( + SettingsScreenMviModel.UiState() + ) + ) + } +} + +actual fun getSettingsScreenModel() = SettingsScreenModelHelper.model + +object SettingsScreenModelHelper : KoinComponent { + val model: SettingsScreenModel by inject() +} \ No newline at end of file diff --git a/feature-settings/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt b/feature-settings/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt deleted file mode 100644 index be89b4ebd..000000000 --- a/feature-settings/src/iosMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature_settings/SettingsScreenModel.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.diegoberaldin.raccoonforlemmy.feature_settings - -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject - -actual fun getSettingsScreenModel() = SettingsScreenModelHelper.model - -object SettingsScreenModelHelper : KoinComponent { - val model: SettingsScreenModel by inject() -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 6f5143a05..9894f9a40 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -31,3 +31,4 @@ include(":core-utils") include(":core-appearance") include(":core-preferences") include(":resources") +include(":core-architecture")