diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..de3e365 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +MetaTerm \ No newline at end of file diff --git a/.idea/artifacts/main_jvm_1_0_1.xml b/.idea/artifacts/main_jvm_1_0_1.xml index eaa2207..1c29a84 100644 --- a/.idea/artifacts/main_jvm_1_0_1.xml +++ b/.idea/artifacts/main_jvm_1_0_1.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/feature-base/main/build/libs - + \ No newline at end of file diff --git a/.idea/artifacts/repo_jvm_1_0_1.xml b/.idea/artifacts/repo_jvm_1_0_1.xml index 27ebf07..23dc48c 100644 --- a/.idea/artifacts/repo_jvm_1_0_1.xml +++ b/.idea/artifacts/repo_jvm_1_0_1.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/core-repository/repo/build/libs - + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index f5f5dcd..d1d59fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,12 +24,12 @@ kotlin { dependencies { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) - implementation(libs.essenty.instancekeeper) - implementation(compose.foundation) implementation(compose.animation) implementation(libs.koin) + implementation(libs.decompose) + implementation(libs.decompose.extensions) implementation(projects.coreCommon) implementation(projects.coreData) @@ -55,7 +55,7 @@ kotlin { compose.desktop { application { - mainClass = "MetaTermKt" + mainClass = "MainKt" nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) packageName = "MetaTerm" diff --git a/core-common/build.gradle.kts b/core-common/build.gradle.kts index 1e5a356..b9ff2c5 100644 --- a/core-common/build.gradle.kts +++ b/core-common/build.gradle.kts @@ -23,7 +23,8 @@ kotlin { implementation(compose.desktop.currentOs) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) + implementation(libs.kotlinx.coroutines) implementation(libs.androidx.datastore) api(libs.bundles.log4j) diff --git a/core-common/gradle.properties b/core-common/gradle.properties index 47c80dc..15a3af6 100644 --- a/core-common/gradle.properties +++ b/core-common/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.1 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-common/src/jvmMain/kotlin/coroutines/CoroutineDispatcherProvider.kt b/core-common/src/jvmMain/kotlin/common/coroutines/CoroutineDispatcherProvider.kt similarity index 90% rename from core-common/src/jvmMain/kotlin/coroutines/CoroutineDispatcherProvider.kt rename to core-common/src/jvmMain/kotlin/common/coroutines/CoroutineDispatcherProvider.kt index 3fc8510..af476f5 100644 --- a/core-common/src/jvmMain/kotlin/coroutines/CoroutineDispatcherProvider.kt +++ b/core-common/src/jvmMain/kotlin/common/coroutines/CoroutineDispatcherProvider.kt @@ -1,4 +1,4 @@ -package coroutines +package common.coroutines import kotlinx.coroutines.CoroutineDispatcher diff --git a/core-common/src/jvmMain/kotlin/coroutines/CoroutineDispatcherProviderImpl.kt b/core-common/src/jvmMain/kotlin/common/coroutines/CoroutineDispatcherProviderImpl.kt similarity index 92% rename from core-common/src/jvmMain/kotlin/coroutines/CoroutineDispatcherProviderImpl.kt rename to core-common/src/jvmMain/kotlin/common/coroutines/CoroutineDispatcherProviderImpl.kt index 4b5aa1b..fa8f222 100644 --- a/core-common/src/jvmMain/kotlin/coroutines/CoroutineDispatcherProviderImpl.kt +++ b/core-common/src/jvmMain/kotlin/common/coroutines/CoroutineDispatcherProviderImpl.kt @@ -1,4 +1,4 @@ -package coroutines +package common.coroutines import kotlinx.coroutines.Dispatchers diff --git a/core-common/src/jvmMain/kotlin/common/di/CommonModule.kt b/core-common/src/jvmMain/kotlin/common/di/CommonModule.kt new file mode 100644 index 0000000..28c8868 --- /dev/null +++ b/core-common/src/jvmMain/kotlin/common/di/CommonModule.kt @@ -0,0 +1,31 @@ +package common.di + +import common.files.DefaultFileManager +import common.files.FileManager +import common.keystore.DefaultTemporaryKeyStore +import common.keystore.TemporaryKeyStore +import common.log.DefaultLogManager +import common.log.LogManager +import common.notification.DefaultNotificationCenter +import common.notification.NotificationCenter +import org.koin.dsl.module +import common.coroutines.CoroutineDispatcherProvider +import common.coroutines.CoroutineDispatcherProviderImpl + +val commonModule = module { + single { + CoroutineDispatcherProviderImpl + } + single { + DefaultNotificationCenter + } + single { + DefaultTemporaryKeyStore(fileManager = get()) + } + single { + DefaultFileManager + } + single { + DefaultLogManager(fileManager = get()) + } +} diff --git a/core-common/src/jvmMain/kotlin/files/DefaultFileManager.kt b/core-common/src/jvmMain/kotlin/common/files/DefaultFileManager.kt similarity index 98% rename from core-common/src/jvmMain/kotlin/files/DefaultFileManager.kt rename to core-common/src/jvmMain/kotlin/common/files/DefaultFileManager.kt index 5594cc1..ff9526f 100644 --- a/core-common/src/jvmMain/kotlin/files/DefaultFileManager.kt +++ b/core-common/src/jvmMain/kotlin/common/files/DefaultFileManager.kt @@ -1,4 +1,4 @@ -package files +package common.files import java.io.File import java.util.* diff --git a/core-common/src/jvmMain/kotlin/files/FileManager.kt b/core-common/src/jvmMain/kotlin/common/files/FileManager.kt similarity index 79% rename from core-common/src/jvmMain/kotlin/files/FileManager.kt rename to core-common/src/jvmMain/kotlin/common/files/FileManager.kt index dd9ef2f..89cd9a1 100644 --- a/core-common/src/jvmMain/kotlin/files/FileManager.kt +++ b/core-common/src/jvmMain/kotlin/common/files/FileManager.kt @@ -1,4 +1,4 @@ -package files +package common.files interface FileManager { fun getFilePath(vararg components: String): String diff --git a/core-common/src/jvmMain/kotlin/keystore/DefaultTemporaryKeyStore.kt b/core-common/src/jvmMain/kotlin/common/keystore/DefaultTemporaryKeyStore.kt similarity index 98% rename from core-common/src/jvmMain/kotlin/keystore/DefaultTemporaryKeyStore.kt rename to core-common/src/jvmMain/kotlin/common/keystore/DefaultTemporaryKeyStore.kt index 403d6ae..809b75c 100644 --- a/core-common/src/jvmMain/kotlin/keystore/DefaultTemporaryKeyStore.kt +++ b/core-common/src/jvmMain/kotlin/common/keystore/DefaultTemporaryKeyStore.kt @@ -1,4 +1,4 @@ -package keystore +package common.keystore import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.PreferenceDataStoreFactory @@ -9,7 +9,7 @@ import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.floatPreferencesKey import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey -import files.FileManager +import common.files.FileManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first diff --git a/core-common/src/jvmMain/kotlin/keystore/TemporaryKeyStore.kt b/core-common/src/jvmMain/kotlin/common/keystore/TemporaryKeyStore.kt similarity index 95% rename from core-common/src/jvmMain/kotlin/keystore/TemporaryKeyStore.kt rename to core-common/src/jvmMain/kotlin/common/keystore/TemporaryKeyStore.kt index 6b80ed0..baa42f6 100644 --- a/core-common/src/jvmMain/kotlin/keystore/TemporaryKeyStore.kt +++ b/core-common/src/jvmMain/kotlin/common/keystore/TemporaryKeyStore.kt @@ -1,4 +1,4 @@ -package keystore +package common.keystore interface TemporaryKeyStore { suspend fun save(key: String, value: Boolean) diff --git a/core-common/src/jvmMain/kotlin/log/DefaultLogManager.kt b/core-common/src/jvmMain/kotlin/common/log/DefaultLogManager.kt similarity index 94% rename from core-common/src/jvmMain/kotlin/log/DefaultLogManager.kt rename to core-common/src/jvmMain/kotlin/common/log/DefaultLogManager.kt index fb1d4a9..48285b8 100644 --- a/core-common/src/jvmMain/kotlin/log/DefaultLogManager.kt +++ b/core-common/src/jvmMain/kotlin/common/log/DefaultLogManager.kt @@ -1,6 +1,6 @@ -package log +package common.log -import files.FileManager +import common.files.FileManager import org.slf4j.Logger import org.slf4j.LoggerFactory diff --git a/core-common/src/jvmMain/kotlin/log/LogManager.kt b/core-common/src/jvmMain/kotlin/common/log/LogManager.kt similarity index 92% rename from core-common/src/jvmMain/kotlin/log/LogManager.kt rename to core-common/src/jvmMain/kotlin/common/log/LogManager.kt index 840ddd1..c626287 100644 --- a/core-common/src/jvmMain/kotlin/log/LogManager.kt +++ b/core-common/src/jvmMain/kotlin/common/log/LogManager.kt @@ -1,4 +1,4 @@ -package log +package common.log interface LogManager { fun trace(message: String) diff --git a/core-common/src/jvmMain/kotlin/notification/DefaultNotificationCenter.kt b/core-common/src/jvmMain/kotlin/common/notification/DefaultNotificationCenter.kt similarity index 95% rename from core-common/src/jvmMain/kotlin/notification/DefaultNotificationCenter.kt rename to core-common/src/jvmMain/kotlin/common/notification/DefaultNotificationCenter.kt index a856cee..dae5339 100644 --- a/core-common/src/jvmMain/kotlin/notification/DefaultNotificationCenter.kt +++ b/core-common/src/jvmMain/kotlin/common/notification/DefaultNotificationCenter.kt @@ -1,4 +1,4 @@ -package notification +package common.notification import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/core-common/src/jvmMain/kotlin/notification/NotificationCenter.kt b/core-common/src/jvmMain/kotlin/common/notification/NotificationCenter.kt similarity index 94% rename from core-common/src/jvmMain/kotlin/notification/NotificationCenter.kt rename to core-common/src/jvmMain/kotlin/common/notification/NotificationCenter.kt index b148de0..7a4a31f 100644 --- a/core-common/src/jvmMain/kotlin/notification/NotificationCenter.kt +++ b/core-common/src/jvmMain/kotlin/common/notification/NotificationCenter.kt @@ -1,4 +1,4 @@ -package notification +package common.notification import kotlinx.coroutines.flow.SharedFlow diff --git a/core-common/src/jvmMain/kotlin/ui/components/CustomDialog.kt b/core-common/src/jvmMain/kotlin/common/ui/components/CustomDialog.kt similarity index 97% rename from core-common/src/jvmMain/kotlin/ui/components/CustomDialog.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/CustomDialog.kt index 5956510..4139fc1 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/CustomDialog.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/CustomDialog.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -19,7 +19,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.rememberDialogState -import ui.theme.Spacing +import common.ui.theme.Spacing @Composable fun CustomDialog( diff --git a/core-common/src/jvmMain/kotlin/ui/components/CustomOpenFileDialog.kt b/core-common/src/jvmMain/kotlin/common/ui/components/CustomOpenFileDialog.kt similarity index 97% rename from core-common/src/jvmMain/kotlin/ui/components/CustomOpenFileDialog.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/CustomOpenFileDialog.kt index b5555d8..a053934 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/CustomOpenFileDialog.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/CustomOpenFileDialog.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.runtime.Composable import androidx.compose.ui.window.AwtWindow diff --git a/core-common/src/jvmMain/kotlin/ui/components/CustomProgressIndicator.kt b/core-common/src/jvmMain/kotlin/common/ui/components/CustomProgressIndicator.kt similarity index 98% rename from core-common/src/jvmMain/kotlin/ui/components/CustomProgressIndicator.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/CustomProgressIndicator.kt index 21e1dc2..4c939dd 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/CustomProgressIndicator.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/CustomProgressIndicator.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.foundation.Canvas import androidx.compose.foundation.background @@ -27,8 +27,8 @@ import androidx.compose.ui.text.drawText import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp -import utils.toLocalPixel -import utils.toRadians +import common.utils.toLocalPixel +import common.utils.toRadians import java.text.DecimalFormat import kotlin.math.sin diff --git a/core-common/src/jvmMain/kotlin/ui/components/CustomSaveFileDialog.kt b/core-common/src/jvmMain/kotlin/common/ui/components/CustomSaveFileDialog.kt similarity index 97% rename from core-common/src/jvmMain/kotlin/ui/components/CustomSaveFileDialog.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/CustomSaveFileDialog.kt index 9d6d7a1..0b09fbd 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/CustomSaveFileDialog.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/CustomSaveFileDialog.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.runtime.Composable import androidx.compose.ui.window.AwtWindow diff --git a/core-common/src/jvmMain/kotlin/ui/components/CustomSpinner.kt b/core-common/src/jvmMain/kotlin/common/ui/components/CustomSpinner.kt similarity index 98% rename from core-common/src/jvmMain/kotlin/ui/components/CustomSpinner.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/CustomSpinner.kt index 60e21d6..f377148 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/CustomSpinner.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/CustomSpinner.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -32,7 +32,7 @@ import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import ui.theme.Spacing +import common.ui.theme.Spacing @OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class) @Composable diff --git a/core-common/src/jvmMain/kotlin/ui/components/CustomTabBar.kt b/core-common/src/jvmMain/kotlin/common/ui/components/CustomTabBar.kt similarity index 96% rename from core-common/src/jvmMain/kotlin/ui/components/CustomTabBar.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/CustomTabBar.kt index e447b44..d22f502 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/CustomTabBar.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/CustomTabBar.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -22,8 +22,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import ui.theme.SelectedBackground -import ui.theme.Spacing +import common.ui.theme.SelectedBackground +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/core-common/src/jvmMain/kotlin/ui/components/CustomTextField.kt b/core-common/src/jvmMain/kotlin/common/ui/components/CustomTextField.kt similarity index 98% rename from core-common/src/jvmMain/kotlin/ui/components/CustomTextField.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/CustomTextField.kt index 6f7e360..f73d6ea 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/CustomTextField.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/CustomTextField.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -15,7 +15,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import ui.theme.Spacing +import common.ui.theme.Spacing @Composable fun CustomTextField( diff --git a/core-common/src/jvmMain/kotlin/ui/components/CustomToolTipArea.kt b/core-common/src/jvmMain/kotlin/common/ui/components/CustomToolTipArea.kt similarity index 94% rename from core-common/src/jvmMain/kotlin/ui/components/CustomToolTipArea.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/CustomToolTipArea.kt index 4aa127a..a0f2bae 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/CustomToolTipArea.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/CustomToolTipArea.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.TooltipArea @@ -11,7 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import ui.theme.Spacing +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/core-common/src/jvmMain/kotlin/ui/components/StyledLabel.kt b/core-common/src/jvmMain/kotlin/common/ui/components/StyledLabel.kt similarity index 97% rename from core-common/src/jvmMain/kotlin/ui/components/StyledLabel.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/StyledLabel.kt index 76c42be..ba95c4b 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/StyledLabel.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/StyledLabel.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import ui.theme.Spacing +import common.ui.theme.Spacing @Composable fun StyledLabel( diff --git a/core-common/src/jvmMain/kotlin/ui/components/TreeItem.kt b/core-common/src/jvmMain/kotlin/common/ui/components/TreeItem.kt similarity index 97% rename from core-common/src/jvmMain/kotlin/ui/components/TreeItem.kt rename to core-common/src/jvmMain/kotlin/common/ui/components/TreeItem.kt index 19d5b9b..e5f7f80 100644 --- a/core-common/src/jvmMain/kotlin/ui/components/TreeItem.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/components/TreeItem.kt @@ -1,4 +1,4 @@ -package ui.components +package common.ui.components import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.Arrangement @@ -13,7 +13,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.unit.dp -import ui.theme.Spacing +import common.ui.theme.Spacing @Composable fun TreeItem( diff --git a/core-common/src/jvmMain/kotlin/ui/theme/Colors.kt b/core-common/src/jvmMain/kotlin/common/ui/theme/Colors.kt similarity index 95% rename from core-common/src/jvmMain/kotlin/ui/theme/Colors.kt rename to core-common/src/jvmMain/kotlin/common/ui/theme/Colors.kt index 2275027..be747af 100644 --- a/core-common/src/jvmMain/kotlin/ui/theme/Colors.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/theme/Colors.kt @@ -1,4 +1,4 @@ -package ui.theme +package common.ui.theme import androidx.compose.material.darkColors import androidx.compose.ui.graphics.Color diff --git a/core-common/src/jvmMain/kotlin/ui/theme/Dimensions.kt b/core-common/src/jvmMain/kotlin/common/ui/theme/Dimensions.kt similarity index 88% rename from core-common/src/jvmMain/kotlin/ui/theme/Dimensions.kt rename to core-common/src/jvmMain/kotlin/common/ui/theme/Dimensions.kt index ef8573e..2ff7ebc 100644 --- a/core-common/src/jvmMain/kotlin/ui/theme/Dimensions.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/theme/Dimensions.kt @@ -1,4 +1,4 @@ -package ui.theme +package common.ui.theme import androidx.compose.ui.unit.dp diff --git a/core-common/src/jvmMain/kotlin/ui/theme/Fonts.kt b/core-common/src/jvmMain/kotlin/common/ui/theme/Fonts.kt similarity index 98% rename from core-common/src/jvmMain/kotlin/ui/theme/Fonts.kt rename to core-common/src/jvmMain/kotlin/common/ui/theme/Fonts.kt index 3ba7127..769de61 100644 --- a/core-common/src/jvmMain/kotlin/ui/theme/Fonts.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/theme/Fonts.kt @@ -1,4 +1,4 @@ -package ui.theme +package common.ui.theme import androidx.compose.material.Typography import androidx.compose.ui.text.TextStyle diff --git a/core-common/src/jvmMain/kotlin/ui/theme/MetaTermTheme.kt b/core-common/src/jvmMain/kotlin/common/ui/theme/MetaTermTheme.kt similarity index 92% rename from core-common/src/jvmMain/kotlin/ui/theme/MetaTermTheme.kt rename to core-common/src/jvmMain/kotlin/common/ui/theme/MetaTermTheme.kt index c8d3b22..05fc8db 100644 --- a/core-common/src/jvmMain/kotlin/ui/theme/MetaTermTheme.kt +++ b/core-common/src/jvmMain/kotlin/common/ui/theme/MetaTermTheme.kt @@ -1,4 +1,4 @@ -package ui.theme +package common.ui.theme import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable diff --git a/core-common/src/jvmMain/kotlin/utils/AppBusiness.kt b/core-common/src/jvmMain/kotlin/common/utils/AppBusiness.kt similarity index 94% rename from core-common/src/jvmMain/kotlin/utils/AppBusiness.kt rename to core-common/src/jvmMain/kotlin/common/utils/AppBusiness.kt index 596f4d5..28f9a7e 100644 --- a/core-common/src/jvmMain/kotlin/utils/AppBusiness.kt +++ b/core-common/src/jvmMain/kotlin/common/utils/AppBusiness.kt @@ -1,4 +1,4 @@ -package utils +package common.utils import com.arkivanov.essenty.instancekeeper.InstanceKeeper import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher diff --git a/core-common/src/jvmMain/kotlin/utils/Extensions.kt b/core-common/src/jvmMain/kotlin/common/utils/Extensions.kt similarity index 94% rename from core-common/src/jvmMain/kotlin/utils/Extensions.kt rename to core-common/src/jvmMain/kotlin/common/utils/Extensions.kt index 241ee1c..a3d626d 100644 --- a/core-common/src/jvmMain/kotlin/utils/Extensions.kt +++ b/core-common/src/jvmMain/kotlin/common/utils/Extensions.kt @@ -1,4 +1,4 @@ -package utils +package common.utils import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalDensity diff --git a/core-common/src/jvmMain/kotlin/common/utils/Utils.kt b/core-common/src/jvmMain/kotlin/common/utils/Utils.kt new file mode 100644 index 0000000..a87bd64 --- /dev/null +++ b/core-common/src/jvmMain/kotlin/common/utils/Utils.kt @@ -0,0 +1,92 @@ +package common.utils + +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.value.Value +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.timeout +import org.koin.core.parameter.parametersOf +import org.koin.java.KoinJavaComponent +import javax.swing.SwingUtilities +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +fun runOnUiThread(block: () -> T): T { + if (SwingUtilities.isEventDispatchThread()) { + return block() + } + + var error: Throwable? = null + var result: T? = null + + SwingUtilities.invokeAndWait { + try { + result = block() + } catch (e: Throwable) { + error = e + } + } + + error?.also { throw it } + + @Suppress("UNCHECKED_CAST") + return result as T +} + +inline fun getByInjection(): T { + val res by KoinJavaComponent.inject(T::class.java) + return res +} + +inline fun getByInjection(vararg params: Any?): T { + val res by KoinJavaComponent.inject(T::class.java) { + parametersOf(*params) + } + return res +} + +@OptIn(FlowPreview::class) +inline fun Value>.activeAsFlow( + withNullsIfNotInstance: Boolean = false, + timeout: Duration = 500.milliseconds, +): Flow = callbackFlow { + val observer: (ChildStack<*, *>) -> Unit = { + val child = it.active.instance + if (T::class.java.isInstance(child)) { + trySend(child as T) + } else { + if (withNullsIfNotInstance) { + trySend(null) + } + } + } + subscribe(observer) + awaitClose { + unsubscribe(observer) + } +}.timeout(timeout).catch { emit(null) } + +@OptIn(FlowPreview::class) +inline fun Value>.asFlow( + withNullsIfNotInstance: Boolean = false, + timeout: Duration = 500.milliseconds, +): Flow = callbackFlow { + val observer: (ChildSlot<*, *>) -> Unit = { + val child = it.child?.instance + if (T::class.java.isInstance(child)) { + trySend(child as T) + } else { + if (withNullsIfNotInstance) { + trySend(null) + } + } + } + subscribe(observer) + awaitClose { + unsubscribe(observer) + } +}.timeout(timeout).catch { emit(null) } diff --git a/core-common/src/jvmMain/kotlin/di/CommonModule.kt b/core-common/src/jvmMain/kotlin/di/CommonModule.kt deleted file mode 100644 index d1d53ce..0000000 --- a/core-common/src/jvmMain/kotlin/di/CommonModule.kt +++ /dev/null @@ -1,31 +0,0 @@ -package di - -import files.DefaultFileManager -import files.FileManager -import keystore.DefaultTemporaryKeyStore -import keystore.TemporaryKeyStore -import log.DefaultLogManager -import log.LogManager -import notification.DefaultNotificationCenter -import notification.NotificationCenter -import org.koin.dsl.module -import coroutines.CoroutineDispatcherProvider -import coroutines.CoroutineDispatcherProviderImpl - -val commonKoinModule = module { - single { - CoroutineDispatcherProviderImpl - } - single { - DefaultNotificationCenter - } - single { - DefaultTemporaryKeyStore(fileManager = get()) - } - single { - DefaultFileManager - } - single { - DefaultLogManager(fileManager = get()) - } -} diff --git a/core-common/tests/gradle.properties b/core-common/tests/gradle.properties index 056070a..ca25374 100644 --- a/core-common/tests/gradle.properties +++ b/core-common/tests/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-common/tests/src/jvmMain/kotlin/files/DefaultFileManagerTest.kt b/core-common/tests/src/jvmMain/kotlin/common/files/DefaultFileManagerTest.kt similarity index 97% rename from core-common/tests/src/jvmMain/kotlin/files/DefaultFileManagerTest.kt rename to core-common/tests/src/jvmMain/kotlin/common/files/DefaultFileManagerTest.kt index 2a2d2ad..068b21f 100644 --- a/core-common/tests/src/jvmMain/kotlin/files/DefaultFileManagerTest.kt +++ b/core-common/tests/src/jvmMain/kotlin/common/files/DefaultFileManagerTest.kt @@ -1,4 +1,4 @@ -package files +package common.files import java.io.File import kotlin.test.AfterTest diff --git a/core-common/tests/src/jvmMain/kotlin/keystore/DefaultTemporaryKeyStoreTest.kt b/core-common/tests/src/jvmMain/kotlin/common/keystore/DefaultTemporaryKeyStoreTest.kt similarity index 98% rename from core-common/tests/src/jvmMain/kotlin/keystore/DefaultTemporaryKeyStoreTest.kt rename to core-common/tests/src/jvmMain/kotlin/common/keystore/DefaultTemporaryKeyStoreTest.kt index ed6008f..9144a98 100644 --- a/core-common/tests/src/jvmMain/kotlin/keystore/DefaultTemporaryKeyStoreTest.kt +++ b/core-common/tests/src/jvmMain/kotlin/common/keystore/DefaultTemporaryKeyStoreTest.kt @@ -1,6 +1,6 @@ -package keystore +package common.keystore -import files.FileManager +import common.files.FileManager import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import java.io.File diff --git a/core-common/tests/src/jvmMain/kotlin/notification/DefaultNotificationCenterTest.kt b/core-common/tests/src/jvmMain/kotlin/common/notification/DefaultNotificationCenterTest.kt similarity index 97% rename from core-common/tests/src/jvmMain/kotlin/notification/DefaultNotificationCenterTest.kt rename to core-common/tests/src/jvmMain/kotlin/common/notification/DefaultNotificationCenterTest.kt index 5de52a5..365d6b7 100644 --- a/core-common/tests/src/jvmMain/kotlin/notification/DefaultNotificationCenterTest.kt +++ b/core-common/tests/src/jvmMain/kotlin/common/notification/DefaultNotificationCenterTest.kt @@ -1,4 +1,4 @@ -package notification +package common.notification import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/core-data/gradle.properties b/core-data/gradle.properties index 47c80dc..15a3af6 100644 --- a/core-data/gradle.properties +++ b/core-data/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.1 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-data/src/jvmMain/kotlin/data/ExportType.kt b/core-data/src/jvmMain/kotlin/data/ExportType.kt new file mode 100644 index 0000000..3b36e9d --- /dev/null +++ b/core-data/src/jvmMain/kotlin/data/ExportType.kt @@ -0,0 +1,6 @@ +package data + +enum class ExportType { + CSV, + TBX, +} \ No newline at end of file diff --git a/core-localization/gradle.properties b/core-localization/gradle.properties index 47c80dc..15a3af6 100644 --- a/core-localization/gradle.properties +++ b/core-localization/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.1 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-localization/tests/gradle.properties b/core-localization/tests/gradle.properties index 056070a..ca25374 100644 --- a/core-localization/tests/gradle.properties +++ b/core-localization/tests/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-persistence/gradle.properties b/core-persistence/gradle.properties index 47c80dc..15a3af6 100644 --- a/core-persistence/gradle.properties +++ b/core-persistence/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.1 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-persistence/src/jvmMain/kotlin/AppDatabase.kt b/core-persistence/src/jvmMain/kotlin/persistence/AppDatabase.kt similarity index 63% rename from core-persistence/src/jvmMain/kotlin/AppDatabase.kt rename to core-persistence/src/jvmMain/kotlin/persistence/AppDatabase.kt index f2e8a49..6f59acd 100644 --- a/core-persistence/src/jvmMain/kotlin/AppDatabase.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/AppDatabase.kt @@ -1,24 +1,26 @@ -import dao.EntryDAO -import dao.EntryPropertyValueDAO -import dao.InputDescriptorDAO -import dao.LanguageDAO -import dao.LanguagePropertyValueDAO -import dao.PicklistValueDAO -import dao.PropertyDAO -import dao.TermDAO -import dao.TermPropertyValueDAO -import dao.TermbaseDAO -import entities.EntryEntity -import entities.EntryPropertyValueEntity -import entities.InputDescriptorEntity -import entities.LanguageEntity -import entities.LanguagePropertyValueEntity -import entities.PicklistValueEntity -import entities.PropertyEntity -import entities.TermEntity -import entities.TermPropertyValueEntity -import entities.TermbaseEntity -import files.FileManager +package persistence + +import persistence.dao.EntryDAO +import persistence.dao.EntryPropertyValueDAO +import persistence.dao.InputDescriptorDAO +import persistence.dao.LanguageDAO +import persistence.dao.LanguagePropertyValueDAO +import persistence.dao.PicklistValueDAO +import persistence.dao.PropertyDAO +import persistence.dao.TermDAO +import persistence.dao.TermPropertyValueDAO +import persistence.dao.TermbaseDAO +import persistence.entities.EntryEntity +import persistence.entities.EntryPropertyValueEntity +import persistence.entities.InputDescriptorEntity +import persistence.entities.LanguageEntity +import persistence.entities.LanguagePropertyValueEntity +import persistence.entities.PicklistValueEntity +import persistence.entities.PropertyEntity +import persistence.entities.TermEntity +import persistence.entities.TermPropertyValueEntity +import persistence.entities.TermbaseEntity +import common.files.FileManager import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.transaction diff --git a/core-persistence/src/jvmMain/kotlin/dao/EntryDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/EntryDAO.kt similarity index 93% rename from core-persistence/src/jvmMain/kotlin/dao/EntryDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/EntryDAO.kt index 2b36e51..2536864 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/EntryDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/EntryDAO.kt @@ -1,9 +1,9 @@ -package dao +package persistence.dao import data.EntryModel -import entities.EntryEntity -import entities.EntryEntity.termbaseId -import entities.TermEntity +import persistence.entities.EntryEntity +import persistence.entities.EntryEntity.termbaseId +import persistence.entities.TermEntity import kotlinx.coroutines.delay import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.isActive diff --git a/core-persistence/src/jvmMain/kotlin/dao/EntryPropertyValueDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/EntryPropertyValueDAO.kt similarity index 95% rename from core-persistence/src/jvmMain/kotlin/dao/EntryPropertyValueDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/EntryPropertyValueDAO.kt index 7fab1b8..65ebb53 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/EntryPropertyValueDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/EntryPropertyValueDAO.kt @@ -1,7 +1,7 @@ -package dao +package persistence.dao import data.PropertyValueModel -import entities.EntryPropertyValueEntity +import persistence.entities.EntryPropertyValueEntity import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.deleteWhere diff --git a/core-persistence/src/jvmMain/kotlin/dao/InputDescriptorDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/InputDescriptorDAO.kt similarity index 96% rename from core-persistence/src/jvmMain/kotlin/dao/InputDescriptorDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/InputDescriptorDAO.kt index a0d152f..0104089 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/InputDescriptorDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/InputDescriptorDAO.kt @@ -1,7 +1,7 @@ -package dao +package persistence.dao import data.InputDescriptorModel -import entities.InputDescriptorEntity +import persistence.entities.InputDescriptorEntity import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.deleteWhere diff --git a/core-persistence/src/jvmMain/kotlin/dao/LanguageDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/LanguageDAO.kt similarity index 96% rename from core-persistence/src/jvmMain/kotlin/dao/LanguageDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/LanguageDAO.kt index 591e50f..6ec17aa 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/LanguageDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/LanguageDAO.kt @@ -1,7 +1,7 @@ -package dao +package persistence.dao import data.LanguageModel -import entities.LanguageEntity +import persistence.entities.LanguageEntity import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and diff --git a/core-persistence/src/jvmMain/kotlin/dao/LanguagePropertyValueDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/LanguagePropertyValueDAO.kt similarity index 96% rename from core-persistence/src/jvmMain/kotlin/dao/LanguagePropertyValueDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/LanguagePropertyValueDAO.kt index d8a8521..9fcfcf2 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/LanguagePropertyValueDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/LanguagePropertyValueDAO.kt @@ -1,7 +1,7 @@ -package dao +package persistence.dao import data.PropertyValueModel -import entities.LanguagePropertyValueEntity +import persistence.entities.LanguagePropertyValueEntity import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and diff --git a/core-persistence/src/jvmMain/kotlin/dao/PicklistValueDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/PicklistValueDAO.kt similarity index 94% rename from core-persistence/src/jvmMain/kotlin/dao/PicklistValueDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/PicklistValueDAO.kt index 90b842c..b00b426 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/PicklistValueDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/PicklistValueDAO.kt @@ -1,7 +1,7 @@ -package dao +package persistence.dao import data.PicklistValueModel -import entities.PicklistValueEntity +import persistence.entities.PicklistValueEntity import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.batchInsert diff --git a/core-persistence/src/jvmMain/kotlin/dao/PropertyDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/PropertyDAO.kt similarity index 97% rename from core-persistence/src/jvmMain/kotlin/dao/PropertyDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/PropertyDAO.kt index 4f0b5c1..a860648 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/PropertyDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/PropertyDAO.kt @@ -1,9 +1,9 @@ -package dao +package persistence.dao import data.PropertyLevel import data.PropertyModel import data.PropertyType -import entities.PropertyEntity +import persistence.entities.PropertyEntity import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.deleteWhere diff --git a/core-persistence/src/jvmMain/kotlin/dao/TermDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/TermDAO.kt similarity index 96% rename from core-persistence/src/jvmMain/kotlin/dao/TermDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/TermDAO.kt index a06e902..ea53616 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/TermDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/TermDAO.kt @@ -1,13 +1,13 @@ -package dao +package persistence.dao import data.SearchCriterion import data.TermModel -import entities.EntryEntity -import entities.EntryPropertyValueEntity -import entities.LanguageEntity -import entities.LanguagePropertyValueEntity -import entities.TermEntity -import entities.TermPropertyValueEntity +import persistence.entities.EntryEntity +import persistence.entities.EntryPropertyValueEntity +import persistence.entities.LanguageEntity +import persistence.entities.LanguagePropertyValueEntity +import persistence.entities.TermEntity +import persistence.entities.TermPropertyValueEntity import org.jetbrains.exposed.sql.JoinType import org.jetbrains.exposed.sql.LikePattern import org.jetbrains.exposed.sql.Op diff --git a/core-persistence/src/jvmMain/kotlin/dao/TermPropertyValueDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/TermPropertyValueDAO.kt similarity index 95% rename from core-persistence/src/jvmMain/kotlin/dao/TermPropertyValueDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/TermPropertyValueDAO.kt index a92dfaf..d69cd70 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/TermPropertyValueDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/TermPropertyValueDAO.kt @@ -1,7 +1,7 @@ -package dao +package persistence.dao import data.PropertyValueModel -import entities.TermPropertyValueEntity +import persistence.entities.TermPropertyValueEntity import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.deleteWhere diff --git a/core-persistence/src/jvmMain/kotlin/dao/TermbaseDAO.kt b/core-persistence/src/jvmMain/kotlin/persistence/dao/TermbaseDAO.kt similarity index 96% rename from core-persistence/src/jvmMain/kotlin/dao/TermbaseDAO.kt rename to core-persistence/src/jvmMain/kotlin/persistence/dao/TermbaseDAO.kt index 0270bc4..2e69ac4 100644 --- a/core-persistence/src/jvmMain/kotlin/dao/TermbaseDAO.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/dao/TermbaseDAO.kt @@ -1,7 +1,7 @@ -package dao +package persistence.dao import data.TermbaseModel -import entities.TermbaseEntity +import persistence.entities.TermbaseEntity import kotlinx.coroutines.delay import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.isActive diff --git a/core-persistence/src/jvmMain/kotlin/di/DatabaseModule.kt b/core-persistence/src/jvmMain/kotlin/persistence/di/DatabaseModule.kt similarity index 92% rename from core-persistence/src/jvmMain/kotlin/di/DatabaseModule.kt rename to core-persistence/src/jvmMain/kotlin/persistence/di/DatabaseModule.kt index 6f28234..399a0fd 100644 --- a/core-persistence/src/jvmMain/kotlin/di/DatabaseModule.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/di/DatabaseModule.kt @@ -1,6 +1,6 @@ -package di +package persistence.di -import AppDatabase +import persistence.AppDatabase import org.koin.dsl.module private val innerDbModule = module { @@ -9,7 +9,7 @@ private val innerDbModule = module { } } -val databaseKoinModule = module { +val databaseModule = module { includes(innerDbModule) single { diff --git a/core-persistence/src/jvmMain/kotlin/entities/EntryEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/EntryEntity.kt similarity index 88% rename from core-persistence/src/jvmMain/kotlin/entities/EntryEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/EntryEntity.kt index 1602840..ba3e9b0 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/EntryEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/EntryEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/EntryPropertyValueEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/EntryPropertyValueEntity.kt similarity index 92% rename from core-persistence/src/jvmMain/kotlin/entities/EntryPropertyValueEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/EntryPropertyValueEntity.kt index f5c477f..60890fb 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/EntryPropertyValueEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/EntryPropertyValueEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/InputDescriptorEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/InputDescriptorEntity.kt similarity index 93% rename from core-persistence/src/jvmMain/kotlin/entities/InputDescriptorEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/InputDescriptorEntity.kt index b2b0955..d484795 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/InputDescriptorEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/InputDescriptorEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/LanguageEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/LanguageEntity.kt similarity index 91% rename from core-persistence/src/jvmMain/kotlin/entities/LanguageEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/LanguageEntity.kt index c6480ba..3795f3b 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/LanguageEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/LanguageEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/LanguagePropertyValueEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/LanguagePropertyValueEntity.kt similarity index 94% rename from core-persistence/src/jvmMain/kotlin/entities/LanguagePropertyValueEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/LanguagePropertyValueEntity.kt index dfb4573..538be04 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/LanguagePropertyValueEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/LanguagePropertyValueEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/PicklistValueEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/PicklistValueEntity.kt similarity index 90% rename from core-persistence/src/jvmMain/kotlin/entities/PicklistValueEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/PicklistValueEntity.kt index bd0317c..d83d682 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/PicklistValueEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/PicklistValueEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/PropertyEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/PropertyEntity.kt similarity index 92% rename from core-persistence/src/jvmMain/kotlin/entities/PropertyEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/PropertyEntity.kt index bf8edc2..dc9b77d 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/PropertyEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/PropertyEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/TermEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/TermEntity.kt similarity index 90% rename from core-persistence/src/jvmMain/kotlin/entities/TermEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/TermEntity.kt index e58f96b..d3e817c 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/TermEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/TermEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/TermPropertyValueEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/TermPropertyValueEntity.kt similarity index 92% rename from core-persistence/src/jvmMain/kotlin/entities/TermPropertyValueEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/TermPropertyValueEntity.kt index 663534e..eeef3d5 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/TermPropertyValueEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/TermPropertyValueEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption diff --git a/core-persistence/src/jvmMain/kotlin/entities/TermbaseEntity.kt b/core-persistence/src/jvmMain/kotlin/persistence/entities/TermbaseEntity.kt similarity index 87% rename from core-persistence/src/jvmMain/kotlin/entities/TermbaseEntity.kt rename to core-persistence/src/jvmMain/kotlin/persistence/entities/TermbaseEntity.kt index cc9710f..dd70465 100644 --- a/core-persistence/src/jvmMain/kotlin/entities/TermbaseEntity.kt +++ b/core-persistence/src/jvmMain/kotlin/persistence/entities/TermbaseEntity.kt @@ -1,4 +1,4 @@ -package entities +package persistence.entities import org.jetbrains.exposed.dao.id.IntIdTable diff --git a/core-persistence/tests/gradle.properties b/core-persistence/tests/gradle.properties index 056070a..ca25374 100644 --- a/core-persistence/tests/gradle.properties +++ b/core-persistence/tests/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-repository/tests/src/jvmMain/kotlin/MockFileManager.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/MockFileManager.kt similarity index 92% rename from core-repository/tests/src/jvmMain/kotlin/MockFileManager.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/MockFileManager.kt index a8bd42f..375773d 100644 --- a/core-repository/tests/src/jvmMain/kotlin/MockFileManager.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/MockFileManager.kt @@ -1,4 +1,4 @@ -import files.FileManager +import common.files.FileManager import java.io.File internal object MockFileManager : FileManager { diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/EntryDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/EntryDAOTest.kt similarity index 98% rename from core-persistence/tests/src/jvmMain/kotlin/dao/EntryDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/EntryDAOTest.kt index 20fd963..016472f 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/EntryDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/EntryDAOTest.kt @@ -1,6 +1,5 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager import data.EntryModel import data.TermbaseModel @@ -8,6 +7,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/EntryPropertyValueDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/EntryPropertyValueDAOTest.kt similarity index 95% rename from core-persistence/tests/src/jvmMain/kotlin/dao/EntryPropertyValueDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/EntryPropertyValueDAOTest.kt index b8addca..6e8582d 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/EntryPropertyValueDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/EntryPropertyValueDAOTest.kt @@ -1,15 +1,11 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager -import data.EntryModel -import data.PropertyLevel -import data.PropertyModel -import data.PropertyValueModel -import data.TermbaseModel +import data.* import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/InputDescriptorDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/InputDescriptorDAOTest.kt similarity index 98% rename from core-persistence/tests/src/jvmMain/kotlin/dao/InputDescriptorDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/InputDescriptorDAOTest.kt index 6af9f12..b84e758 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/InputDescriptorDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/InputDescriptorDAOTest.kt @@ -1,12 +1,12 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager import data.InputDescriptorModel import data.TermbaseModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/LanguageDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/LanguageDAOTest.kt similarity index 98% rename from core-persistence/tests/src/jvmMain/kotlin/dao/LanguageDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/LanguageDAOTest.kt index 38e6770..8ed191f 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/LanguageDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/LanguageDAOTest.kt @@ -1,12 +1,12 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager import data.LanguageModel import data.TermbaseModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/LanguagePropertyValueDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/LanguagePropertyValueDAOTest.kt similarity index 94% rename from core-persistence/tests/src/jvmMain/kotlin/dao/LanguagePropertyValueDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/LanguagePropertyValueDAOTest.kt index ef35672..97f9327 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/LanguagePropertyValueDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/LanguagePropertyValueDAOTest.kt @@ -1,21 +1,15 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager -import data.EntryModel -import data.LanguageModel -import data.PropertyLevel -import data.PropertyModel -import data.PropertyValueModel -import data.TermbaseModel +import data.* import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class LanguagePropertyValueDAOTest { private lateinit var appDb: AppDatabase diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/PicklistValueDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/PicklistValueDAOTest.kt similarity index 97% rename from core-persistence/tests/src/jvmMain/kotlin/dao/PicklistValueDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/PicklistValueDAOTest.kt index bcb4ef7..962a87e 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/PicklistValueDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/PicklistValueDAOTest.kt @@ -1,6 +1,5 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager import data.PicklistValueModel import data.PropertyModel @@ -9,11 +8,11 @@ import data.TermbaseModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class PicklistValueDAOTest { private lateinit var appDb: AppDatabase diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/PropertyDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/PropertyDAOTest.kt similarity index 97% rename from core-persistence/tests/src/jvmMain/kotlin/dao/PropertyDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/PropertyDAOTest.kt index f00d1d7..510356e 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/PropertyDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/PropertyDAOTest.kt @@ -1,21 +1,18 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager import data.PropertyLevel import data.PropertyModel import data.PropertyType import data.TermbaseModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class PropertyDAOTest { - private lateinit var appDb: AppDatabase private lateinit var sut: PropertyDAO private var termbaseId: Int = 0 diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/TermDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermDAOTest.kt similarity index 96% rename from core-persistence/tests/src/jvmMain/kotlin/dao/TermDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermDAOTest.kt index ea9762f..1dc9d88 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/TermDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermDAOTest.kt @@ -1,20 +1,14 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager -import data.EntryModel -import data.LanguageModel -import data.SearchCriterion -import data.TermModel -import data.TermbaseModel -import kotlinx.coroutines.ExperimentalCoroutinesApi +import data.* import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class TermDAOTest { private lateinit var appDb: AppDatabase diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/TermPropertyValueDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermPropertyValueDAOTest.kt similarity index 93% rename from core-persistence/tests/src/jvmMain/kotlin/dao/TermPropertyValueDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermPropertyValueDAOTest.kt index d9a025c..aa39dc1 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/TermPropertyValueDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermPropertyValueDAOTest.kt @@ -1,21 +1,15 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager -import data.EntryModel -import data.PropertyLevel -import data.PropertyModel -import data.PropertyValueModel -import data.TermModel -import data.TermbaseModel +import data.* import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class TermPropertyValueDAOTest { private lateinit var appDb: AppDatabase diff --git a/core-persistence/tests/src/jvmMain/kotlin/dao/TermbaseDAOTest.kt b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermbaseDAOTest.kt similarity index 95% rename from core-persistence/tests/src/jvmMain/kotlin/dao/TermbaseDAOTest.kt rename to core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermbaseDAOTest.kt index 9a002b6..012d4d1 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/dao/TermbaseDAOTest.kt +++ b/core-persistence/tests/src/jvmMain/kotlin/persistence/dao/TermbaseDAOTest.kt @@ -1,16 +1,14 @@ -package dao +package persistence.dao -import AppDatabase import MockFileManager import data.TermbaseModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class TermbaseDAOTest { private lateinit var appDb: AppDatabase diff --git a/core-repository/build.gradle.kts b/core-repository/build.gradle.kts index 1d7709b..26bb73d 100644 --- a/core-repository/build.gradle.kts +++ b/core-repository/build.gradle.kts @@ -28,6 +28,7 @@ kotlin { implementation(projects.coreData) implementation(projects.corePersistence) implementation(projects.coreLocalization) + api(projects.coreRepository.repo) api(projects.coreRepository.usecase) } diff --git a/core-repository/gradle.properties b/core-repository/gradle.properties index 47c80dc..15a3af6 100644 --- a/core-repository/gradle.properties +++ b/core-repository/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.1 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-repository/repo/build.gradle.kts b/core-repository/repo/build.gradle.kts index ebe1158..8674a6b 100644 --- a/core-repository/repo/build.gradle.kts +++ b/core-repository/repo/build.gradle.kts @@ -23,6 +23,8 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) + implementation(libs.koin) + implementation(projects.coreCommon) implementation(projects.coreData) implementation(projects.corePersistence) diff --git a/core-repository/repo/gradle.properties b/core-repository/repo/gradle.properties index 056070a..ca25374 100644 --- a/core-repository/repo/gradle.properties +++ b/core-repository/repo/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/EntryPropertyValueRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/EntryPropertyValueRepository.kt similarity index 92% rename from core-repository/repo/src/jvmMain/kotlin/repository/EntryPropertyValueRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/EntryPropertyValueRepository.kt index d384939..9e11c4f 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/EntryPropertyValueRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/EntryPropertyValueRepository.kt @@ -1,6 +1,6 @@ -package repository +package repo -import dao.EntryPropertyValueDAO +import persistence.dao.EntryPropertyValueDAO import data.PropertyValueModel class EntryPropertyValueRepository( diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/EntryRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/EntryRepository.kt similarity index 93% rename from core-repository/repo/src/jvmMain/kotlin/repository/EntryRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/EntryRepository.kt index 9bd11f2..932707c 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/EntryRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/EntryRepository.kt @@ -1,6 +1,6 @@ -package repository +package repo -import dao.EntryDAO +import persistence.dao.EntryDAO import data.EntryModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged class EntryRepository( - private val entryDAO: EntryDAO, + private val entryDAO: persistence.dao.EntryDAO, ) { private val _currentEntry = MutableStateFlow(null) diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/FlagsRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/FlagsRepository.kt similarity index 98% rename from core-repository/repo/src/jvmMain/kotlin/repository/FlagsRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/FlagsRepository.kt index 7e1a4e6..315c7b1 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/FlagsRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/FlagsRepository.kt @@ -1,4 +1,4 @@ -package repository +package repo import java.util.* diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/InputDescriptorRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/InputDescriptorRepository.kt similarity index 92% rename from core-repository/repo/src/jvmMain/kotlin/repository/InputDescriptorRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/InputDescriptorRepository.kt index 9103816..f45dda4 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/InputDescriptorRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/InputDescriptorRepository.kt @@ -1,6 +1,6 @@ -package repository +package repo -import dao.InputDescriptorDAO +import persistence.dao.InputDescriptorDAO import data.InputDescriptorModel class InputDescriptorRepository( diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/LanguageNameRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/LanguageNameRepository.kt similarity index 98% rename from core-repository/repo/src/jvmMain/kotlin/repository/LanguageNameRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/LanguageNameRepository.kt index d12b050..bcd9080 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/LanguageNameRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/LanguageNameRepository.kt @@ -1,4 +1,4 @@ -package repository +package repo import localized import java.util.* diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/LanguagePropertyValueRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/LanguagePropertyValueRepository.kt similarity index 93% rename from core-repository/repo/src/jvmMain/kotlin/repository/LanguagePropertyValueRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/LanguagePropertyValueRepository.kt index cd11f6c..ee53162 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/LanguagePropertyValueRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/LanguagePropertyValueRepository.kt @@ -1,6 +1,6 @@ -package repository +package repo -import dao.LanguagePropertyValueDAO +import persistence.dao.LanguagePropertyValueDAO import data.PropertyValueModel class LanguagePropertyValueRepository( diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/LanguageRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/LanguageRepository.kt similarity index 97% rename from core-repository/repo/src/jvmMain/kotlin/repository/LanguageRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/LanguageRepository.kt index 6e1d018..2cfd606 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/LanguageRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/LanguageRepository.kt @@ -1,6 +1,6 @@ -package repository +package repo -import dao.LanguageDAO +import persistence.dao.LanguageDAO import data.LanguageModel import java.util.* diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/PropertyRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/PropertyRepository.kt similarity index 94% rename from core-repository/repo/src/jvmMain/kotlin/repository/PropertyRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/PropertyRepository.kt index eb51b8a..b5bdbdf 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/PropertyRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/PropertyRepository.kt @@ -1,7 +1,7 @@ -package repository +package repo -import dao.PicklistValueDAO -import dao.PropertyDAO +import persistence.dao.PicklistValueDAO +import persistence.dao.PropertyDAO import data.PropertyModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/TermPropertyValueRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/TermPropertyValueRepository.kt similarity index 92% rename from core-repository/repo/src/jvmMain/kotlin/repository/TermPropertyValueRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/TermPropertyValueRepository.kt index 48dead7..6f2b2b5 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/TermPropertyValueRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/TermPropertyValueRepository.kt @@ -1,6 +1,6 @@ -package repository +package repo -import dao.TermPropertyValueDAO +import persistence.dao.TermPropertyValueDAO import data.PropertyValueModel class TermPropertyValueRepository( diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/TermRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/TermRepository.kt similarity index 96% rename from core-repository/repo/src/jvmMain/kotlin/repository/TermRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/TermRepository.kt index b430e90..5bd3c8e 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/TermRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/TermRepository.kt @@ -1,6 +1,6 @@ -package repository +package repo -import dao.TermDAO +import persistence.dao.TermDAO import data.SearchCriterion import data.TermModel diff --git a/core-repository/repo/src/jvmMain/kotlin/repository/TermbaseRepository.kt b/core-repository/repo/src/jvmMain/kotlin/repo/TermbaseRepository.kt similarity index 92% rename from core-repository/repo/src/jvmMain/kotlin/repository/TermbaseRepository.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/TermbaseRepository.kt index 7f2fb60..6aa3a6b 100644 --- a/core-repository/repo/src/jvmMain/kotlin/repository/TermbaseRepository.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/TermbaseRepository.kt @@ -1,6 +1,6 @@ -package repository +package repo -import dao.TermbaseDAO +import persistence.dao.TermbaseDAO import data.TermbaseModel import kotlinx.coroutines.flow.distinctUntilChanged diff --git a/core-repository/src/jvmMain/kotlin/di/RepoModule.kt b/core-repository/repo/src/jvmMain/kotlin/repo/di/Module.kt similarity index 61% rename from core-repository/src/jvmMain/kotlin/di/RepoModule.kt rename to core-repository/repo/src/jvmMain/kotlin/repo/di/Module.kt index c674b81..c76ecb7 100644 --- a/core-repository/src/jvmMain/kotlin/di/RepoModule.kt +++ b/core-repository/repo/src/jvmMain/kotlin/repo/di/Module.kt @@ -1,29 +1,29 @@ -package di +package repo.di -import dao.EntryDAO -import dao.EntryPropertyValueDAO -import dao.InputDescriptorDAO -import dao.LanguageDAO -import dao.LanguagePropertyValueDAO -import dao.PicklistValueDAO -import dao.PropertyDAO -import dao.TermDAO -import dao.TermPropertyValueDAO -import dao.TermbaseDAO +import persistence.dao.EntryDAO +import persistence.dao.EntryPropertyValueDAO +import persistence.dao.InputDescriptorDAO +import persistence.dao.LanguageDAO +import persistence.dao.LanguagePropertyValueDAO +import persistence.dao.PicklistValueDAO +import persistence.dao.PropertyDAO +import persistence.dao.TermDAO +import persistence.dao.TermPropertyValueDAO +import persistence.dao.TermbaseDAO import org.koin.dsl.module -import repository.EntryPropertyValueRepository -import repository.EntryRepository -import repository.FlagsRepository -import repository.InputDescriptorRepository -import repository.LanguageNameRepository -import repository.LanguagePropertyValueRepository -import repository.LanguageRepository -import repository.PropertyRepository -import repository.TermPropertyValueRepository -import repository.TermRepository -import repository.TermbaseRepository +import repo.EntryPropertyValueRepository +import repo.EntryRepository +import repo.FlagsRepository +import repo.InputDescriptorRepository +import repo.LanguageNameRepository +import repo.LanguagePropertyValueRepository +import repo.LanguageRepository +import repo.PropertyRepository +import repo.TermPropertyValueRepository +import repo.TermRepository +import repo.TermbaseRepository -internal val repoKoinModule = module { +val repoModule = module { single { val dao: LanguageDAO by inject() LanguageRepository(languageDAO = dao) diff --git a/core-repository/src/jvmMain/kotlin/di/RepositoryModule.kt b/core-repository/src/jvmMain/kotlin/di/RepositoryModule.kt deleted file mode 100644 index 1d45059..0000000 --- a/core-repository/src/jvmMain/kotlin/di/RepositoryModule.kt +++ /dev/null @@ -1,7 +0,0 @@ -package di - -import org.koin.dsl.module - -val repositoryKoinModule = module { - includes(listOf(repoKoinModule, useCaseKoinModule)) -} \ No newline at end of file diff --git a/core-repository/src/jvmMain/kotlin/repository/di/Module.kt b/core-repository/src/jvmMain/kotlin/repository/di/Module.kt new file mode 100644 index 0000000..31a73d4 --- /dev/null +++ b/core-repository/src/jvmMain/kotlin/repository/di/Module.kt @@ -0,0 +1,12 @@ +package repository.di + +import org.koin.dsl.module +import usecase.di.usecaseModule +import repo.di.repoModule + +val repositoryModule = module { + includes( + repoModule, + usecaseModule, + ) +} \ No newline at end of file diff --git a/core-repository/tests/gradle.properties b/core-repository/tests/gradle.properties index 056070a..ca25374 100644 --- a/core-repository/tests/gradle.properties +++ b/core-repository/tests/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-repository/tests/src/jvmMain/kotlin/repository/EntryRepositoryTest.kt b/core-repository/tests/src/jvmMain/kotlin/repo/EntryRepositoryTest.kt similarity index 96% rename from core-repository/tests/src/jvmMain/kotlin/repository/EntryRepositoryTest.kt rename to core-repository/tests/src/jvmMain/kotlin/repo/EntryRepositoryTest.kt index f1defb5..ba8cd0b 100644 --- a/core-repository/tests/src/jvmMain/kotlin/repository/EntryRepositoryTest.kt +++ b/core-repository/tests/src/jvmMain/kotlin/repo/EntryRepositoryTest.kt @@ -1,20 +1,18 @@ -package repository +package repo -import AppDatabase import MockFileManager import data.EntryModel import data.TermbaseModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.time.Duration.Companion.seconds -@OptIn(ExperimentalCoroutinesApi::class) class EntryRepositoryTest { private lateinit var sut: EntryRepository diff --git a/core-repository/tests/src/jvmMain/kotlin/repository/FlagsRepositoryTest.kt b/core-repository/tests/src/jvmMain/kotlin/repo/FlagsRepositoryTest.kt similarity index 98% rename from core-repository/tests/src/jvmMain/kotlin/repository/FlagsRepositoryTest.kt rename to core-repository/tests/src/jvmMain/kotlin/repo/FlagsRepositoryTest.kt index bc859d7..1d1acff 100644 --- a/core-repository/tests/src/jvmMain/kotlin/repository/FlagsRepositoryTest.kt +++ b/core-repository/tests/src/jvmMain/kotlin/repo/FlagsRepositoryTest.kt @@ -1,4 +1,4 @@ -package repository +package repo import kotlin.test.Test diff --git a/core-repository/tests/src/jvmMain/kotlin/repository/LanguageNameRepositoryTest.kt b/core-repository/tests/src/jvmMain/kotlin/repo/LanguageNameRepositoryTest.kt similarity index 98% rename from core-repository/tests/src/jvmMain/kotlin/repository/LanguageNameRepositoryTest.kt rename to core-repository/tests/src/jvmMain/kotlin/repo/LanguageNameRepositoryTest.kt index 8409c02..e50ffb3 100644 --- a/core-repository/tests/src/jvmMain/kotlin/repository/LanguageNameRepositoryTest.kt +++ b/core-repository/tests/src/jvmMain/kotlin/repo/LanguageNameRepositoryTest.kt @@ -1,4 +1,4 @@ -package repository +package repo import L10n import kotlin.test.Test diff --git a/core-repository/tests/src/jvmMain/kotlin/repository/LanguageRepositoryTest.kt b/core-repository/tests/src/jvmMain/kotlin/repo/LanguageRepositoryTest.kt similarity index 97% rename from core-repository/tests/src/jvmMain/kotlin/repository/LanguageRepositoryTest.kt rename to core-repository/tests/src/jvmMain/kotlin/repo/LanguageRepositoryTest.kt index c877f68..237f29c 100644 --- a/core-repository/tests/src/jvmMain/kotlin/repository/LanguageRepositoryTest.kt +++ b/core-repository/tests/src/jvmMain/kotlin/repo/LanguageRepositoryTest.kt @@ -1,16 +1,15 @@ -package repository +package repo -import AppDatabase import MockFileManager import data.LanguageModel import data.TermbaseModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import persistence.AppDatabase import kotlin.test.BeforeTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class LanguageRepositoryTest { private lateinit var sut: LanguageRepository diff --git a/core-persistence/tests/src/jvmMain/kotlin/MockFileManager.kt b/core-repository/tests/src/jvmMain/kotlin/repo/MockFileManager.kt similarity index 92% rename from core-persistence/tests/src/jvmMain/kotlin/MockFileManager.kt rename to core-repository/tests/src/jvmMain/kotlin/repo/MockFileManager.kt index a8bd42f..375773d 100644 --- a/core-persistence/tests/src/jvmMain/kotlin/MockFileManager.kt +++ b/core-repository/tests/src/jvmMain/kotlin/repo/MockFileManager.kt @@ -1,4 +1,4 @@ -import files.FileManager +import common.files.FileManager import java.io.File internal object MockFileManager : FileManager { diff --git a/core-repository/tests/src/jvmMain/kotlin/repository/TermbaseRepositoryTest.kt b/core-repository/tests/src/jvmMain/kotlin/repo/TermbaseRepositoryTest.kt similarity index 95% rename from core-repository/tests/src/jvmMain/kotlin/repository/TermbaseRepositoryTest.kt rename to core-repository/tests/src/jvmMain/kotlin/repo/TermbaseRepositoryTest.kt index df7f39d..32210b3 100644 --- a/core-repository/tests/src/jvmMain/kotlin/repository/TermbaseRepositoryTest.kt +++ b/core-repository/tests/src/jvmMain/kotlin/repo/TermbaseRepositoryTest.kt @@ -1,18 +1,16 @@ -package repository +package repo -import AppDatabase import MockFileManager import data.TermbaseModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout +import persistence.AppDatabase import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.time.Duration.Companion.seconds -@OptIn(ExperimentalCoroutinesApi::class) class TermbaseRepositoryTest { private lateinit var sut: TermbaseRepository diff --git a/core-repository/usecase/build.gradle.kts b/core-repository/usecase/build.gradle.kts index ff07231..92d3b78 100644 --- a/core-repository/usecase/build.gradle.kts +++ b/core-repository/usecase/build.gradle.kts @@ -23,6 +23,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) + implementation(libs.koin) implementation(libs.redundent) implementation(projects.coreCommon) diff --git a/core-repository/usecase/gradle.properties b/core-repository/usecase/gradle.properties index 056070a..ca25374 100644 --- a/core-repository/usecase/gradle.properties +++ b/core-repository/usecase/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteEntryUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteEntryUseCase.kt index 8268eb8..313a997 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteEntryUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteEntryUseCase.kt @@ -2,13 +2,13 @@ package usecase import data.EntryModel import data.PropertyType -import repository.EntryPropertyValueRepository -import repository.EntryRepository -import repository.LanguagePropertyValueRepository -import repository.LanguageRepository -import repository.PropertyRepository -import repository.TermPropertyValueRepository -import repository.TermRepository +import repo.EntryPropertyValueRepository +import repo.EntryRepository +import repo.LanguagePropertyValueRepository +import repo.LanguageRepository +import repo.PropertyRepository +import repo.TermPropertyValueRepository +import repo.TermRepository import java.io.File class DeleteEntryUseCase( diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermUseCase.kt index dd1bd12..32695b5 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermUseCase.kt @@ -1,7 +1,7 @@ package usecase -import repository.TermPropertyValueRepository -import repository.TermRepository +import repo.TermPropertyValueRepository +import repo.TermRepository class DeleteTermUseCase( private val termRepository: TermRepository, diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermbaseLanguageUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermbaseLanguageUseCase.kt index 356825f..2f4b34e 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermbaseLanguageUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermbaseLanguageUseCase.kt @@ -1,10 +1,10 @@ package usecase -import repository.EntryRepository -import repository.LanguagePropertyValueRepository -import repository.LanguageRepository -import repository.TermPropertyValueRepository -import repository.TermRepository +import repo.EntryRepository +import repo.LanguagePropertyValueRepository +import repo.LanguageRepository +import repo.TermPropertyValueRepository +import repo.TermRepository class DeleteTermbaseLanguageUseCase( private val entryRepository: EntryRepository, diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermbaseUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermbaseUseCase.kt index fbf95df..50592ca 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermbaseUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/DeleteTermbaseUseCase.kt @@ -3,14 +3,14 @@ package usecase import data.EntryModel import data.PropertyType import data.TermbaseModel -import repository.EntryPropertyValueRepository -import repository.EntryRepository -import repository.LanguagePropertyValueRepository -import repository.LanguageRepository -import repository.PropertyRepository -import repository.TermPropertyValueRepository -import repository.TermRepository -import repository.TermbaseRepository +import repo.EntryPropertyValueRepository +import repo.EntryRepository +import repo.LanguagePropertyValueRepository +import repo.LanguageRepository +import repo.PropertyRepository +import repo.TermPropertyValueRepository +import repo.TermRepository +import repo.TermbaseRepository import java.io.File class DeleteTermbaseUseCase( diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/ExportCsvUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/ExportCsvUseCase.kt index 4ced0d1..6c0cae1 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/ExportCsvUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/ExportCsvUseCase.kt @@ -3,9 +3,9 @@ package usecase import data.EntryModel import data.TermModel import data.TermbaseModel -import repository.EntryRepository -import repository.LanguageRepository -import repository.TermRepository +import repo.EntryRepository +import repo.LanguageRepository +import repo.TermRepository import java.io.File import java.io.FileWriter import kotlin.math.max diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/ExportTbxUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/ExportTbxUseCase.kt index efeac87..282a6af 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/ExportTbxUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/ExportTbxUseCase.kt @@ -4,10 +4,10 @@ import data.EntryModel import data.PropertyLevel import data.TermbaseModel import org.redundent.kotlin.xml.xml -import repository.EntryRepository -import repository.PropertyRepository -import repository.TermPropertyValueRepository -import repository.TermRepository +import repo.EntryRepository +import repo.PropertyRepository +import repo.TermPropertyValueRepository +import repo.TermRepository import usecase.dto.TermWithDefinition import java.io.File import java.io.FileWriter diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/ImportCsvUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/ImportCsvUseCase.kt index 3bc8f56..eea7488 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/ImportCsvUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/ImportCsvUseCase.kt @@ -3,9 +3,9 @@ package usecase import data.EntryModel import data.TermModel import data.TermbaseModel -import repository.EntryRepository -import repository.LanguageRepository -import repository.TermRepository +import repo.EntryRepository +import repo.LanguageRepository +import repo.TermRepository import java.io.File class ImportCsvUseCase( diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/ImportTbxUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/ImportTbxUseCase.kt index 1a70244..878e493 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/ImportTbxUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/ImportTbxUseCase.kt @@ -9,11 +9,11 @@ import kotlinx.coroutines.CompletableDeferred import org.xml.sax.Attributes import org.xml.sax.InputSource import org.xml.sax.helpers.DefaultHandler -import repository.EntryRepository -import repository.LanguageRepository -import repository.PropertyRepository -import repository.TermPropertyValueRepository -import repository.TermRepository +import repo.EntryRepository +import repo.LanguageRepository +import repo.PropertyRepository +import repo.TermPropertyValueRepository +import repo.TermRepository import usecase.dto.TermWithDefinition import java.io.File import javax.xml.parsers.SAXParserFactory diff --git a/core-repository/usecase/src/jvmMain/kotlin/usecase/SearchTermsUseCase.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/SearchTermsUseCase.kt index deb9b7e..ce52e3d 100644 --- a/core-repository/usecase/src/jvmMain/kotlin/usecase/SearchTermsUseCase.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/SearchTermsUseCase.kt @@ -2,7 +2,7 @@ package usecase import data.SearchCriterion import data.TermModel -import repository.TermRepository +import repo.TermRepository class SearchTermsUseCase( private val termRepository: TermRepository, diff --git a/core-repository/src/jvmMain/kotlin/di/UseCaseModule.kt b/core-repository/usecase/src/jvmMain/kotlin/usecase/di/UseCaseModule.kt similarity index 93% rename from core-repository/src/jvmMain/kotlin/di/UseCaseModule.kt rename to core-repository/usecase/src/jvmMain/kotlin/usecase/di/UseCaseModule.kt index 9ada677..c594c76 100644 --- a/core-repository/src/jvmMain/kotlin/di/UseCaseModule.kt +++ b/core-repository/usecase/src/jvmMain/kotlin/usecase/di/UseCaseModule.kt @@ -1,14 +1,14 @@ -package di +package usecase.di import org.koin.dsl.module -import repository.EntryPropertyValueRepository -import repository.EntryRepository -import repository.LanguagePropertyValueRepository -import repository.LanguageRepository -import repository.PropertyRepository -import repository.TermPropertyValueRepository -import repository.TermRepository -import repository.TermbaseRepository +import repo.EntryPropertyValueRepository +import repo.EntryRepository +import repo.LanguagePropertyValueRepository +import repo.LanguageRepository +import repo.PropertyRepository +import repo.TermPropertyValueRepository +import repo.TermRepository +import repo.TermbaseRepository import usecase.DeleteEntryUseCase import usecase.DeleteTermUseCase import usecase.DeleteTermbaseLanguageUseCase @@ -19,7 +19,7 @@ import usecase.ImportCsvUseCase import usecase.ImportTbxUseCase import usecase.SearchTermsUseCase -internal val useCaseKoinModule = module { +val usecaseModule = module { single { val entryRepository: EntryRepository by inject() val languageRepository: LanguageRepository by inject() diff --git a/feature-base/build.gradle.kts b/feature-base/build.gradle.kts index 1b9b6e6..1d23950 100644 --- a/feature-base/build.gradle.kts +++ b/feature-base/build.gradle.kts @@ -22,7 +22,6 @@ kotlin { dependencies { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) - implementation(libs.essenty.instancekeeper) implementation(compose.foundation) implementation(compose.animation) diff --git a/feature-base/gradle.properties b/feature-base/gradle.properties index 056070a..ca25374 100644 --- a/feature-base/gradle.properties +++ b/feature-base/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-base/intro/build.gradle.kts b/feature-base/intro/build.gradle.kts index 59cf615..8ddc94c 100644 --- a/feature-base/intro/build.gradle.kts +++ b/feature-base/intro/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-base/intro/gradle.properties b/feature-base/intro/gradle.properties index 056070a..ca25374 100644 --- a/feature-base/intro/gradle.properties +++ b/feature-base/intro/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-base/intro/src/jvmMain/kotlin/intro/di/Module.kt b/feature-base/intro/src/jvmMain/kotlin/intro/di/Module.kt new file mode 100644 index 0000000..8f844c1 --- /dev/null +++ b/feature-base/intro/src/jvmMain/kotlin/intro/di/Module.kt @@ -0,0 +1,17 @@ +package intro.di + +import intro.ui.DefaultIntroComponent +import intro.ui.IntroComponent +import org.koin.dsl.module + +val introModule = module { + factory { + DefaultIntroComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + termbaseRepository = get(), + notificationCenter = get(), + ) + } +} \ No newline at end of file diff --git a/feature-base/intro/src/jvmMain/kotlin/intro/ui/DefaultIntroComponent.kt b/feature-base/intro/src/jvmMain/kotlin/intro/ui/DefaultIntroComponent.kt new file mode 100644 index 0000000..915783e --- /dev/null +++ b/feature-base/intro/src/jvmMain/kotlin/intro/ui/DefaultIntroComponent.kt @@ -0,0 +1,62 @@ +package intro.ui + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider +import data.TermbaseModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import common.notification.NotificationCenter +import repo.TermbaseRepository +import kotlin.coroutines.CoroutineContext + +internal class DefaultIntroComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, + private val dispatcherProvider: CoroutineDispatcherProvider, + private val termbaseRepository: TermbaseRepository, + private val notificationCenter: NotificationCenter, +) : IntroComponent, ComponentContext by componentContext { + + private var termbases = MutableStateFlow>(emptyList()) + private lateinit var viewModelScope: CoroutineScope + + override lateinit var uiState: StateFlow + + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine(termbases) { + IntroUiState( + termbases = it.first() + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = IntroUiState() + ) + } + doOnDestroy { + viewModelScope.cancel() + } + } + } + + override fun load() { + viewModelScope.launch(dispatcherProvider.io) { + val values = termbaseRepository.getAll() + termbases.value = values + } + } + + override fun openTermbase(termbase: TermbaseModel) { + val termbaseId = termbase.id + notificationCenter.send(NotificationCenter.Event.OpenTermbase(termbaseId)) + } +} + diff --git a/feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroComponent.kt b/feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroComponent.kt new file mode 100644 index 0000000..71fe1d9 --- /dev/null +++ b/feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroComponent.kt @@ -0,0 +1,11 @@ +package intro.ui + +import data.TermbaseModel +import kotlinx.coroutines.flow.StateFlow + +interface IntroComponent { + val uiState: StateFlow + + fun load() + fun openTermbase(termbase: TermbaseModel) +} \ No newline at end of file diff --git a/feature-base/intro/src/jvmMain/kotlin/ui/screens/intro/IntroScreen.kt b/feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroScreen.kt similarity index 84% rename from feature-base/intro/src/jvmMain/kotlin/ui/screens/intro/IntroScreen.kt rename to feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroScreen.kt index d69039f..5f06fb5 100644 --- a/feature-base/intro/src/jvmMain/kotlin/ui/screens/intro/IntroScreen.kt +++ b/feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroScreen.kt @@ -1,4 +1,4 @@ -package ui.screens.intro +package intro.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -20,22 +20,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.arkivanov.essenty.instancekeeper.getOrCreate import localized -import org.koin.java.KoinJavaComponent.inject -import ui.theme.SelectedBackground -import ui.theme.Spacing -import utils.AppBusiness +import common.ui.theme.SelectedBackground +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable -fun IntroScreen() { - val viewModel: IntroViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: IntroViewModel by inject(IntroViewModel::class.java) - res - } +fun IntroScreen( + component: IntroComponent, +) { SideEffect { - viewModel.load() + component.load() } Column( @@ -53,7 +48,7 @@ fun IntroScreen() { ) Spacer(Modifier.height(Spacing.m)) - val uiState by viewModel.uiState.collectAsState() + val uiState by component.uiState.collectAsState() if (uiState.termbases.isEmpty()) { Text( text = "app_intro_empty".localized(), @@ -77,7 +72,7 @@ fun IntroScreen() { .fillMaxWidth() .background(color = SelectedBackground, shape = RoundedCornerShape(4.dp)) .padding(Spacing.m).onClick { - viewModel.openTermbase(termbase) + component.openTermbase(termbase) }, horizontalArrangement = Arrangement.spacedBy(Spacing.m) ) { diff --git a/feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroUiState.kt b/feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroUiState.kt new file mode 100644 index 0000000..c96821e --- /dev/null +++ b/feature-base/intro/src/jvmMain/kotlin/intro/ui/IntroUiState.kt @@ -0,0 +1,7 @@ +package intro.ui + +import data.TermbaseModel + +data class IntroUiState( + val termbases: List = emptyList(), +) \ No newline at end of file diff --git a/feature-base/intro/src/jvmMain/kotlin/ui/screens/intro/IntroViewModel.kt b/feature-base/intro/src/jvmMain/kotlin/ui/screens/intro/IntroViewModel.kt deleted file mode 100644 index a4c1303..0000000 --- a/feature-base/intro/src/jvmMain/kotlin/ui/screens/intro/IntroViewModel.kt +++ /dev/null @@ -1,55 +0,0 @@ -package ui.screens.intro - -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import data.TermbaseModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import notification.NotificationCenter -import repository.TermbaseRepository -import coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel - -class IntroViewModel( - private val dispatcherProvider: CoroutineDispatcherProvider, - private val termbaseRepository: TermbaseRepository, - private val notificationCenter: NotificationCenter, -) : InstanceKeeper.Instance { - - private var termbases = MutableStateFlow>(emptyList()) - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val uiState = combine(termbases) { - IntroUiState( - termbases = it.first() - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = IntroUiState() - ) - - override fun onDestroy() { - viewModelScope.cancel() - } - - fun load() { - viewModelScope.launch(dispatcherProvider.io) { - val values = termbaseRepository.getAll() - termbases.value = values - } - } - - fun openTermbase(termbase: TermbaseModel) { - val termbaseId = termbase.id - notificationCenter.send(NotificationCenter.Event.OpenTermbase(termbaseId)) - } -} - -data class IntroUiState( - val termbases: List = emptyList(), -) \ No newline at end of file diff --git a/feature-base/main/build.gradle.kts b/feature-base/main/build.gradle.kts index 64ae7b6..9b352c2 100644 --- a/feature-base/main/build.gradle.kts +++ b/feature-base/main/build.gradle.kts @@ -23,7 +23,8 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) + implementation(libs.decompose.extensions) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-base/main/gradle.properties b/feature-base/main/gradle.properties index 056070a..ca25374 100644 --- a/feature-base/main/gradle.properties +++ b/feature-base/main/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-base/main/src/jvmMain/kotlin/main/di/Module.kt b/feature-base/main/src/jvmMain/kotlin/main/di/Module.kt new file mode 100644 index 0000000..768249e --- /dev/null +++ b/feature-base/main/src/jvmMain/kotlin/main/di/Module.kt @@ -0,0 +1,16 @@ +package main.di + +import main.ui.MainComponent +import main.ui.DefaultMainComponent +import org.koin.dsl.module + +val mainModule = module { + factory { + DefaultMainComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + entryRepository = get(), + ) + } +} \ No newline at end of file diff --git a/feature-base/main/src/jvmMain/kotlin/main/ui/DefaultMainComponent.kt b/feature-base/main/src/jvmMain/kotlin/main/ui/DefaultMainComponent.kt new file mode 100644 index 0000000..8fa389d --- /dev/null +++ b/feature-base/main/src/jvmMain/kotlin/main/ui/DefaultMainComponent.kt @@ -0,0 +1,80 @@ +package main.ui + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.arkivanov.decompose.router.slot.childSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider +import common.utils.getByInjection +import data.TermbaseModel +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import repo.EntryRepository +import terms.ui.TermsComponent +import kotlin.coroutines.CoroutineContext + +internal class DefaultMainComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, + private val dispatcherProvider: CoroutineDispatcherProvider, + private val entryRepository: EntryRepository, +) : MainComponent, ComponentContext by componentContext { + + private val termbase = MutableStateFlow(null) + private val entryCount = MutableStateFlow(0) + private var observeEntriesJob: Job? = null + private lateinit var viewModelScope: CoroutineScope + private val termsNavigation = SlotNavigation() + + override lateinit var uiState: StateFlow + override val terms: Value> = childSlot( + source = termsNavigation, + key = "MainTermsSlot", + childFactory = { _, context -> + getByInjection(context, coroutineContext) + } + ) + + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + termbase, + entryCount, + ) { termbase, entryCount -> + MainUiState( + termbase = termbase, + entryCount = entryCount, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = MainUiState() + ) + termsNavigation.activate(MainComponent.TermsConfig) + } + doOnDestroy { + viewModelScope.cancel() + } + } + } + + override fun load(termbase: TermbaseModel) { + this.termbase.value = termbase + + viewModelScope.launch(dispatcherProvider.io) { + entryCount.value = entryRepository.getAll(termbase.id).count() + observeEntriesJob?.cancel() + observeEntriesJob = launch { + entryRepository.observeEntries(termbase.id).collect { entries -> + entryCount.value = entries.count() + } + } + } + } +} diff --git a/feature-base/main/src/jvmMain/kotlin/main/ui/MainComponent.kt b/feature-base/main/src/jvmMain/kotlin/main/ui/MainComponent.kt new file mode 100644 index 0000000..bee5cd5 --- /dev/null +++ b/feature-base/main/src/jvmMain/kotlin/main/ui/MainComponent.kt @@ -0,0 +1,18 @@ +package main.ui + +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import data.TermbaseModel +import kotlinx.coroutines.flow.StateFlow + +interface MainComponent { + val uiState: StateFlow + val terms: Value> + + fun load(termbase: TermbaseModel) + + @Parcelize + object TermsConfig: Parcelable +} \ No newline at end of file diff --git a/feature-base/main/src/jvmMain/kotlin/main/ui/MainScreen.kt b/feature-base/main/src/jvmMain/kotlin/main/ui/MainScreen.kt new file mode 100644 index 0000000..13ba2bb --- /dev/null +++ b/feature-base/main/src/jvmMain/kotlin/main/ui/MainScreen.kt @@ -0,0 +1,52 @@ +package main.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import data.TermbaseModel +import terms.ui.TermsComponent +import terms.ui.TermsScreen + +@Composable +fun MainScreen( + component: MainComponent, + termbase: TermbaseModel, + modifier: Modifier = Modifier, +) { + val termsContent by component.terms.subscribeAsState() + LaunchedEffect(termbase) { + component.load(termbase) + } + + val uiState by component.uiState.collectAsState() + + Column( + modifier = modifier, + ) { + when (termsContent.child?.configuration) { + MainComponent.TermsConfig -> { + val childComponent = termsContent.child?.instance as TermsComponent + LaunchedEffect(childComponent) { + childComponent.load(termbase) + } + TermsScreen( + component = childComponent, + modifier = Modifier.weight(1f).fillMaxWidth(), + ) + } + + else -> Unit + } + + StatusBar( + modifier = Modifier.fillMaxWidth(), + termbaseName = uiState.termbase?.name ?: "", + entryCount = uiState.entryCount, + ) + } +} diff --git a/feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainUiState.kt b/feature-base/main/src/jvmMain/kotlin/main/ui/MainUiState.kt similarity index 83% rename from feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainUiState.kt rename to feature-base/main/src/jvmMain/kotlin/main/ui/MainUiState.kt index 5e06563..e55b151 100644 --- a/feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainUiState.kt +++ b/feature-base/main/src/jvmMain/kotlin/main/ui/MainUiState.kt @@ -1,4 +1,4 @@ -package ui.screens.main +package main.ui import data.TermbaseModel diff --git a/feature-base/main/src/jvmMain/kotlin/ui/screens/main/StatusBar.kt b/feature-base/main/src/jvmMain/kotlin/main/ui/StatusBar.kt similarity index 96% rename from feature-base/main/src/jvmMain/kotlin/ui/screens/main/StatusBar.kt rename to feature-base/main/src/jvmMain/kotlin/main/ui/StatusBar.kt index 5f23db3..0af708f 100644 --- a/feature-base/main/src/jvmMain/kotlin/ui/screens/main/StatusBar.kt +++ b/feature-base/main/src/jvmMain/kotlin/main/ui/StatusBar.kt @@ -1,4 +1,4 @@ -package ui.screens.main +package main.ui import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -9,7 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import localized -import ui.theme.Spacing +import common.ui.theme.Spacing @Composable internal fun StatusBar( diff --git a/feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainScreen.kt b/feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainScreen.kt deleted file mode 100644 index 2078f5f..0000000 --- a/feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainScreen.kt +++ /dev/null @@ -1,60 +0,0 @@ -package ui.screens.main - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import com.arkivanov.essenty.instancekeeper.getOrCreate -import data.TermbaseModel -import org.koin.java.KoinJavaComponent.inject -import ui.TermsScreen -import ui.TermsViewModel -import ui.detail.TermDetailViewModel -import ui.dialog.filter.TermFilterViewModel -import utils.AppBusiness - -@Composable -fun MainScreen( - termbase: TermbaseModel, - modifier: Modifier = Modifier, -) { - val mainViewModel: MainViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: MainViewModel by inject(MainViewModel::class.java) - res - } - val termsViewModel: TermsViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: TermsViewModel by inject(TermsViewModel::class.java) - res - } - val termDetailViewModel: TermDetailViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: TermDetailViewModel by inject(TermDetailViewModel::class.java) - res - } - val termFilterViewModel: TermFilterViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: TermFilterViewModel by inject(TermFilterViewModel::class.java) - res - } - LaunchedEffect(termbase) { - mainViewModel.load(termbase) - termsViewModel.load(termbase) - } - - val uiState by mainViewModel.uiState.collectAsState() - - Column( - modifier = modifier, - ) { - TermsScreen( - modifier = Modifier.weight(1f).fillMaxWidth(), - ) - - StatusBar( - modifier = Modifier.fillMaxWidth(), - termbaseName = uiState.termbase?.name ?: "", - entryCount = uiState.entryCount, - ) - } -} diff --git a/feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainViewModel.kt b/feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainViewModel.kt deleted file mode 100644 index 751c3ff..0000000 --- a/feature-base/main/src/jvmMain/kotlin/ui/screens/main/MainViewModel.kt +++ /dev/null @@ -1,58 +0,0 @@ -package ui.screens.main - -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider -import data.TermbaseModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import repository.EntryRepository - -class MainViewModel( - private val dispatcherProvider: CoroutineDispatcherProvider, - private val entryRepository: EntryRepository, -) : InstanceKeeper.Instance { - - private val termbase = MutableStateFlow(null) - private val entryCount = MutableStateFlow(0) - private var observeEntriesJob: Job? = null - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val uiState = combine( - termbase, - entryCount, - ) { termbase, entryCount -> - MainUiState( - termbase = termbase, - entryCount = entryCount, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = MainUiState() - ) - - override fun onDestroy() { - viewModelScope.cancel() - } - - fun load(termbase: TermbaseModel) { - this.termbase.value = termbase - - viewModelScope.launch(dispatcherProvider.io) { - entryCount.value = entryRepository.getAll(termbase.id).count() - observeEntriesJob?.cancel() - observeEntriesJob = launch { - entryRepository.observeEntries(termbase.id).collect { entries -> - entryCount.value = entries.count() - } - } - } - } -} diff --git a/feature-termbases/build.gradle.kts b/feature-termbases/build.gradle.kts index 65e6bfb..28a4f0f 100644 --- a/feature-termbases/build.gradle.kts +++ b/feature-termbases/build.gradle.kts @@ -22,7 +22,6 @@ kotlin { dependencies { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) - implementation(libs.essenty.instancekeeper) implementation(compose.foundation) implementation(compose.animation) @@ -35,14 +34,14 @@ kotlin { implementation(projects.coreRepository) implementation(projects.coreLocalization) - api(projects.featureTermbases.statistics) - api(projects.featureTermbases.management) - api(projects.featureTermbases.create) - api(projects.featureTermbases.edit) + api(projects.featureTermbases.dialog.statistics) + api(projects.featureTermbases.dialog.management) + api(projects.featureTermbases.dialog.create) + api(projects.featureTermbases.dialog.edit) - api(projects.featureTermbases.metadata) - api(projects.featureTermbases.definitionmodel) - api(projects.featureTermbases.inputmodel) + api(projects.featureTermbases.wizard.metadata) + api(projects.featureTermbases.wizard.definitionmodel) + api(projects.featureTermbases.wizard.inputmodel) } } val jvmTest by getting diff --git a/feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseViewModel.kt b/feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseViewModel.kt deleted file mode 100644 index 8951915..0000000 --- a/feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseViewModel.kt +++ /dev/null @@ -1,151 +0,0 @@ -package ui.dialog.create - -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider -import data.InputDescriptorModel -import data.LanguageModel -import data.PropertyModel -import data.TermbaseModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.updateAndGet -import kotlinx.coroutines.launch -import notification.NotificationCenter -import repository.InputDescriptorRepository -import repository.LanguageRepository -import repository.PropertyRepository -import repository.TermbaseRepository -import kotlin.math.max - -class CreateTermbaseViewModel( - private val dispatcherProvider: CoroutineDispatcherProvider, - private val termbaseRepository: TermbaseRepository, - private val languageRepository: LanguageRepository, - private val propertyRepository: PropertyRepository, - private val inputDescriptorRepository: InputDescriptorRepository, - private val notificationCenter: NotificationCenter, -) : InstanceKeeper.Instance { - - companion object { - const val STEPS = 3 - } - - private val step = MutableStateFlow(0) - private val loading = MutableStateFlow(false) - private val done = MutableStateFlow(false) - - private var termbase: TermbaseModel? = null - private var languages: List = emptyList() - private var properties: List = emptyList() - private var inputDescriptors: List = emptyList() - private var openNewlyCreated: Boolean = false - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val uiState = combine( - step, - loading, - done, - ) { step, loading, done -> - CreateTermbaseUiState( - step = step, - loading = loading, - done = done, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = CreateTermbaseUiState(), - ) - - override fun onDestroy() { - viewModelScope.cancel() - } - - fun reset() { - step.value = 0 - loading.value = false - done.value = false - termbase = null - languages = emptyList() - properties = emptyList() - inputDescriptors = emptyList() - openNewlyCreated = false - } - - fun setOpenNewlyCreated(value: Boolean) { - openNewlyCreated = value - } - - fun setTermbase(termbase: TermbaseModel) { - this.termbase = termbase - } - - fun setSelectedLanguages(value: List) { - languages = value - } - - fun setProperties(value: List) { - properties = value - } - - fun getProperties() = properties - - fun getLanguages() = languages - - fun getInputModelDescriptors() = inputDescriptors - - fun setInputModelDescriptors(value: List) { - inputDescriptors = value - } - - fun previous() { - step.updateAndGet { - max(0, it - 1) - } - } - - fun next() { - step.updateAndGet { - it + 1 - } - } - - fun submit() { - val termbase = termbase ?: return - loading.value = true - viewModelScope.launch(dispatcherProvider.io) { - val termbaseId = termbaseRepository.create(termbase) - - // create languages - for (language in languages) { - val lang = language.copy(termbaseId = termbaseId) - languageRepository.create(lang) - } - - // create definition model - for (property in properties) { - val prop = property.copy(termbaseId = termbaseId) - propertyRepository.create(prop) - } - - // create input model - for (descriptor in inputDescriptors) { - val desc = descriptor.copy(termbaseId = termbaseId) - inputDescriptorRepository.create(desc) - } - - // open new termbase if needed - if (openNewlyCreated) { - notificationCenter.send(NotificationCenter.Event.OpenTermbase(termbaseId)) - } - - loading.value = false - done.value = true - } - } -} diff --git a/feature-termbases/create/.gitignore b/feature-termbases/dialog/.gitignore similarity index 100% rename from feature-termbases/create/.gitignore rename to feature-termbases/dialog/.gitignore diff --git a/feature-termbases/dialog/build.gradle.kts b/feature-termbases/dialog/build.gradle.kts new file mode 100644 index 0000000..277a19d --- /dev/null +++ b/feature-termbases/dialog/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") +} + +group = "feature.termbases.dialog" +version = libs.versions.appVersion.get() + +repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") +} + +kotlin { + jvm { + jvmToolchain(11) + withJava() + } + sourceSets { + val jvmMain by getting { + dependencies { + implementation(compose.desktop.currentOs) + } + } + val jvmTest by getting + } +} \ No newline at end of file diff --git a/feature-termbases/definitionmodel/.gitignore b/feature-termbases/dialog/create/.gitignore similarity index 100% rename from feature-termbases/definitionmodel/.gitignore rename to feature-termbases/dialog/create/.gitignore diff --git a/feature-termbases/create/build.gradle.kts b/feature-termbases/dialog/create/build.gradle.kts similarity index 73% rename from feature-termbases/create/build.gradle.kts rename to feature-termbases/dialog/create/build.gradle.kts index 59d0b9f..9c32096 100644 --- a/feature-termbases/create/build.gradle.kts +++ b/feature-termbases/dialog/create/build.gradle.kts @@ -23,7 +23,8 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) + implementation(libs.decompose.extensions) implementation(projects.coreCommon) implementation(projects.coreData) @@ -31,9 +32,9 @@ kotlin { implementation(projects.coreRepository) implementation(projects.coreLocalization) - implementation(projects.featureTermbases.metadata) - implementation(projects.featureTermbases.definitionmodel) - implementation(projects.featureTermbases.inputmodel) + implementation(projects.featureTermbases.wizard.metadata) + implementation(projects.featureTermbases.wizard.definitionmodel) + implementation(projects.featureTermbases.wizard.inputmodel) } } val jvmTest by getting diff --git a/feature-termbases/inputmodel/gradle.properties b/feature-termbases/dialog/create/gradle.properties similarity index 76% rename from feature-termbases/inputmodel/gradle.properties rename to feature-termbases/dialog/create/gradle.properties index 056070a..ca25374 100644 --- a/feature-termbases/inputmodel/gradle.properties +++ b/feature-termbases/dialog/create/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-termbases/create/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/dialog/create/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from feature-termbases/create/gradle/wrapper/gradle-wrapper.jar rename to feature-termbases/dialog/create/gradle/wrapper/gradle-wrapper.jar diff --git a/feature-termbases/create/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/dialog/create/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from feature-termbases/create/gradle/wrapper/gradle-wrapper.properties rename to feature-termbases/dialog/create/gradle/wrapper/gradle-wrapper.properties diff --git a/feature-termbases/create/gradlew b/feature-termbases/dialog/create/gradlew similarity index 100% rename from feature-termbases/create/gradlew rename to feature-termbases/dialog/create/gradlew diff --git a/feature-termbases/create/gradlew.bat b/feature-termbases/dialog/create/gradlew.bat similarity index 100% rename from feature-termbases/create/gradlew.bat rename to feature-termbases/dialog/create/gradlew.bat diff --git a/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/di/Module.kt b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/di/Module.kt new file mode 100644 index 0000000..1a1e78c --- /dev/null +++ b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/di/Module.kt @@ -0,0 +1,20 @@ +package dialogcreate.di + +import dialogcreate.ui.CreateTermbaseComponent +import dialogcreate.ui.DefaultCreateTermbaseComponent +import org.koin.dsl.module + +val dialogCreateModule = module { + factory { + DefaultCreateTermbaseComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + termbaseRepository = get(), + languageRepository = get(), + propertyRepository = get(), + inputDescriptorRepository = get(), + notificationCenter = get(), + ) + } +} \ No newline at end of file diff --git a/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseComponent.kt b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseComponent.kt new file mode 100644 index 0000000..abfebf0 --- /dev/null +++ b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseComponent.kt @@ -0,0 +1,40 @@ +package dialogcreate.ui + +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import data.InputDescriptorModel +import data.LanguageModel +import data.PropertyModel +import data.TermbaseModel +import kotlinx.coroutines.flow.StateFlow + +interface CreateTermbaseComponent { + val uiState: StateFlow + val content: Value> + + fun reset() + fun setOpenNewlyCreated(value: Boolean) + fun setTermbase(termbase: TermbaseModel) + fun setSelectedLanguages(value: List) + fun setProperties(value: List) + fun getProperties(): List + fun getLanguages(): List + fun getInputModelDescriptors(): List + fun setInputModelDescriptors(value: List) + fun previous() + fun next() + fun submit() + + sealed interface ContentConfig : Parcelable { + @Parcelize + object Step1 : ContentConfig + + @Parcelize + object Step2 : ContentConfig + + @Parcelize + object Step3 : ContentConfig + } +} \ No newline at end of file diff --git a/feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseUiState.kt b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseUiState.kt similarity index 83% rename from feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseUiState.kt rename to feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseUiState.kt index 95b6c67..a6b108b 100644 --- a/feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseUiState.kt +++ b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseUiState.kt @@ -1,4 +1,4 @@ -package ui.dialog.create +package dialogcreate.ui data class CreateTermbaseUiState( val step: Int = 0, diff --git a/feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseWizard.kt b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseWizard.kt similarity index 53% rename from feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseWizard.kt rename to feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseWizard.kt index 144aa3c..e92bb02 100644 --- a/feature-termbases/create/src/jvmMain/kotlin/ui/dialog/create/CreateTermbaseWizard.kt +++ b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/CreateTermbaseWizard.kt @@ -1,6 +1,5 @@ -package ui.dialog.create +package dialogcreate.ui -import androidx.compose.animation.* import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -12,43 +11,25 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.rememberWindowState -import com.arkivanov.essenty.instancekeeper.getOrCreate +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import common.ui.theme.MetaTermTheme +import common.ui.theme.Spacing +import dialogcreate.stepone.ui.CreateTermbaseWizardStepOne +import dialogcreate.stepone.ui.CreateTermbaseWizardStepOneComponent +import dialogcreate.stepthree.ui.CreateTermbaseWizardStepThree +import dialogcreate.stepthree.ui.CreateTermbaseWizardStepThreeComponent +import dialogcreate.steptwo.ui.CreateTermbaseWizardStepTwo +import dialogcreate.steptwo.ui.CreateTermbaseWizardStepTwoComponent import kotlinx.coroutines.launch import localized -import org.koin.java.KoinJavaComponent.inject -import ui.dialog.create.stepone.CreateTermbaseWizardStepOne -import ui.dialog.create.stepone.CreateTermbaseWizardStepOneViewModel -import ui.dialog.create.stepthree.CreateTermbaseWizardStepThree -import ui.dialog.create.stepthree.CreateTermbaseWizardStepThreeViewModel -import ui.dialog.create.steptwo.CreateTermbaseWizardStepTwo -import ui.dialog.create.steptwo.CreateTermbaseWizardStepTwoViewModel -import ui.theme.MetaTermTheme -import ui.theme.Spacing -import utils.AppBusiness -@OptIn(ExperimentalAnimationApi::class) @Composable fun CreateTermbaseWizardDialog( + component: CreateTermbaseComponent, openNewlyCreated: Boolean = false, onClose: () -> Unit, ) { - val viewModel: CreateTermbaseViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseViewModel by inject(CreateTermbaseViewModel::class.java) - res - } - val stepOneViewModel: CreateTermbaseWizardStepOneViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepOneViewModel by inject(CreateTermbaseWizardStepOneViewModel::class.java) - res - } - val stepTwoViewModel: CreateTermbaseWizardStepTwoViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepTwoViewModel by inject(CreateTermbaseWizardStepTwoViewModel::class.java) - res - } - val stepThreeViewModel: CreateTermbaseWizardStepThreeViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepThreeViewModel by inject(CreateTermbaseWizardStepThreeViewModel::class.java) - res - } - + val content by component.content.subscribeAsState() MetaTermTheme { Window( title = "dialog_title_create_termbase".localized(), @@ -58,10 +39,10 @@ fun CreateTermbaseWizardDialog( onClose() }, ) { - val uiState by viewModel.uiState.collectAsState() + val uiState by component.uiState.collectAsState() LaunchedEffect(openNewlyCreated) { - viewModel.setOpenNewlyCreated(openNewlyCreated) + component.setOpenNewlyCreated(openNewlyCreated) } LaunchedEffect(uiState.done) { @@ -70,70 +51,75 @@ fun CreateTermbaseWizardDialog( } } - LaunchedEffect(stepOneViewModel) { - launch { - stepOneViewModel.done.collect { result -> - viewModel.setTermbase(result.first) - viewModel.setSelectedLanguages(result.second) - viewModel.next() - } - } - } - LaunchedEffect(stepTwoViewModel) { - launch { - stepTwoViewModel.done.collect { result -> - viewModel.setProperties(result) - viewModel.next() - } - } - } - LaunchedEffect(stepThreeViewModel) { - launch { - stepThreeViewModel.done.collect { result -> - viewModel.setInputModelDescriptors(result) - viewModel.submit() - } - } - } - Box( modifier = Modifier.size(800.dp, 600.dp) .background(MaterialTheme.colors.background), ) { - Column { - AnimatedContent( - modifier = Modifier.weight(1f).fillMaxWidth(), - targetState = uiState.step, - transitionSpec = { - fadeIn() with fadeOut() - }, - ) { - when (uiState.step) { - 0 -> CreateTermbaseWizardStepOne( - modifier = Modifier.fillMaxSize(), - ) + Column( + modifier = Modifier.fillMaxSize() + ) { + Column(modifier = Modifier.weight(1f)) { + when (content.child?.configuration) { + CreateTermbaseComponent.ContentConfig.Step1 -> { + val childComponent = content.child?.instance as CreateTermbaseWizardStepOneComponent + LaunchedEffect(childComponent) { + launch { + childComponent.done.collect { result -> + component.setTermbase(result.first) + component.setSelectedLanguages(result.second) + component.next() + } + } + } + CreateTermbaseWizardStepOne( + component = childComponent, + modifier = Modifier.fillMaxSize(), + ) + } - 1 -> { + CreateTermbaseComponent.ContentConfig.Step2 -> { + val childComponent = content.child?.instance as CreateTermbaseWizardStepTwoComponent + LaunchedEffect(childComponent) { + launch { + childComponent.done.collect { result -> + component.setProperties(result) + component.next() + } + } + } CreateTermbaseWizardStepTwo( + component = childComponent, modifier = Modifier.fillMaxSize(), ) } - else -> { + CreateTermbaseComponent.ContentConfig.Step3 -> { + val childComponent = content.child?.instance as CreateTermbaseWizardStepThreeComponent + LaunchedEffect(childComponent) { + launch { + childComponent.done.collect { result -> + component.setInputModelDescriptors(result) + component.submit() + } + } + } LaunchedEffect(uiState.step) { - val properties = viewModel.getProperties() - val languages = viewModel.getLanguages() - val descriptors = viewModel.getInputModelDescriptors() - stepThreeViewModel.loadItems( + val properties = component.getProperties() + val languages = component.getLanguages() + val descriptors = component.getInputModelDescriptors() + childComponent.loadItems( properties = properties, languages = languages, oldInputModel = descriptors, ) } CreateTermbaseWizardStepThree( + component = childComponent, modifier = Modifier.fillMaxSize(), ) } + + else -> Unit } } Spacer(modifier = Modifier.height(Spacing.s)) @@ -157,20 +143,25 @@ fun CreateTermbaseWizardDialog( modifier = Modifier.heightIn(max = 25.dp), contentPadding = PaddingValues(0.dp), onClick = { - viewModel.previous() + component.previous() }, ) { Text(text = "button_prev".localized(), style = MaterialTheme.typography.button) } } - if (uiState.step < CreateTermbaseViewModel.STEPS - 1) { + if (uiState.step < DefaultCreateTermbaseComponent.STEPS - 1) { Button( modifier = Modifier.heightIn(max = 25.dp), contentPadding = PaddingValues(0.dp), onClick = { when (uiState.step) { - 0 -> stepOneViewModel.submit() - 1 -> stepTwoViewModel.submit() + 0 -> { + (content.child?.instance as? CreateTermbaseWizardStepOneComponent)?.submit() + } + + 1 -> { + (content.child?.instance as? CreateTermbaseWizardStepTwoComponent)?.submit() + } } }, ) { @@ -181,7 +172,7 @@ fun CreateTermbaseWizardDialog( modifier = Modifier.heightIn(max = 25.dp), contentPadding = PaddingValues(0.dp), onClick = { - stepThreeViewModel.submit() + (content.child?.instance as? CreateTermbaseWizardStepThreeComponent)?.submit() }, ) { Text(text = "button_ok".localized(), style = MaterialTheme.typography.button) diff --git a/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/DefaultCreateTermbaseComponent.kt b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/DefaultCreateTermbaseComponent.kt new file mode 100644 index 0000000..4b4a230 --- /dev/null +++ b/feature-termbases/dialog/create/src/jvmMain/kotlin/dialogcreate/ui/DefaultCreateTermbaseComponent.kt @@ -0,0 +1,201 @@ +package dialogcreate.ui + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.arkivanov.decompose.router.slot.childSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider +import data.InputDescriptorModel +import data.LanguageModel +import data.PropertyModel +import data.TermbaseModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import common.notification.NotificationCenter +import common.utils.getByInjection +import dialogcreate.stepone.ui.CreateTermbaseWizardStepOneComponent +import dialogcreate.stepthree.ui.CreateTermbaseWizardStepThreeComponent +import dialogcreate.steptwo.ui.CreateTermbaseWizardStepTwoComponent +import repo.InputDescriptorRepository +import repo.LanguageRepository +import repo.PropertyRepository +import repo.TermbaseRepository +import kotlin.coroutines.CoroutineContext +import kotlin.math.max + +internal class DefaultCreateTermbaseComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, + private val dispatcherProvider: CoroutineDispatcherProvider, + private val termbaseRepository: TermbaseRepository, + private val languageRepository: LanguageRepository, + private val propertyRepository: PropertyRepository, + private val inputDescriptorRepository: InputDescriptorRepository, + private val notificationCenter: NotificationCenter, +) : CreateTermbaseComponent, ComponentContext by componentContext { + + companion object { + const val STEPS = 3 + } + + private val step = MutableStateFlow(0) + private val loading = MutableStateFlow(false) + private val done = MutableStateFlow(false) + + private var termbase: TermbaseModel? = null + private var languages: List = emptyList() + private var properties: List = emptyList() + private var inputDescriptors: List = emptyList() + private var openNewlyCreated: Boolean = false + private lateinit var viewModelScope: CoroutineScope + private val contentNavigation = SlotNavigation() + + override lateinit var uiState: StateFlow + override val content: Value> = childSlot( + source = contentNavigation, + key = "CreateTermbaseContentSlot", + childFactory = { config, context -> + when (config) { + CreateTermbaseComponent.ContentConfig.Step1 -> getByInjection( + context, + coroutineContext + ) + + CreateTermbaseComponent.ContentConfig.Step2 -> getByInjection( + context, + coroutineContext + ) + + CreateTermbaseComponent.ContentConfig.Step3 -> getByInjection( + context, + coroutineContext + ) + + else -> Unit + } + } + ) + + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + step, + loading, + done, + ) { step, loading, done -> + CreateTermbaseUiState( + step = step, + loading = loading, + done = done, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = CreateTermbaseUiState(), + ) + contentNavigation.activate(CreateTermbaseComponent.ContentConfig.Step1) + } + doOnDestroy { + viewModelScope.cancel() + } + } + } + + override fun reset() { + step.value = 0 + loading.value = false + done.value = false + termbase = null + languages = emptyList() + properties = emptyList() + inputDescriptors = emptyList() + openNewlyCreated = false + } + + override fun setOpenNewlyCreated(value: Boolean) { + openNewlyCreated = value + } + + override fun setTermbase(termbase: TermbaseModel) { + this.termbase = termbase + } + + override fun setSelectedLanguages(value: List) { + languages = value + } + + override fun setProperties(value: List) { + properties = value + } + + override fun getProperties() = properties + + override fun getLanguages() = languages + + override fun getInputModelDescriptors() = inputDescriptors + + override fun setInputModelDescriptors(value: List) { + inputDescriptors = value + } + + override fun previous() { + step.updateAndGet { + max(0, it - 1) + } + } + + override fun next() { + val newStep = step.value +1 + step.value = newStep + viewModelScope.launch(dispatcherProvider.main) { + when (newStep) { + 1 -> contentNavigation.activate(CreateTermbaseComponent.ContentConfig.Step2) + 2 -> contentNavigation.activate(CreateTermbaseComponent.ContentConfig.Step3) + 0 -> contentNavigation.activate(CreateTermbaseComponent.ContentConfig.Step1) + } + } + } + + override fun submit() { + val termbase = termbase ?: return + loading.value = true + viewModelScope.launch(dispatcherProvider.io) { + val termbaseId = termbaseRepository.create(termbase) + + // create languages + for (language in languages) { + val lang = language.copy(termbaseId = termbaseId) + languageRepository.create(lang) + } + + // create definition model + for (property in properties) { + val prop = property.copy(termbaseId = termbaseId) + propertyRepository.create(prop) + } + + // create input model + for (descriptor in inputDescriptors) { + val desc = descriptor.copy(termbaseId = termbaseId) + inputDescriptorRepository.create(desc) + } + + // open new termbase if needed + if (openNewlyCreated) { + notificationCenter.send(NotificationCenter.Event.OpenTermbase(termbaseId)) + } + + loading.value = false + done.value = true + } + } +} diff --git a/feature-termbases/edit/.gitignore b/feature-termbases/dialog/edit/.gitignore similarity index 100% rename from feature-termbases/edit/.gitignore rename to feature-termbases/dialog/edit/.gitignore diff --git a/feature-termbases/edit/build.gradle.kts b/feature-termbases/dialog/edit/build.gradle.kts similarity index 73% rename from feature-termbases/edit/build.gradle.kts rename to feature-termbases/dialog/edit/build.gradle.kts index 323575d..2806f3f 100644 --- a/feature-termbases/edit/build.gradle.kts +++ b/feature-termbases/dialog/edit/build.gradle.kts @@ -23,7 +23,8 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) + implementation(libs.decompose.extensions) implementation(projects.coreCommon) implementation(projects.coreData) @@ -31,9 +32,9 @@ kotlin { implementation(projects.coreRepository) implementation(projects.coreLocalization) - implementation(projects.featureTermbases.metadata) - implementation(projects.featureTermbases.definitionmodel) - implementation(projects.featureTermbases.inputmodel) + implementation(projects.featureTermbases.wizard.metadata) + implementation(projects.featureTermbases.wizard.definitionmodel) + implementation(projects.featureTermbases.wizard.inputmodel) } } val jvmTest by getting diff --git a/feature-termbases/edit/gradle.properties b/feature-termbases/dialog/edit/gradle.properties similarity index 76% rename from feature-termbases/edit/gradle.properties rename to feature-termbases/dialog/edit/gradle.properties index 056070a..ca25374 100644 --- a/feature-termbases/edit/gradle.properties +++ b/feature-termbases/dialog/edit/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-termbases/definitionmodel/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/dialog/edit/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from feature-termbases/definitionmodel/gradle/wrapper/gradle-wrapper.jar rename to feature-termbases/dialog/edit/gradle/wrapper/gradle-wrapper.jar diff --git a/feature-termbases/definitionmodel/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/dialog/edit/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from feature-termbases/definitionmodel/gradle/wrapper/gradle-wrapper.properties rename to feature-termbases/dialog/edit/gradle/wrapper/gradle-wrapper.properties diff --git a/feature-termbases/definitionmodel/gradlew b/feature-termbases/dialog/edit/gradlew similarity index 100% rename from feature-termbases/definitionmodel/gradlew rename to feature-termbases/dialog/edit/gradlew diff --git a/feature-termbases/definitionmodel/gradlew.bat b/feature-termbases/dialog/edit/gradlew.bat similarity index 100% rename from feature-termbases/definitionmodel/gradlew.bat rename to feature-termbases/dialog/edit/gradlew.bat diff --git a/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/di/Module.kt b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/di/Module.kt new file mode 100644 index 0000000..dac83de --- /dev/null +++ b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/di/Module.kt @@ -0,0 +1,21 @@ +package dialogedit.di + +import dialogedit.ui.DefaultEditTermbaseComponent +import dialogedit.ui.EditTermbaseComponent +import org.koin.dsl.module + +val dialogEditModule = module { + factory { + DefaultEditTermbaseComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + termbaseRepository = get(), + languageRepository = get(), + propertyRepository = get(), + deleteTermbaseLanguage = get(), + inputDescriptorRepository = get(), + notificationCenter = get(), + ) + } +} \ No newline at end of file diff --git a/feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseViewModel.kt b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/DefaultEditTermbaseComponent.kt similarity index 51% rename from feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseViewModel.kt rename to feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/DefaultEditTermbaseComponent.kt index cb1a177..a6a7db6 100644 --- a/feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseViewModel.kt +++ b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/DefaultEditTermbaseComponent.kt @@ -1,7 +1,14 @@ -package ui.dialog.edit - -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider +package dialogedit.ui + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.arkivanov.decompose.router.slot.childSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider import data.InputDescriptorModel import data.LanguageModel import data.PropertyModel @@ -9,19 +16,23 @@ import data.TermbaseModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import notification.NotificationCenter -import repository.InputDescriptorRepository -import repository.LanguageRepository -import repository.PropertyRepository -import repository.TermbaseRepository +import common.notification.NotificationCenter +import common.utils.getByInjection +import dialogcreate.stepone.ui.CreateTermbaseWizardStepOneComponent +import dialogcreate.stepthree.ui.CreateTermbaseWizardStepThreeComponent +import dialogcreate.steptwo.ui.CreateTermbaseWizardStepTwoComponent +import repo.InputDescriptorRepository +import repo.LanguageRepository +import repo.PropertyRepository +import repo.TermbaseRepository import usecase.DeleteTermbaseLanguageUseCase +import kotlin.coroutines.CoroutineContext -class EditTermbaseViewModel( +internal class DefaultEditTermbaseComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, private val dispatcherProvider: CoroutineDispatcherProvider, private val termbaseRepository: TermbaseRepository, private val languageRepository: LanguageRepository, @@ -29,50 +40,93 @@ class EditTermbaseViewModel( private val inputDescriptorRepository: InputDescriptorRepository, private val deleteTermbaseLanguage: DeleteTermbaseLanguageUseCase, private val notificationCenter: NotificationCenter, -) : InstanceKeeper.Instance { +) : EditTermbaseComponent, ComponentContext by componentContext { private var termbase: TermbaseModel? = null private val step = MutableStateFlow(0) private val loading = MutableStateFlow(false) private val done = MutableStateFlow(false) - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val uiState = combine( - step, - loading, - done, - ) { step, loading, done -> - EditTermbaseUiState( - step = step, - loading = loading, - done = done, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = EditTermbaseUiState(), + private lateinit var viewModelScope: CoroutineScope + private val contentNavigation = SlotNavigation() + + override lateinit var uiState: StateFlow + override val content: Value> = childSlot( + source = contentNavigation, + key = "EditTermbaseContentSlot", + childFactory = { config, context -> + when (config) { + EditTermbaseComponent.ContentConfig.Step1 -> getByInjection( + context, + coroutineContext + ) + + EditTermbaseComponent.ContentConfig.Step2 -> getByInjection( + context, + coroutineContext + ) + + EditTermbaseComponent.ContentConfig.Step3 -> getByInjection( + context, + coroutineContext + ) + } + } ) - override fun onDestroy() { - viewModelScope.cancel() + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + step, + loading, + done, + ) { step, loading, done -> + EditTermbaseUiState( + step = step, + loading = loading, + done = done, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = EditTermbaseUiState(), + ) + + contentNavigation.activate(EditTermbaseComponent.ContentConfig.Step1) + } + doOnDestroy { + viewModelScope.cancel() + } + } } - fun reset() { + override fun reset() { termbase = null step.value = 0 + viewModelScope.launch(dispatcherProvider.main) { + contentNavigation.activate(EditTermbaseComponent.ContentConfig.Step1) + } loading.value = false done.value = false } - fun setTermbase(value: TermbaseModel) { + override fun setTermbase(value: TermbaseModel) { termbase = value } - fun changeStep(index: Int) { + override fun changeStep(index: Int) { step.value = index + viewModelScope.launch(dispatcherProvider.main) { + when (index) { + 1 -> contentNavigation.activate(EditTermbaseComponent.ContentConfig.Step2) + 2 -> contentNavigation.activate(EditTermbaseComponent.ContentConfig.Step3) + else -> contentNavigation.activate(EditTermbaseComponent.ContentConfig.Step1) + } + } } - fun submitStep1(name: String, description: String, selectedLanguages: List) { + override fun submitStep1(name: String, description: String, selectedLanguages: List) { val current = termbase ?: return loading.value = true viewModelScope.launch(dispatcherProvider.io) { @@ -101,7 +155,7 @@ class EditTermbaseViewModel( } } - fun submitStep2(properties: List) { + override fun submitStep2(properties: List) { val current = termbase ?: return viewModelScope.launch(dispatcherProvider.io) { val newlyCreatedIds = mutableListOf() @@ -126,7 +180,7 @@ class EditTermbaseViewModel( } } - fun submitStep3(descriptors: List) { + override fun submitStep3(descriptors: List) { val current = termbase ?: return viewModelScope.launch(dispatcherProvider.io) { val newlyCreatedIds = mutableListOf() diff --git a/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseComponent.kt b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseComponent.kt new file mode 100644 index 0000000..df5918a --- /dev/null +++ b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseComponent.kt @@ -0,0 +1,34 @@ +package dialogedit.ui + +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import data.InputDescriptorModel +import data.LanguageModel +import data.PropertyModel +import data.TermbaseModel +import kotlinx.coroutines.flow.StateFlow + +interface EditTermbaseComponent { + val uiState: StateFlow + val content: Value> + + fun reset() + fun setTermbase(value: TermbaseModel) + fun changeStep(index: Int) + fun submitStep1(name: String, description: String, selectedLanguages: List) + fun submitStep2(properties: List) + fun submitStep3(descriptors: List) + + sealed interface ContentConfig : Parcelable { + @Parcelize + object Step1 : ContentConfig + + @Parcelize + object Step2 : ContentConfig + + @Parcelize + object Step3 : ContentConfig + } +} \ No newline at end of file diff --git a/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseDialog.kt b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseDialog.kt new file mode 100644 index 0000000..bef741b --- /dev/null +++ b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseDialog.kt @@ -0,0 +1,222 @@ +package dialogedit.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.rememberWindowState +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import data.TermbaseModel +import kotlinx.coroutines.launch +import localized +import common.ui.components.CustomTabBar +import dialogcreate.stepone.ui.CreateTermbaseWizardStepOne +import dialogcreate.stepthree.ui.CreateTermbaseWizardStepThree +import dialogcreate.steptwo.ui.CreateTermbaseWizardStepTwo +import common.ui.theme.MetaTermTheme +import common.ui.theme.SelectedBackground +import common.ui.theme.Spacing +import dialogcreate.stepone.ui.CreateTermbaseWizardStepOneComponent +import dialogcreate.stepthree.ui.CreateTermbaseWizardStepThreeComponent +import dialogcreate.steptwo.ui.CreateTermbaseWizardStepTwoComponent + +@Composable +fun EditTermbaseDialog( + component: EditTermbaseComponent, + initialTermbase: TermbaseModel, + onClose: () -> Unit, +) { + val content by component.content.subscribeAsState() + + MetaTermTheme { + Window( + title = "dialog_title_edit_termbase".localized(), + state = rememberWindowState(width = Dp.Unspecified, height = Dp.Unspecified), + resizable = false, + onCloseRequest = { + onClose() + }, + ) { + LaunchedEffect(initialTermbase) { + component.setTermbase(initialTermbase) + } + + val uiState by component.uiState.collectAsState() + + Box( + modifier = Modifier.size(800.dp, 600.dp) + .background(MaterialTheme.colors.background), + ) { + Column { + CustomTabBar( + modifier = Modifier.fillMaxWidth(), + tabs = listOf( + "dialog_edit_termbase_tab_1".localized(), + "dialog_edit_termbase_tab_2".localized(), + "dialog_edit_termbase_tab_3".localized(), + ), + current = uiState.step, + onTabSelected = { + component.changeStep(it) + }, + ) + + Column( + modifier = Modifier.weight(1f).fillMaxWidth().background(SelectedBackground), + ) { + when (content.child?.configuration) { + EditTermbaseComponent.ContentConfig.Step1 -> { + val childComponent = content.child?.instance as CreateTermbaseWizardStepOneComponent + LaunchedEffect(initialTermbase) { + childComponent.loadInitial(initialTermbase) + + launch { + childComponent.done.collect { (termbase, languages) -> + val name = termbase.name + val description = termbase.description + component.submitStep1( + name = name, + description = description, + selectedLanguages = languages, + ) + onClose() + } + } + } + CreateTermbaseWizardStepOne( + component = childComponent, + modifier = Modifier.weight(1f).fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(Spacing.xs)) + ButtonArea( + onSubmit = { + childComponent.submit() + }, + onClose = onClose, + ) + } + + EditTermbaseComponent.ContentConfig.Step2 -> { + val childComponent = + content.child?.instance as CreateTermbaseWizardStepTwoComponent + LaunchedEffect(initialTermbase) { + childComponent.loadInitial(initialTermbase) + + launch { + childComponent.done.collect { properties -> + component.submitStep2(properties) + onClose() + } + } + } + CreateTermbaseWizardStepTwo( + component = childComponent, + modifier = Modifier.weight(1f).fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(Spacing.xs)) + ButtonArea( + onSubmit = { + childComponent.submit() + }, + onClose = onClose, + ) + } + + EditTermbaseComponent.ContentConfig.Step3 -> { + val childComponent = + content.child?.instance as CreateTermbaseWizardStepThreeComponent + LaunchedEffect(initialTermbase) { + childComponent.loadInitial(initialTermbase) + + launch { + childComponent.done.collect { result -> + component.submitStep3(result) + onClose() + } + } + } + CreateTermbaseWizardStepThree( + component = childComponent, + modifier = Modifier.weight(1f).fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(Spacing.xs)) + ButtonArea( + onSubmit = { + childComponent.submit() + }, + onClose = onClose, + ) + } + + else -> Unit + } + } + } + + if (uiState.loading) { + Surface( + modifier = Modifier.matchParentSize(), + ) { + Box( + modifier = Modifier.fillMaxSize() + .background(Color.White.copy(alpha = 0.1f)), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator() + } + } + } + } + } + } +} + +@Composable +private fun ButtonArea( + onClose: () -> Unit, + onSubmit: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier.padding(Spacing.s), + horizontalArrangement = Arrangement.spacedBy(Spacing.s), + ) { + Spacer(modifier = Modifier.weight(1f)) + Button( + modifier = Modifier.heightIn(max = 25.dp), + contentPadding = PaddingValues(0.dp), + onClick = { + onClose() + }, + ) { + Text( + text = "button_cancel".localized(), + style = MaterialTheme.typography.button, + ) + } + Button( + modifier = Modifier.heightIn(max = 25.dp), + contentPadding = PaddingValues(0.dp), + onClick = { + onSubmit() + }, + ) { + Text( + text = "button_ok".localized(), + style = MaterialTheme.typography.button, + ) + } + } +} diff --git a/feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseUiState.kt b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseUiState.kt similarity index 84% rename from feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseUiState.kt rename to feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseUiState.kt index c80fdbf..e5232e2 100644 --- a/feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseUiState.kt +++ b/feature-termbases/dialog/edit/src/jvmMain/kotlin/dialogedit/ui/EditTermbaseUiState.kt @@ -1,4 +1,4 @@ -package ui.dialog.edit +package dialogedit.ui data class EditTermbaseUiState( val step: Int = 0, diff --git a/feature-terms/filter/gradle.properties b/feature-termbases/dialog/gradle.properties similarity index 100% rename from feature-terms/filter/gradle.properties rename to feature-termbases/dialog/gradle.properties diff --git a/feature-termbases/edit/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/dialog/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from feature-termbases/edit/gradle/wrapper/gradle-wrapper.jar rename to feature-termbases/dialog/gradle/wrapper/gradle-wrapper.jar diff --git a/feature-termbases/edit/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/dialog/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from feature-termbases/edit/gradle/wrapper/gradle-wrapper.properties rename to feature-termbases/dialog/gradle/wrapper/gradle-wrapper.properties diff --git a/feature-termbases/edit/gradlew b/feature-termbases/dialog/gradlew similarity index 100% rename from feature-termbases/edit/gradlew rename to feature-termbases/dialog/gradlew diff --git a/feature-termbases/edit/gradlew.bat b/feature-termbases/dialog/gradlew.bat similarity index 100% rename from feature-termbases/edit/gradlew.bat rename to feature-termbases/dialog/gradlew.bat diff --git a/feature-termbases/inputmodel/.gitignore b/feature-termbases/dialog/management/.gitignore similarity index 100% rename from feature-termbases/inputmodel/.gitignore rename to feature-termbases/dialog/management/.gitignore diff --git a/feature-termbases/management/build.gradle.kts b/feature-termbases/dialog/management/build.gradle.kts similarity index 93% rename from feature-termbases/management/build.gradle.kts rename to feature-termbases/dialog/management/build.gradle.kts index 1b4f185..22f92c4 100644 --- a/feature-termbases/management/build.gradle.kts +++ b/feature-termbases/dialog/management/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-termbases/dialog/management/gradle.properties b/feature-termbases/dialog/management/gradle.properties new file mode 100644 index 0000000..ca25374 --- /dev/null +++ b/feature-termbases/dialog/management/gradle.properties @@ -0,0 +1,5 @@ +kotlin.code.style=official + +kotlin.version=1.8.0 +agp.version=7.3.0 +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-termbases/inputmodel/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/dialog/management/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from feature-termbases/inputmodel/gradle/wrapper/gradle-wrapper.jar rename to feature-termbases/dialog/management/gradle/wrapper/gradle-wrapper.jar diff --git a/feature-termbases/inputmodel/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/dialog/management/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from feature-termbases/inputmodel/gradle/wrapper/gradle-wrapper.properties rename to feature-termbases/dialog/management/gradle/wrapper/gradle-wrapper.properties diff --git a/feature-termbases/inputmodel/gradlew b/feature-termbases/dialog/management/gradlew similarity index 100% rename from feature-termbases/inputmodel/gradlew rename to feature-termbases/dialog/management/gradlew diff --git a/feature-termbases/inputmodel/gradlew.bat b/feature-termbases/dialog/management/gradlew.bat similarity index 100% rename from feature-termbases/inputmodel/gradlew.bat rename to feature-termbases/dialog/management/gradlew.bat diff --git a/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/di/Module.kt b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/di/Module.kt new file mode 100644 index 0000000..ec243fc --- /dev/null +++ b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/di/Module.kt @@ -0,0 +1,18 @@ +package dialogmanage.di + +import dialogmanage.ui.DefaultManageTermbasesComponent +import dialogmanage.ui.ManageTermbasesComponent +import org.koin.dsl.module + +val dialogManageModule = module { + factory { + DefaultManageTermbasesComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + termbaseRepository = get(), + deleteTermbaseUseCase = get(), + notificationCenter = get(), + ) + } +} \ No newline at end of file diff --git a/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/DefaultManageTermbasesComponent.kt b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/DefaultManageTermbasesComponent.kt new file mode 100644 index 0000000..c18028b --- /dev/null +++ b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/DefaultManageTermbasesComponent.kt @@ -0,0 +1,90 @@ +package dialogmanage.ui + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider +import data.TermbaseModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import common.notification.NotificationCenter +import repo.TermbaseRepository +import usecase.DeleteTermbaseUseCase +import kotlin.coroutines.CoroutineContext + +class DefaultManageTermbasesComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, + private val dispatcherProvider: CoroutineDispatcherProvider, + private val termbaseRepository: TermbaseRepository, + private val deleteTermbaseUseCase: DeleteTermbaseUseCase, + private val notificationCenter: NotificationCenter, +) : ManageTermbasesComponent, ComponentContext by componentContext { + + private val termbases = MutableStateFlow>(emptyList()) + private val selectedTermbase = MutableStateFlow(null) + private val loading = MutableStateFlow(false) + private lateinit var viewModelScope: CoroutineScope + + override lateinit var uiState: StateFlow + + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + termbases, + selectedTermbase, + loading, + ) { termbases, selectedTermbase, loading -> + ManageTermbasesUiState( + termbases = termbases, + selectedTermbase = selectedTermbase, + loading = loading, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = ManageTermbasesUiState(), + ) + viewModelScope.launch(dispatcherProvider.io) { + launch { + termbaseRepository.all.collect { + termbases.value = it + } + } + } + } + doOnDestroy { + viewModelScope.cancel() + } + } + } + + override fun selectTermbase(termbase: TermbaseModel) { + selectedTermbase.getAndUpdate { + if (it == termbase) { + null + } else { + termbase + } + } + } + + override fun openCurrentTermbase() { + val termbase = selectedTermbase.value ?: return + viewModelScope.launch(dispatcherProvider.io) { + notificationCenter.send(NotificationCenter.Event.OpenTermbase(termbase.id)) + } + } + + override fun deleteCurrentTermbase() { + val termbase = selectedTermbase.value ?: return + viewModelScope.launch(dispatcherProvider.io) { + deleteTermbaseUseCase(termbase) + } + } +} diff --git a/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesComponent.kt b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesComponent.kt new file mode 100644 index 0000000..6dc6808 --- /dev/null +++ b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesComponent.kt @@ -0,0 +1,11 @@ +package dialogmanage.ui + +import data.TermbaseModel +import kotlinx.coroutines.flow.StateFlow + +interface ManageTermbasesComponent { + val uiState: StateFlow + fun selectTermbase(termbase: TermbaseModel) + fun openCurrentTermbase() + fun deleteCurrentTermbase() +} \ No newline at end of file diff --git a/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesDialog.kt b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesDialog.kt similarity index 91% rename from feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesDialog.kt rename to feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesDialog.kt index a5b5e34..da3a71d 100644 --- a/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesDialog.kt +++ b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesDialog.kt @@ -1,4 +1,4 @@ -package ui.dialog.manage +package dialogmanage.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -28,25 +28,18 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.rememberWindowState -import com.arkivanov.essenty.instancekeeper.getOrCreate import data.TermbaseModel import localized -import org.koin.java.KoinJavaComponent.inject -import ui.theme.MetaTermTheme -import ui.theme.Spacing -import utils.AppBusiness +import common.ui.theme.MetaTermTheme +import common.ui.theme.Spacing @Composable fun ManageTermbasesDialog( + component: ManageTermbasesComponent, onNew: () -> Unit, onClose: () -> Unit, onEdit: (TermbaseModel) -> Unit, ) { - val viewModel: ManageTermbasesViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: ManageTermbasesViewModel by inject(ManageTermbasesViewModel::class.java) - res - } - MetaTermTheme { Window( title = "dialog_title_manage_termbases".localized(), @@ -56,7 +49,7 @@ fun ManageTermbasesDialog( onClose() }, ) { - val uiState by viewModel.uiState.collectAsState() + val uiState by component.uiState.collectAsState() Box( modifier = Modifier @@ -90,7 +83,7 @@ fun ManageTermbasesDialog( name = it.name, selected = it == uiState.selectedTermbase, onSelected = { - viewModel.selectTermbase(it) + component.selectTermbase(it) }, ) } @@ -125,7 +118,7 @@ fun ManageTermbasesDialog( contentPadding = PaddingValues(0.dp), enabled = uiState.selectedTermbase != null, onClick = { - viewModel.openCurrentTermbase() + component.openCurrentTermbase() onClose() }, ) { @@ -136,7 +129,7 @@ fun ManageTermbasesDialog( contentPadding = PaddingValues(0.dp), enabled = uiState.selectedTermbase != null, onClick = { - viewModel.deleteCurrentTermbase() + component.deleteCurrentTermbase() }, ) { Text(text = "button_delete".localized(), style = MaterialTheme.typography.button) diff --git a/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesUiState.kt b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesUiState.kt similarity index 88% rename from feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesUiState.kt rename to feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesUiState.kt index 4bf640f..c7ed814 100644 --- a/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesUiState.kt +++ b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/ManageTermbasesUiState.kt @@ -1,4 +1,4 @@ -package ui.dialog.manage +package dialogmanage.ui import data.TermbaseModel diff --git a/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/TermbaseCell.kt b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/TermbaseCell.kt similarity index 96% rename from feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/TermbaseCell.kt rename to feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/TermbaseCell.kt index 79dfd96..cc108e6 100644 --- a/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/TermbaseCell.kt +++ b/feature-termbases/dialog/management/src/jvmMain/kotlin/dialogmanage/ui/TermbaseCell.kt @@ -1,4 +1,4 @@ -package ui.dialog.manage +package dialogmanage.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -14,7 +14,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import ui.theme.Spacing +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/feature-termbases/management/.gitignore b/feature-termbases/dialog/statistics/.gitignore similarity index 100% rename from feature-termbases/management/.gitignore rename to feature-termbases/dialog/statistics/.gitignore diff --git a/feature-termbases/statistics/build.gradle.kts b/feature-termbases/dialog/statistics/build.gradle.kts similarity index 93% rename from feature-termbases/statistics/build.gradle.kts rename to feature-termbases/dialog/statistics/build.gradle.kts index 7c44c59..1b7126a 100644 --- a/feature-termbases/statistics/build.gradle.kts +++ b/feature-termbases/dialog/statistics/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-termbases/dialog/statistics/gradle.properties b/feature-termbases/dialog/statistics/gradle.properties new file mode 100644 index 0000000..ca25374 --- /dev/null +++ b/feature-termbases/dialog/statistics/gradle.properties @@ -0,0 +1,5 @@ +kotlin.code.style=official + +kotlin.version=1.8.0 +agp.version=7.3.0 +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-termbases/management/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/dialog/statistics/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from feature-termbases/management/gradle/wrapper/gradle-wrapper.jar rename to feature-termbases/dialog/statistics/gradle/wrapper/gradle-wrapper.jar diff --git a/feature-termbases/management/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/dialog/statistics/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from feature-termbases/management/gradle/wrapper/gradle-wrapper.properties rename to feature-termbases/dialog/statistics/gradle/wrapper/gradle-wrapper.properties diff --git a/feature-termbases/management/gradlew b/feature-termbases/dialog/statistics/gradlew similarity index 100% rename from feature-termbases/management/gradlew rename to feature-termbases/dialog/statistics/gradlew diff --git a/feature-termbases/management/gradlew.bat b/feature-termbases/dialog/statistics/gradlew.bat similarity index 100% rename from feature-termbases/management/gradlew.bat rename to feature-termbases/dialog/statistics/gradlew.bat diff --git a/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/di/Module.kt b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/di/Module.kt new file mode 100644 index 0000000..6102d83 --- /dev/null +++ b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/di/Module.kt @@ -0,0 +1,20 @@ +package dialogstatistics.di + +import dialogstatistics.ui.DefaultTermbaseStatisticsComponent +import dialogstatistics.ui.TermbaseStatisticsComponent +import org.koin.dsl.module + +val dialogStatisticsModule = module { + factory { + DefaultTermbaseStatisticsComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + entryRepository = get(), + languageRepository = get(), + languageNameRepository = get(), + flagsRepository = get(), + termRepository = get(), + ) + } +} \ No newline at end of file diff --git a/feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsViewModel.kt b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/DefaultTermbaseStatisticsComponent.kt similarity index 70% rename from feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsViewModel.kt rename to feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/DefaultTermbaseStatisticsComponent.kt index 2daaf74..2e5ec8b 100644 --- a/feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsViewModel.kt +++ b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/DefaultTermbaseStatisticsComponent.kt @@ -1,53 +1,58 @@ -package ui.dialog.statistics +package dialogstatistics.ui -import com.arkivanov.essenty.instancekeeper.InstanceKeeper +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider import data.TermbaseModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import localized -import repository.EntryRepository -import repository.FlagsRepository -import repository.LanguageNameRepository -import repository.LanguageRepository -import repository.TermRepository -import coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import localized +import repo.* +import kotlin.coroutines.CoroutineContext -class TermbaseStatisticsViewModel( +internal class DefaultTermbaseStatisticsComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, private val dispatcherProvider: CoroutineDispatcherProvider, private val entryRepository: EntryRepository, private val languageRepository: LanguageRepository, private val languageNameRepository: LanguageNameRepository, private val flagsRepository: FlagsRepository, private val termRepository: TermRepository, -) : InstanceKeeper.Instance { +) : TermbaseStatisticsComponent, ComponentContext by componentContext { private val items = MutableStateFlow>(emptyList()) private val loading = MutableStateFlow(false) - private val viewModelScope = CoroutineScope(SupervisorJob()) + private lateinit var viewModelScope: CoroutineScope - val uiState = combine(items, loading) { items, loading -> - TermbaseStatisticsUiState( - items = items, - loading = loading, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = TermbaseStatisticsUiState(), - ) + override lateinit var uiState: StateFlow - override fun onDestroy() { - viewModelScope.cancel() + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine(items, loading) { items, loading -> + TermbaseStatisticsUiState( + items = items, + loading = loading, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = TermbaseStatisticsUiState(), + ) + } + doOnDestroy { + viewModelScope.cancel() + } + } } - fun load(termbase: TermbaseModel) { + override fun load(termbase: TermbaseModel) { val termbaseId = termbase.id loading.value = true viewModelScope.launch(dispatcherProvider.io) { diff --git a/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsComponent.kt b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsComponent.kt new file mode 100644 index 0000000..dea8c70 --- /dev/null +++ b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsComponent.kt @@ -0,0 +1,10 @@ +package dialogstatistics.ui + +import data.TermbaseModel +import kotlinx.coroutines.flow.StateFlow + +interface TermbaseStatisticsComponent { + val uiState: StateFlow + + fun load(termbase: TermbaseModel) +} \ No newline at end of file diff --git a/feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsDialog.kt b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsDialog.kt similarity index 90% rename from feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsDialog.kt rename to feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsDialog.kt index ab5ed23..dbb8047 100644 --- a/feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsDialog.kt +++ b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsDialog.kt @@ -1,4 +1,4 @@ -package ui.dialog.statistics +package dialogstatistics.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -18,25 +18,18 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.rememberWindowState -import com.arkivanov.essenty.instancekeeper.getOrCreate import data.TermbaseModel import localized -import org.koin.java.KoinJavaComponent.inject -import ui.components.CustomProgressIndicator -import ui.theme.MetaTermTheme -import ui.theme.Spacing -import utils.AppBusiness +import common.ui.components.CustomProgressIndicator +import common.ui.theme.MetaTermTheme +import common.ui.theme.Spacing @Composable fun TermbaseStatisticsDialog( + component: TermbaseStatisticsComponent, termbase: TermbaseModel, onClose: () -> Unit, ) { - val viewModel: TermbaseStatisticsViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: TermbaseStatisticsViewModel by inject(TermbaseStatisticsViewModel::class.java) - res - } - MetaTermTheme { Window( title = "dialog_title_termbase_statistics".localized(), @@ -47,10 +40,10 @@ fun TermbaseStatisticsDialog( }, ) { LaunchedEffect(termbase) { - viewModel.load(termbase) + component.load(termbase) } - val uiState by viewModel.uiState.collectAsState() + val uiState by component.uiState.collectAsState() Column( modifier = Modifier.size(600.dp, 400.dp) diff --git a/feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsItem.kt b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsItem.kt similarity index 93% rename from feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsItem.kt rename to feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsItem.kt index aa2e738..d36f1de 100644 --- a/feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsItem.kt +++ b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsItem.kt @@ -1,4 +1,4 @@ -package ui.dialog.statistics +package dialogstatistics.ui sealed interface TermbaseStatisticsItem { object Divider : TermbaseStatisticsItem diff --git a/feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsUiState.kt b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsUiState.kt similarity index 81% rename from feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsUiState.kt rename to feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsUiState.kt index 38f3198..2fe0ec0 100644 --- a/feature-termbases/statistics/src/jvmMain/kotlin/ui/dialog/statistics/TermbaseStatisticsUiState.kt +++ b/feature-termbases/dialog/statistics/src/jvmMain/kotlin/dialogstatistics/ui/TermbaseStatisticsUiState.kt @@ -1,4 +1,4 @@ -package ui.dialog.statistics +package dialogstatistics.ui data class TermbaseStatisticsUiState( val items: List = listOf(), diff --git a/feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseDialog.kt b/feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseDialog.kt deleted file mode 100644 index d780c10..0000000 --- a/feature-termbases/edit/src/jvmMain/kotlin/ui/dialog/edit/EditTermbaseDialog.kt +++ /dev/null @@ -1,230 +0,0 @@ -package ui.dialog.edit - -import androidx.compose.animation.* -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Window -import androidx.compose.ui.window.rememberWindowState -import com.arkivanov.essenty.instancekeeper.getOrCreate -import data.TermbaseModel -import kotlinx.coroutines.launch -import localized -import org.koin.java.KoinJavaComponent.inject -import ui.components.CustomTabBar -import ui.dialog.create.stepone.CreateTermbaseWizardStepOne -import ui.dialog.create.stepone.CreateTermbaseWizardStepOneViewModel -import ui.dialog.create.stepthree.CreateTermbaseWizardStepThree -import ui.dialog.create.stepthree.CreateTermbaseWizardStepThreeViewModel -import ui.dialog.create.steptwo.CreateTermbaseWizardStepTwo -import ui.dialog.create.steptwo.CreateTermbaseWizardStepTwoViewModel -import ui.theme.MetaTermTheme -import ui.theme.SelectedBackground -import ui.theme.Spacing -import utils.AppBusiness - -@OptIn(ExperimentalAnimationApi::class) -@Composable -fun EditTermbaseDialog( - initialTermbase: TermbaseModel, - onClose: () -> Unit, -) { - val viewModel: EditTermbaseViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: EditTermbaseViewModel by inject(EditTermbaseViewModel::class.java) - res - } - val stepOneViewModel: CreateTermbaseWizardStepOneViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepOneViewModel by inject(CreateTermbaseWizardStepOneViewModel::class.java) - res - } - val stepTwoViewModel: CreateTermbaseWizardStepTwoViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepTwoViewModel by inject(CreateTermbaseWizardStepTwoViewModel::class.java) - res - } - val stepThreeViewModel: CreateTermbaseWizardStepThreeViewModel = - AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepThreeViewModel by inject(CreateTermbaseWizardStepThreeViewModel::class.java) - res - } - - MetaTermTheme { - Window( - title = "dialog_title_edit_termbase".localized(), - state = rememberWindowState(width = Dp.Unspecified, height = Dp.Unspecified), - resizable = false, - onCloseRequest = { - onClose() - }, - ) { - LaunchedEffect(initialTermbase) { - viewModel.setTermbase(initialTermbase) - stepOneViewModel.loadInitial(initialTermbase) - stepTwoViewModel.loadInitial(initialTermbase) - stepThreeViewModel.loadInitial(initialTermbase) - - launch { - stepOneViewModel.done.collect { (termbase, languages) -> - val name = termbase.name - val description = termbase.description - viewModel.submitStep1( - name = name, - description = description, - selectedLanguages = languages, - ) - onClose() - } - } - - launch { - stepTwoViewModel.done.collect { properties -> - viewModel.submitStep2(properties) - onClose() - } - } - - launch { - stepThreeViewModel.done.collect { result -> - viewModel.submitStep3(result) - onClose() - } - } - } - - val uiState by viewModel.uiState.collectAsState() - - Box( - modifier = Modifier.size(800.dp, 600.dp) - .background(MaterialTheme.colors.background), - ) { - Column { - CustomTabBar( - modifier = Modifier.fillMaxWidth(), - tabs = listOf( - "dialog_edit_termbase_tab_1".localized(), - "dialog_edit_termbase_tab_2".localized(), - "dialog_edit_termbase_tab_3".localized(), - ), - current = uiState.step, - onTabSelected = { - viewModel.changeStep(it) - }, - ) - AnimatedContent( - modifier = Modifier.weight(1f).fillMaxWidth().background(SelectedBackground), - targetState = uiState.step, - transitionSpec = { - fadeIn() with fadeOut() - }, - ) { - Column { - when (uiState.step) { - 0 -> { - CreateTermbaseWizardStepOne( - modifier = Modifier.weight(1f).fillMaxWidth(), - ) - - Spacer(modifier = Modifier.height(Spacing.xs)) - ButtonArea( - onSubmit = { - stepOneViewModel.submit() - }, - onClose = onClose, - ) - } - - 1 -> { - CreateTermbaseWizardStepTwo( - modifier = Modifier.weight(1f).fillMaxWidth(), - ) - - Spacer(modifier = Modifier.height(Spacing.xs)) - ButtonArea( - onSubmit = { - stepTwoViewModel.submit() - }, - onClose = onClose, - ) - } - - else -> { - CreateTermbaseWizardStepThree( - modifier = Modifier.weight(1f).fillMaxWidth(), - ) - - Spacer(modifier = Modifier.height(Spacing.xs)) - ButtonArea( - onSubmit = { - stepThreeViewModel.submit() - }, - onClose = onClose, - ) - } - } - } - } - } - - if (uiState.loading) { - Surface( - modifier = Modifier.matchParentSize(), - ) { - Box( - modifier = Modifier.fillMaxSize() - .background(Color.White.copy(alpha = 0.1f)), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator() - } - } - } - } - } - } -} - -@Composable -private fun ButtonArea( - onClose: () -> Unit, - onSubmit: () -> Unit, - modifier: Modifier = Modifier, -) { - Row( - modifier = modifier.padding(Spacing.s), - horizontalArrangement = Arrangement.spacedBy(Spacing.s), - ) { - Spacer(modifier = Modifier.weight(1f)) - Button( - modifier = Modifier.heightIn(max = 25.dp), - contentPadding = PaddingValues(0.dp), - onClick = { - onClose() - }, - ) { - Text( - text = "button_cancel".localized(), - style = MaterialTheme.typography.button, - ) - } - Button( - modifier = Modifier.heightIn(max = 25.dp), - contentPadding = PaddingValues(0.dp), - onClick = { - onSubmit() - }, - ) { - Text( - text = "button_ok".localized(), - style = MaterialTheme.typography.button, - ) - } - } -} diff --git a/feature-termbases/gradle.properties b/feature-termbases/gradle.properties index 47c80dc..15a3af6 100644 --- a/feature-termbases/gradle.properties +++ b/feature-termbases/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.1 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-termbases/management/gradle.properties b/feature-termbases/management/gradle.properties deleted file mode 100644 index 056070a..0000000 --- a/feature-termbases/management/gradle.properties +++ /dev/null @@ -1,5 +0,0 @@ -kotlin.code.style=official - -kotlin.version=1.8.0 -agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file diff --git a/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesViewModel.kt b/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesViewModel.kt deleted file mode 100644 index 9366026..0000000 --- a/feature-termbases/management/src/jvmMain/kotlin/ui/dialog/manage/ManageTermbasesViewModel.kt +++ /dev/null @@ -1,84 +0,0 @@ -package ui.dialog.manage - -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider -import data.TermbaseModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.getAndUpdate -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import notification.NotificationCenter -import repository.TermbaseRepository -import usecase.DeleteTermbaseUseCase - -class ManageTermbasesViewModel( - private val dispatcherProvider: CoroutineDispatcherProvider, - private val termbaseRepository: TermbaseRepository, - private val deleteTermbaseUseCase: DeleteTermbaseUseCase, - private val notificationCenter: NotificationCenter, -) : InstanceKeeper.Instance { - - private val termbases = MutableStateFlow>(emptyList()) - private val selectedTermbase = MutableStateFlow(null) - private val loading = MutableStateFlow(false) - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val uiState = combine( - termbases, - selectedTermbase, - loading, - ) { termbases, selectedTermbase, loading -> - ManageTermbasesUiState( - termbases = termbases, - selectedTermbase = selectedTermbase, - loading = loading, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = ManageTermbasesUiState(), - ) - - init { - viewModelScope.launch(dispatcherProvider.io) { - launch { - termbaseRepository.all.collect { - termbases.value = it - } - } - } - } - - override fun onDestroy() { - viewModelScope.cancel() - } - - fun selectTermbase(termbase: TermbaseModel) { - selectedTermbase.getAndUpdate { - if (it == termbase) { - null - } else { - termbase - } - } - } - - fun openCurrentTermbase() { - val termbase = selectedTermbase.value ?: return - viewModelScope.launch(dispatcherProvider.io) { - notificationCenter.send(NotificationCenter.Event.OpenTermbase(termbase.id)) - } - } - - fun deleteCurrentTermbase() { - val termbase = selectedTermbase.value ?: return - viewModelScope.launch(dispatcherProvider.io) { - deleteTermbaseUseCase(termbase) - } - } -} diff --git a/feature-termbases/metadata/gradle.properties b/feature-termbases/metadata/gradle.properties deleted file mode 100644 index 056070a..0000000 --- a/feature-termbases/metadata/gradle.properties +++ /dev/null @@ -1,5 +0,0 @@ -kotlin.code.style=official - -kotlin.version=1.8.0 -agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file diff --git a/feature-termbases/src/jvmMain/kotlin/di/TermbasesModule.kt b/feature-termbases/src/jvmMain/kotlin/di/TermbasesModule.kt deleted file mode 100644 index 587a870..0000000 --- a/feature-termbases/src/jvmMain/kotlin/di/TermbasesModule.kt +++ /dev/null @@ -1,76 +0,0 @@ -package di - -import org.koin.dsl.module -import ui.dialog.create.CreateTermbaseViewModel -import ui.dialog.create.stepone.CreateTermbaseWizardStepOneViewModel -import ui.dialog.create.stepthree.CreateTermbaseWizardStepThreeViewModel -import ui.dialog.create.steptwo.CreateTermbaseWizardStepTwoViewModel -import ui.dialog.edit.EditTermbaseViewModel -import ui.dialog.manage.ManageTermbasesViewModel -import ui.dialog.statistics.TermbaseStatisticsViewModel - -val termbasesKoinModule = module { - factory { - CreateTermbaseViewModel( - dispatcherProvider = get(), - termbaseRepository = get(), - languageRepository = get(), - propertyRepository = get(), - inputDescriptorRepository = get(), - notificationCenter = get(), - ) - } - factory { - CreateTermbaseWizardStepOneViewModel( - dispatcherProvider = get(), - languageRepository = get(), - languageNameRepository = get(), - flagsRepository = get(), - ) - } - factory { - CreateTermbaseWizardStepTwoViewModel( - dispatcherProvider = get(), - propertyRepository = get(), - ) - } - factory { - CreateTermbaseWizardStepThreeViewModel( - dispatcherProvider = get(), - propertyRepository = get(), - languageRepository = get(), - languageNameRepository = get(), - flagsRepository = get(), - inputDescriptorRepository = get(), - ) - } - factory { - EditTermbaseViewModel( - dispatcherProvider = get(), - termbaseRepository = get(), - languageRepository = get(), - propertyRepository = get(), - deleteTermbaseLanguage = get(), - inputDescriptorRepository = get(), - notificationCenter = get(), - ) - } - factory { - ManageTermbasesViewModel( - dispatcherProvider = get(), - termbaseRepository = get(), - deleteTermbaseUseCase = get(), - notificationCenter = get(), - ) - } - factory { - TermbaseStatisticsViewModel( - dispatcherProvider = get(), - entryRepository = get(), - languageRepository = get(), - languageNameRepository = get(), - flagsRepository = get(), - termRepository = get(), - ) - } -} \ No newline at end of file diff --git a/feature-termbases/src/jvmMain/kotlin/termbases/di/Module.kt b/feature-termbases/src/jvmMain/kotlin/termbases/di/Module.kt new file mode 100644 index 0000000..b9fa396 --- /dev/null +++ b/feature-termbases/src/jvmMain/kotlin/termbases/di/Module.kt @@ -0,0 +1,22 @@ +package termbases.di + +import dialogcreate.di.dialogCreateModule +import dialogcreate.stepone.di.createTermbaseStepOneModule +import dialogcreate.stepthree.di.createTermbaseStepThreeModule +import dialogcreate.steptwo.di.createTermbaseStepTwoModule +import dialogedit.di.dialogEditModule +import dialogmanage.di.dialogManageModule +import dialogstatistics.di.dialogStatisticsModule +import org.koin.dsl.module + +val termbasesModule = module { + includes( + dialogStatisticsModule, + dialogManageModule, + dialogCreateModule, + dialogEditModule, + createTermbaseStepOneModule, + createTermbaseStepTwoModule, + createTermbaseStepThreeModule, + ) +} \ No newline at end of file diff --git a/feature-termbases/statistics/gradle.properties b/feature-termbases/statistics/gradle.properties deleted file mode 100644 index 056070a..0000000 --- a/feature-termbases/statistics/gradle.properties +++ /dev/null @@ -1,5 +0,0 @@ -kotlin.code.style=official - -kotlin.version=1.8.0 -agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file diff --git a/feature-termbases/metadata/.gitignore b/feature-termbases/wizard/.gitignore similarity index 100% rename from feature-termbases/metadata/.gitignore rename to feature-termbases/wizard/.gitignore diff --git a/feature-termbases/wizard/build.gradle.kts b/feature-termbases/wizard/build.gradle.kts new file mode 100644 index 0000000..ec22750 --- /dev/null +++ b/feature-termbases/wizard/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") +} + +group = "feature.termbases.wizard" +version = libs.versions.appVersion.get() + +repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") +} + +kotlin { + jvm { + jvmToolchain(11) + withJava() + } + sourceSets { + val jvmMain by getting { + dependencies { + implementation(compose.desktop.currentOs) + } + } + val jvmTest by getting + } +} \ No newline at end of file diff --git a/feature-termbases/statistics/.gitignore b/feature-termbases/wizard/definitionmodel/.gitignore similarity index 100% rename from feature-termbases/statistics/.gitignore rename to feature-termbases/wizard/definitionmodel/.gitignore diff --git a/feature-termbases/definitionmodel/build.gradle.kts b/feature-termbases/wizard/definitionmodel/build.gradle.kts similarity index 93% rename from feature-termbases/definitionmodel/build.gradle.kts rename to feature-termbases/wizard/definitionmodel/build.gradle.kts index 5aa5cbc..e400212 100644 --- a/feature-termbases/definitionmodel/build.gradle.kts +++ b/feature-termbases/wizard/definitionmodel/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-termbases/wizard/definitionmodel/gradle.properties b/feature-termbases/wizard/definitionmodel/gradle.properties new file mode 100644 index 0000000..ca25374 --- /dev/null +++ b/feature-termbases/wizard/definitionmodel/gradle.properties @@ -0,0 +1,5 @@ +kotlin.code.style=official + +kotlin.version=1.8.0 +agp.version=7.3.0 +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-termbases/metadata/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/wizard/definitionmodel/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from feature-termbases/metadata/gradle/wrapper/gradle-wrapper.jar rename to feature-termbases/wizard/definitionmodel/gradle/wrapper/gradle-wrapper.jar diff --git a/feature-termbases/metadata/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/wizard/definitionmodel/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from feature-termbases/metadata/gradle/wrapper/gradle-wrapper.properties rename to feature-termbases/wizard/definitionmodel/gradle/wrapper/gradle-wrapper.properties diff --git a/feature-termbases/metadata/gradlew b/feature-termbases/wizard/definitionmodel/gradlew similarity index 100% rename from feature-termbases/metadata/gradlew rename to feature-termbases/wizard/definitionmodel/gradlew diff --git a/feature-termbases/metadata/gradlew.bat b/feature-termbases/wizard/definitionmodel/gradlew.bat similarity index 100% rename from feature-termbases/metadata/gradlew.bat rename to feature-termbases/wizard/definitionmodel/gradlew.bat diff --git a/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/di/Module.kt b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/di/Module.kt new file mode 100644 index 0000000..5d49fc7 --- /dev/null +++ b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/di/Module.kt @@ -0,0 +1,16 @@ +package dialogcreate.steptwo.di + +import dialogcreate.steptwo.ui.CreateTermbaseWizardStepTwoComponent +import dialogcreate.steptwo.ui.DefaultCreateTermbaseWizardStepTwoComponent +import org.koin.dsl.module + +val createTermbaseStepTwoModule = module { + factory { + DefaultCreateTermbaseWizardStepTwoComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + propertyRepository = get(), + ) + } +} \ No newline at end of file diff --git a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseStepTwoUiState.kt b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseStepTwoUiState.kt similarity index 95% rename from feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseStepTwoUiState.kt rename to feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseStepTwoUiState.kt index 8179399..6d5a651 100644 --- a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseStepTwoUiState.kt +++ b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseStepTwoUiState.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.steptwo +package dialogcreate.steptwo.ui import data.PropertyLevel import data.PropertyModel diff --git a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseWizardStepTwo.kt b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseWizardStepTwo.kt similarity index 92% rename from feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseWizardStepTwo.kt rename to feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseWizardStepTwo.kt index 84dc2ff..1e0121b 100644 --- a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseWizardStepTwo.kt +++ b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseWizardStepTwo.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.steptwo +package dialogcreate.steptwo.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -50,30 +50,24 @@ import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.arkivanov.essenty.instancekeeper.getOrCreate import data.PropertyLevel import data.PropertyType import data.toReadableString import localized -import org.koin.java.KoinJavaComponent.inject -import ui.components.CustomSpinner -import ui.components.CustomTextField -import ui.components.StyledLabel -import ui.components.TreeItem -import ui.theme.Purple800 -import ui.theme.Spacing -import utils.AppBusiness +import common.ui.components.CustomSpinner +import common.ui.components.CustomTextField +import common.ui.components.StyledLabel +import common.ui.components.TreeItem +import common.ui.theme.Purple800 +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) @Composable fun CreateTermbaseWizardStepTwo( + component: CreateTermbaseWizardStepTwoComponent, modifier: Modifier = Modifier, ) { - val viewModel: CreateTermbaseWizardStepTwoViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepTwoViewModel by inject(CreateTermbaseWizardStepTwoViewModel::class.java) - res - } - val uiState by viewModel.uiState.collectAsState() + val uiState by component.uiState.collectAsState() Box(modifier = modifier) { Column(modifier = Modifier.padding(horizontal = Spacing.s, vertical = Spacing.s)) { Spacer(modifier = Modifier.height(Spacing.s)) @@ -129,7 +123,7 @@ fun CreateTermbaseWizardStepTwo( .onPointerEvent(PointerEventType.Exit) { hoveredValue = null } .padding(horizontal = Spacing.s, vertical = Spacing.xxs), onClick = { - viewModel.addProperty(level) + component.addProperty(level) newPropertyLevelChooserExpanded = false }, ) { @@ -146,7 +140,7 @@ fun CreateTermbaseWizardStepTwo( modifier = Modifier.width(20.dp).onClick( enabled = uiState.selectedProperty != null, onClick = { - viewModel.removeProperty() + component.removeProperty() }, ), imageVector = Icons.Outlined.RemoveCircle, @@ -197,7 +191,7 @@ fun CreateTermbaseWizardStepTwo( selected = item.property == uiState.selectedProperty, hasNext = nextItem?.indentLevel() == item.indentLevel(), onSelected = { - viewModel.selectProperty(item.property) + component.selectProperty(item.property) }, ) } @@ -221,7 +215,7 @@ fun CreateTermbaseWizardStepTwo( color = Color.Gray, ) } else { - val currentState by viewModel.currentPropertyState.collectAsState() + val currentState by component.currentPropertyState.collectAsState() StyledLabel( modifier = Modifier.fillMaxWidth().height(54.dp), @@ -236,7 +230,7 @@ fun CreateTermbaseWizardStepTwo( modifier = Modifier.fillMaxWidth(), value = currentState.name, onValueChange = { - viewModel.setCurrentPropertyName(it) + component.setCurrentPropertyName(it) }, ) } @@ -258,7 +252,7 @@ fun CreateTermbaseWizardStepTwo( values = values.map { it.toReadableString() }, current = currentState.level?.toReadableString(), onValueChanged = { - viewModel.setCurrentPropertyLevel(values[it]) + component.setCurrentPropertyLevel(values[it]) }, ) } @@ -280,7 +274,7 @@ fun CreateTermbaseWizardStepTwo( values = values.map { it.toReadableString() }, current = currentState.type?.toReadableString(), onValueChanged = { - viewModel.setCurrentPropertyType(values[it]) + component.setCurrentPropertyType(values[it]) }, ) } @@ -309,7 +303,7 @@ fun CreateTermbaseWizardStepTwo( when { it.type == KeyEventType.KeyDown && it.key == Key.Enter -> { if (newPicklistValue.isNotEmpty()) { - viewModel.addPicklistValue(newPicklistValue) + component.addPicklistValue(newPicklistValue) newPicklistValue = "" } true @@ -329,7 +323,7 @@ fun CreateTermbaseWizardStepTwo( .size(18.dp) .onClick { if (newPicklistValue.isNotEmpty()) { - viewModel.addPicklistValue(newPicklistValue) + component.addPicklistValue(newPicklistValue) newPicklistValue = "" } }, @@ -343,7 +337,7 @@ fun CreateTermbaseWizardStepTwo( PicklistValueCell( value = value, onRemove = { - viewModel.removePicklistValue(idx) + component.removePicklistValue(idx) }, ) } diff --git a/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseWizardStepTwoComponent.kt b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseWizardStepTwoComponent.kt new file mode 100644 index 0000000..457530e --- /dev/null +++ b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/CreateTermbaseWizardStepTwoComponent.kt @@ -0,0 +1,25 @@ +package dialogcreate.steptwo.ui + +import data.PropertyLevel +import data.PropertyModel +import data.PropertyType +import data.TermbaseModel +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow + +interface CreateTermbaseWizardStepTwoComponent { + val uiState: StateFlow + val currentPropertyState: StateFlow + val done: SharedFlow> + fun reset() + fun loadInitial(termbase: TermbaseModel) + fun selectProperty(value: PropertyModel) + fun addProperty(level: PropertyLevel) + fun removeProperty() + fun setCurrentPropertyName(value: String) + fun setCurrentPropertyLevel(value: PropertyLevel) + fun setCurrentPropertyType(value: PropertyType) + fun addPicklistValue(value: String) + fun removePicklistValue(index: Int) + fun submit() +} \ No newline at end of file diff --git a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseWizardStepTwoViewModel.kt b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/DefaultCreateTermbaseWizardStepTwoComponent.kt similarity index 74% rename from feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseWizardStepTwoViewModel.kt rename to feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/DefaultCreateTermbaseWizardStepTwoComponent.kt index cd889f4..027e83b 100644 --- a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/CreateTermbaseWizardStepTwoViewModel.kt +++ b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/DefaultCreateTermbaseWizardStepTwoComponent.kt @@ -1,7 +1,9 @@ -package ui.dialog.create.steptwo +package dialogcreate.steptwo.ui -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider import data.PicklistValueModel import data.PropertyLevel import data.PropertyModel @@ -10,22 +12,18 @@ import data.TermbaseModel 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.SharingStarted -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.updateAndGet +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import repository.PropertyRepository +import repo.PropertyRepository +import kotlin.coroutines.CoroutineContext import kotlin.math.max -class CreateTermbaseWizardStepTwoViewModel( +internal class DefaultCreateTermbaseWizardStepTwoComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, private val dispatcherProvider: CoroutineDispatcherProvider, private val propertyRepository: PropertyRepository, -) : InstanceKeeper.Instance { +) : CreateTermbaseWizardStepTwoComponent, ComponentContext by componentContext { private val items = MutableStateFlow>(emptyList()) private val selectedProperty = MutableStateFlow(null) @@ -35,47 +33,54 @@ class CreateTermbaseWizardStepTwoViewModel( private val currentPropertyType = MutableStateFlow(PropertyType.TEXT) private val currentPicklistValues = MutableStateFlow>(emptyList()) - private val _done = MutableSharedFlow>() - val done = _done.asSharedFlow() - private val viewModelScope = CoroutineScope(SupervisorJob()) + override val done = MutableSharedFlow>() + private lateinit var viewModelScope: CoroutineScope - val uiState = combine( - items, - selectedProperty, - ) { items, selectedProperty -> - CreateTermbaseStepTwoUiState( - items = items, - selectedProperty = selectedProperty, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = CreateTermbaseStepTwoUiState(), - ) + override lateinit var uiState: StateFlow + override lateinit var currentPropertyState: StateFlow - val currentPropertyState = combine( - currentPropertyName, - currentPropertyLevel, - currentPropertyType, - currentPicklistValues, - ) { name, level, type, picklistValues -> - CreteTermbaseStepTwoEditPropertyState( - name = name, - level = level, - type = type, - picklistValues = picklistValues, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = CreteTermbaseStepTwoEditPropertyState(), - ) - - override fun onDestroy() { - viewModelScope.cancel() + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + items, + selectedProperty, + ) { items, selectedProperty -> + CreateTermbaseStepTwoUiState( + items = items, + selectedProperty = selectedProperty, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = CreateTermbaseStepTwoUiState(), + ) + currentPropertyState = combine( + currentPropertyName, + currentPropertyLevel, + currentPropertyType, + currentPicklistValues, + ) { name, level, type, picklistValues -> + CreteTermbaseStepTwoEditPropertyState( + name = name, + level = level, + type = type, + picklistValues = picklistValues, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = CreteTermbaseStepTwoEditPropertyState(), + ) + } + doOnDestroy { + viewModelScope.cancel() + } + } } - fun reset() { + override fun reset() { items.value = emptyList() selectedProperty.value = null currentPropertyName.value = "" @@ -84,7 +89,7 @@ class CreateTermbaseWizardStepTwoViewModel( currentPicklistValues.value = emptyList() } - fun loadInitial(termbase: TermbaseModel) { + override fun loadInitial(termbase: TermbaseModel) { viewModelScope.launch(dispatcherProvider.io) { val data = propertyRepository.getAll(termbase.id) items.update { @@ -111,7 +116,7 @@ class CreateTermbaseWizardStepTwoViewModel( } } - fun selectProperty(value: PropertyModel) { + override fun selectProperty(value: PropertyModel) { selectedProperty.updateAndGet { if (it == value) { currentPropertyName.value = "" @@ -131,7 +136,7 @@ class CreateTermbaseWizardStepTwoViewModel( } } - fun addProperty(level: PropertyLevel) { + override fun addProperty(level: PropertyLevel) { items.updateAndGet { oldList -> val maxId = oldList.fold(0) { acc, current -> max( @@ -180,14 +185,14 @@ class CreateTermbaseWizardStepTwoViewModel( } } - fun removeProperty() { + override fun removeProperty() { val property = selectedProperty.value ?: return items.updateAndGet { it.filterNot { e -> e is CreateTermbaseStepTwoItem.Property && e.property.id == property.id } } } - fun setCurrentPropertyName(value: String) { + override fun setCurrentPropertyName(value: String) { currentPropertyName.value = value items.updateAndGet { val newList = mutableListOf() @@ -202,7 +207,7 @@ class CreateTermbaseWizardStepTwoViewModel( } } - fun setCurrentPropertyLevel(value: PropertyLevel) { + override fun setCurrentPropertyLevel(value: PropertyLevel) { currentPropertyLevel.value = value items.updateAndGet { val newList = mutableListOf() @@ -217,7 +222,7 @@ class CreateTermbaseWizardStepTwoViewModel( } } - fun setCurrentPropertyType(value: PropertyType) { + override fun setCurrentPropertyType(value: PropertyType) { currentPropertyType.value = value items.updateAndGet { val newList = mutableListOf() @@ -232,7 +237,7 @@ class CreateTermbaseWizardStepTwoViewModel( } } - fun addPicklistValue(value: String) { + override fun addPicklistValue(value: String) { val temporaryId = selectedProperty.value?.id ?: return currentPicklistValues.updateAndGet { val res = it + value @@ -267,7 +272,7 @@ class CreateTermbaseWizardStepTwoViewModel( } } - fun removePicklistValue(index: Int) { + override fun removePicklistValue(index: Int) { val temporaryId = selectedProperty.value?.id ?: return currentPicklistValues.updateAndGet { val res = it.filterIndexed { i, _ -> i != index } @@ -293,9 +298,9 @@ class CreateTermbaseWizardStepTwoViewModel( } } - fun submit() { + override fun submit() { viewModelScope.launch(dispatcherProvider.io) { - _done.emit(items.value.mapNotNull { (it as? CreateTermbaseStepTwoItem.Property)?.property }) + done.emit(items.value.mapNotNull { (it as? CreateTermbaseStepTwoItem.Property)?.property }) } } } diff --git a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/PicklistValueCell.kt b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/PicklistValueCell.kt similarity index 94% rename from feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/PicklistValueCell.kt rename to feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/PicklistValueCell.kt index 34ee4e3..a398ddc 100644 --- a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/PicklistValueCell.kt +++ b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/PicklistValueCell.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.steptwo +package dialogcreate.steptwo.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -18,8 +18,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import ui.theme.Purple800 -import ui.theme.Spacing +import common.ui.theme.Purple800 +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/PropertyCell.kt b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/PropertyCell.kt similarity index 94% rename from feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/PropertyCell.kt rename to feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/PropertyCell.kt index de9663f..7a428c7 100644 --- a/feature-termbases/definitionmodel/src/jvmMain/kotlin/ui/dialog/create/steptwo/PropertyCell.kt +++ b/feature-termbases/wizard/definitionmodel/src/jvmMain/kotlin/dialogcreate/steptwo/ui/PropertyCell.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.steptwo +package dialogcreate.steptwo.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -14,8 +14,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import ui.components.TreeItem -import ui.theme.Spacing +import common.ui.components.TreeItem +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/feature-termbases/create/gradle.properties b/feature-termbases/wizard/gradle.properties similarity index 98% rename from feature-termbases/create/gradle.properties rename to feature-termbases/wizard/gradle.properties index 056070a..b70abdd 100644 --- a/feature-termbases/create/gradle.properties +++ b/feature-termbases/wizard/gradle.properties @@ -1,5 +1,4 @@ kotlin.code.style=official - kotlin.version=1.8.0 agp.version=7.3.0 compose.version=1.3.0 \ No newline at end of file diff --git a/feature-termbases/statistics/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/wizard/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from feature-termbases/statistics/gradle/wrapper/gradle-wrapper.jar rename to feature-termbases/wizard/gradle/wrapper/gradle-wrapper.jar diff --git a/feature-termbases/statistics/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/wizard/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from feature-termbases/statistics/gradle/wrapper/gradle-wrapper.properties rename to feature-termbases/wizard/gradle/wrapper/gradle-wrapper.properties diff --git a/feature-termbases/statistics/gradlew b/feature-termbases/wizard/gradlew similarity index 100% rename from feature-termbases/statistics/gradlew rename to feature-termbases/wizard/gradlew diff --git a/feature-termbases/statistics/gradlew.bat b/feature-termbases/wizard/gradlew.bat similarity index 100% rename from feature-termbases/statistics/gradlew.bat rename to feature-termbases/wizard/gradlew.bat diff --git a/feature-terms/filter/.gitignore b/feature-termbases/wizard/inputmodel/.gitignore similarity index 100% rename from feature-terms/filter/.gitignore rename to feature-termbases/wizard/inputmodel/.gitignore diff --git a/feature-termbases/inputmodel/build.gradle.kts b/feature-termbases/wizard/inputmodel/build.gradle.kts similarity index 93% rename from feature-termbases/inputmodel/build.gradle.kts rename to feature-termbases/wizard/inputmodel/build.gradle.kts index e77e5b9..6331f73 100644 --- a/feature-termbases/inputmodel/build.gradle.kts +++ b/feature-termbases/wizard/inputmodel/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-termbases/wizard/inputmodel/gradle.properties b/feature-termbases/wizard/inputmodel/gradle.properties new file mode 100644 index 0000000..ca25374 --- /dev/null +++ b/feature-termbases/wizard/inputmodel/gradle.properties @@ -0,0 +1,5 @@ +kotlin.code.style=official + +kotlin.version=1.8.0 +agp.version=7.3.0 +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-terms/filter/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/wizard/inputmodel/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from feature-terms/filter/gradle/wrapper/gradle-wrapper.jar rename to feature-termbases/wizard/inputmodel/gradle/wrapper/gradle-wrapper.jar diff --git a/feature-terms/filter/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/wizard/inputmodel/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from feature-terms/filter/gradle/wrapper/gradle-wrapper.properties rename to feature-termbases/wizard/inputmodel/gradle/wrapper/gradle-wrapper.properties diff --git a/feature-terms/filter/gradlew b/feature-termbases/wizard/inputmodel/gradlew similarity index 100% rename from feature-terms/filter/gradlew rename to feature-termbases/wizard/inputmodel/gradlew diff --git a/feature-terms/filter/gradlew.bat b/feature-termbases/wizard/inputmodel/gradlew.bat similarity index 100% rename from feature-terms/filter/gradlew.bat rename to feature-termbases/wizard/inputmodel/gradlew.bat diff --git a/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/di/Module.kt b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/di/Module.kt new file mode 100644 index 0000000..3c79d3f --- /dev/null +++ b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/di/Module.kt @@ -0,0 +1,20 @@ +package dialogcreate.stepthree.di + +import dialogcreate.stepthree.ui.CreateTermbaseWizardStepThreeComponent +import dialogcreate.stepthree.ui.DefaultCreateTermbaseWizardStepThreeComponent +import org.koin.dsl.module + +val createTermbaseStepThreeModule = module { + factory { + DefaultCreateTermbaseWizardStepThreeComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + propertyRepository = get(), + languageRepository = get(), + languageNameRepository = get(), + flagsRepository = get(), + inputDescriptorRepository = get(), + ) + } +} \ No newline at end of file diff --git a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CheckableItem.kt b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CheckableItem.kt similarity index 95% rename from feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CheckableItem.kt rename to feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CheckableItem.kt index 67fa2b8..7998e4a 100644 --- a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CheckableItem.kt +++ b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CheckableItem.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.stepthree +package dialogcreate.stepthree.ui import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -15,8 +15,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import ui.components.TreeItem -import ui.theme.Spacing +import common.ui.components.TreeItem +import common.ui.theme.Spacing @Composable fun CheckableItem( diff --git a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThree.kt b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThree.kt similarity index 87% rename from feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThree.kt rename to feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThree.kt index f9e8874..9b98450 100644 --- a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThree.kt +++ b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThree.kt @@ -1,15 +1,7 @@ -package ui.dialog.create.stepthree +package dialogcreate.stepthree.ui import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape @@ -24,24 +16,17 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.arkivanov.essenty.instancekeeper.getOrCreate +import common.ui.components.TreeItem +import common.ui.theme.Spacing import data.PropertyLevel import data.toReadableString import localized -import org.koin.java.KoinJavaComponent.inject -import ui.components.TreeItem -import ui.theme.Spacing -import utils.AppBusiness @Composable fun CreateTermbaseWizardStepThree( + component: CreateTermbaseWizardStepThreeComponent, modifier: Modifier = Modifier, ) { - val viewModel: CreateTermbaseWizardStepThreeViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepThreeViewModel by inject(CreateTermbaseWizardStepThreeViewModel::class.java) - res - } - Box(modifier = modifier) { Column(modifier = Modifier.padding(horizontal = Spacing.s, vertical = Spacing.s)) { Spacer(modifier = Modifier.height(Spacing.s)) @@ -61,7 +46,7 @@ fun CreateTermbaseWizardStepThree( modifier = Modifier.fillMaxWidth().weight(1f), horizontalArrangement = Arrangement.spacedBy(Spacing.s), ) { - val uiState by viewModel.uiState.collectAsState() + val uiState by component.uiState.collectAsState() LazyColumn( modifier = Modifier .fillMaxHeight() @@ -127,7 +112,7 @@ fun CreateTermbaseWizardStepThree( hasNext = hasNext, previousLevels = previousLevels, onCheckedChange = { - viewModel.toggleSelection( + component.toggleSelection( item = item, selected = it, ) @@ -143,7 +128,7 @@ fun CreateTermbaseWizardStepThree( hasNext = hasNext, previousLevels = previousLevels, onCheckedChange = { - viewModel.toggleSelection( + component.toggleSelection( item = item, selected = it, ) diff --git a/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeComponent.kt b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeComponent.kt new file mode 100644 index 0000000..1a2a51e --- /dev/null +++ b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeComponent.kt @@ -0,0 +1,23 @@ +package dialogcreate.stepthree.ui + +import data.InputDescriptorModel +import data.LanguageModel +import data.PropertyModel +import data.TermbaseModel +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow + +interface CreateTermbaseWizardStepThreeComponent { + val done: SharedFlow> + val uiState: StateFlow + fun reset() + fun loadInitial(termbase: TermbaseModel) + fun loadItems( + properties: List, + languages: List, + oldInputModel: List, + ) + + fun toggleSelection(item: CreateTermbaseWizardStepThreeItem, selected: Boolean) + fun submit() +} \ No newline at end of file diff --git a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeItem.kt b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeItem.kt similarity index 94% rename from feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeItem.kt rename to feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeItem.kt index 28fad79..68ec7aa 100644 --- a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeItem.kt +++ b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeItem.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.stepthree +package dialogcreate.stepthree.ui import data.PropertyLevel import data.PropertyModel diff --git a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeUiState.kt b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeUiState.kt similarity index 77% rename from feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeUiState.kt rename to feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeUiState.kt index 4670e43..0eaa34a 100644 --- a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeUiState.kt +++ b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/CreateTermbaseWizardStepThreeUiState.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.stepthree +package dialogcreate.stepthree.ui data class CreateTermbaseWizardStepThreeUiState( val items: List = emptyList(), diff --git a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeViewModel.kt b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/DefaultCreateTermbaseWizardStepThreeComponent.kt similarity index 79% rename from feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeViewModel.kt rename to feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/DefaultCreateTermbaseWizardStepThreeComponent.kt index dcf4263..ff3eac9 100644 --- a/feature-termbases/inputmodel/src/jvmMain/kotlin/ui/dialog/create/stepthree/CreateTermbaseWizardStepThreeViewModel.kt +++ b/feature-termbases/wizard/inputmodel/src/jvmMain/kotlin/dialogcreate/stepthree/ui/DefaultCreateTermbaseWizardStepThreeComponent.kt @@ -1,7 +1,9 @@ -package ui.dialog.create.stepthree +package dialogcreate.stepthree.ui -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider import data.InputDescriptorModel import data.LanguageModel import data.PropertyLevel @@ -10,56 +12,59 @@ import data.TermbaseModel 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.SharingStarted -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.updateAndGet +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import repository.FlagsRepository -import repository.InputDescriptorRepository -import repository.LanguageNameRepository -import repository.LanguageRepository -import repository.PropertyRepository +import repo.FlagsRepository +import repo.InputDescriptorRepository +import repo.LanguageNameRepository +import repo.LanguageRepository +import repo.PropertyRepository +import kotlin.coroutines.CoroutineContext -class CreateTermbaseWizardStepThreeViewModel( +internal class DefaultCreateTermbaseWizardStepThreeComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, private val dispatcherProvider: CoroutineDispatcherProvider, private val propertyRepository: PropertyRepository, private val languageRepository: LanguageRepository, private val languageNameRepository: LanguageNameRepository, private val flagsRepository: FlagsRepository, private val inputDescriptorRepository: InputDescriptorRepository, -) : InstanceKeeper.Instance { +) : CreateTermbaseWizardStepThreeComponent, ComponentContext by componentContext { private val items = MutableStateFlow>(emptyList()) - private val _done = MutableSharedFlow>() - val done = _done.asSharedFlow() + override val done = MutableSharedFlow>() private var termbaseId: Int = 0 - private val viewModelScope = CoroutineScope(SupervisorJob()) + private lateinit var viewModelScope: CoroutineScope - val uiState = combine(items) { - CreateTermbaseWizardStepThreeUiState( - items = it.first(), - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = CreateTermbaseWizardStepThreeUiState(), - ) + override lateinit var uiState: StateFlow - override fun onDestroy() { - viewModelScope.cancel() + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine(items) { + CreateTermbaseWizardStepThreeUiState( + items = it.first(), + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = CreateTermbaseWizardStepThreeUiState(), + ) + } + doOnDestroy { + viewModelScope.cancel() + } + } } - fun reset() { + override fun reset() { items.value = emptyList() termbaseId = 0 } - fun loadInitial(termbase: TermbaseModel) { + override fun loadInitial(termbase: TermbaseModel) { termbaseId = termbase.id viewModelScope.launch(dispatcherProvider.io) { val properties = propertyRepository.getAll(termbaseId) @@ -73,7 +78,7 @@ class CreateTermbaseWizardStepThreeViewModel( } } - fun loadItems( + override fun loadItems( properties: List, languages: List, oldInputModel: List, @@ -120,7 +125,7 @@ class CreateTermbaseWizardStepThreeViewModel( } } - fun toggleSelection(item: CreateTermbaseWizardStepThreeItem, selected: Boolean) { + override fun toggleSelection(item: CreateTermbaseWizardStepThreeItem, selected: Boolean) { items.updateAndGet { val updatedSelection = it.map { e -> when (e) { @@ -163,7 +168,7 @@ class CreateTermbaseWizardStepThreeViewModel( } } - fun submit() { + override fun submit() { viewModelScope.launch(dispatcherProvider.io) { val existingInputModel = inputDescriptorRepository.getAll(termbaseId) @@ -199,7 +204,7 @@ class CreateTermbaseWizardStepThreeViewModel( } inputDescriptorModel } - _done.emit(result) + done.emit(result) } } } diff --git a/feature-termbases/wizard/metadata/.gitignore b/feature-termbases/wizard/metadata/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/feature-termbases/wizard/metadata/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/feature-termbases/metadata/build.gradle.kts b/feature-termbases/wizard/metadata/build.gradle.kts similarity index 93% rename from feature-termbases/metadata/build.gradle.kts rename to feature-termbases/wizard/metadata/build.gradle.kts index cb707ed..1b3758f 100644 --- a/feature-termbases/metadata/build.gradle.kts +++ b/feature-termbases/wizard/metadata/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-termbases/wizard/metadata/gradle.properties b/feature-termbases/wizard/metadata/gradle.properties new file mode 100644 index 0000000..ca25374 --- /dev/null +++ b/feature-termbases/wizard/metadata/gradle.properties @@ -0,0 +1,5 @@ +kotlin.code.style=official + +kotlin.version=1.8.0 +agp.version=7.3.0 +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-termbases/wizard/metadata/gradle/wrapper/gradle-wrapper.jar b/feature-termbases/wizard/metadata/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/feature-termbases/wizard/metadata/gradle/wrapper/gradle-wrapper.jar differ diff --git a/feature-termbases/wizard/metadata/gradle/wrapper/gradle-wrapper.properties b/feature-termbases/wizard/metadata/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ae04661 --- /dev/null +++ b/feature-termbases/wizard/metadata/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/feature-termbases/wizard/metadata/gradlew b/feature-termbases/wizard/metadata/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/feature-termbases/wizard/metadata/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/feature-termbases/wizard/metadata/gradlew.bat b/feature-termbases/wizard/metadata/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/feature-termbases/wizard/metadata/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/di/Module.kt b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/di/Module.kt new file mode 100644 index 0000000..2bd35aa --- /dev/null +++ b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/di/Module.kt @@ -0,0 +1,18 @@ +package dialogcreate.stepone.di + +import dialogcreate.stepone.ui.CreateTermbaseWizardStepOneComponent +import dialogcreate.stepone.ui.DefaultCreateTermbaseWizardStepOneComponent +import org.koin.dsl.module + +val createTermbaseStepOneModule = module { + factory { + DefaultCreateTermbaseWizardStepOneComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + languageRepository = get(), + languageNameRepository = get(), + flagsRepository = get(), + ) + } +} \ No newline at end of file diff --git a/feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseStepOneUiState.kt b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseStepOneUiState.kt similarity index 95% rename from feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseStepOneUiState.kt rename to feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseStepOneUiState.kt index b2a418b..5e96431 100644 --- a/feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseStepOneUiState.kt +++ b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseStepOneUiState.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.stepone +package dialogcreate.stepone.ui import data.LanguageModel diff --git a/feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseWizardStepOne.kt b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseWizardStepOne.kt similarity index 78% rename from feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseWizardStepOne.kt rename to feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseWizardStepOne.kt index f716ea6..c645193 100644 --- a/feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseWizardStepOne.kt +++ b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseWizardStepOne.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.stepone +package dialogcreate.stepone.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -15,25 +15,19 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.arkivanov.essenty.instancekeeper.getOrCreate import localized -import org.koin.java.KoinJavaComponent.inject -import ui.components.CustomDialog -import ui.components.CustomTextField -import ui.theme.Spacing -import utils.AppBusiness +import common.ui.components.CustomDialog +import common.ui.components.CustomTextField +import common.ui.theme.Spacing @Composable fun CreateTermbaseWizardStepOne( + component: CreateTermbaseWizardStepOneComponent, modifier: Modifier = Modifier, ) { - val viewModel: CreateTermbaseWizardStepOneViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepOneViewModel by inject(CreateTermbaseWizardStepOneViewModel::class.java) - res - } - val uiState by viewModel.uiState.collectAsState() - val errorState by viewModel.errorUiState.collectAsState() - val languageState by viewModel.languagesUiState.collectAsState() + val uiState by component.uiState.collectAsState() + val errorState by component.errorUiState.collectAsState() + val languageState by component.languagesUiState.collectAsState() Box( modifier = modifier, @@ -49,7 +43,7 @@ fun CreateTermbaseWizardStepOne( value = uiState.name, singleLine = true, onValueChange = { - viewModel.setName(it) + component.setName(it) }, ) if (errorState.nameError.isNotEmpty()) { @@ -66,7 +60,7 @@ fun CreateTermbaseWizardStepOne( modifier = Modifier.height(100.dp), value = uiState.description, onValueChange = { - viewModel.setDescription(it) + component.setDescription(it) }, ) Spacer(modifier = Modifier.height(Spacing.s)) @@ -83,16 +77,16 @@ fun CreateTermbaseWizardStepOne( selectedLanguages = languageState.selectedLanguages, currentSelectedLanguage = languageState.currentSelectedLanguage, onArrowLeft = { - viewModel.onArrowLeft() + component.onArrowLeft() }, onArrowRight = { - viewModel.onArrowRight() + component.onArrowRight() }, onAvailableLanguageClicked = { - viewModel.onAvailableClick(it) + component.onAvailableClick(it) }, onSelectedLanguageClicked = { - viewModel.onSelectedClick(it) + component.onSelectedClick(it) }, ) if (errorState.languagesError.isNotEmpty()) { @@ -111,7 +105,7 @@ fun CreateTermbaseWizardStepOne( message = errorState.genericError, closeButtonText = "button_close".localized(), onClose = { - viewModel.clearErrors() + component.clearErrors() }, ) } diff --git a/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseWizardStepOneComponent.kt b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseWizardStepOneComponent.kt new file mode 100644 index 0000000..6fa5a44 --- /dev/null +++ b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/CreateTermbaseWizardStepOneComponent.kt @@ -0,0 +1,23 @@ +package dialogcreate.stepone.ui + +import data.LanguageModel +import data.TermbaseModel +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow + +interface CreateTermbaseWizardStepOneComponent { + val done: SharedFlow>> + val uiState: StateFlow + val errorUiState: StateFlow + val languagesUiState: StateFlow + fun reset() + fun loadInitial(termbase: TermbaseModel) + fun setName(value: String) + fun setDescription(value: String) + fun onAvailableClick(language: LanguageModel) + fun onArrowRight() + fun onSelectedClick(language: LanguageModel) + fun onArrowLeft() + fun clearErrors() + fun submit() +} \ No newline at end of file diff --git a/feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseWizardStepOneViewModel.kt b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/DefaultCreateTermbaseWizardStepOneComponent.kt similarity index 50% rename from feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseWizardStepOneViewModel.kt rename to feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/DefaultCreateTermbaseWizardStepOneComponent.kt index a77a498..2938e6c 100644 --- a/feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/CreateTermbaseWizardStepOneViewModel.kt +++ b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/DefaultCreateTermbaseWizardStepOneComponent.kt @@ -1,32 +1,30 @@ -package ui.dialog.create.stepone +package dialogcreate.stepone.ui -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider import data.LanguageModel import data.TermbaseModel 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.SharingStarted -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.getAndUpdate -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import localized -import repository.FlagsRepository -import repository.LanguageNameRepository -import repository.LanguageRepository - -class CreateTermbaseWizardStepOneViewModel( +import repo.FlagsRepository +import repo.LanguageNameRepository +import repo.LanguageRepository +import kotlin.coroutines.CoroutineContext + +internal class DefaultCreateTermbaseWizardStepOneComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, private val dispatcherProvider: CoroutineDispatcherProvider, private val languageRepository: LanguageRepository, private val languageNameRepository: LanguageNameRepository, private val flagsRepository: FlagsRepository, -) : InstanceKeeper.Instance { +) : CreateTermbaseWizardStepOneComponent, ComponentContext by componentContext { private val name = MutableStateFlow("") private val description = MutableStateFlow("") @@ -37,61 +35,70 @@ class CreateTermbaseWizardStepOneViewModel( private val nameError = MutableStateFlow("") private val languagesError = MutableStateFlow("") - private val _done = MutableSharedFlow>>() - val done = _done.asSharedFlow() - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val uiState = combine( - name, - description, - ) { name, description -> - CreateTermbaseStepOneUiState( - name = name, - description = description, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = CreateTermbaseStepOneUiState(), - ) - - val errorUiState = combine( - nameError, - languagesError, - ) { nameError, languagesError -> - CreateTermbaseUiErrorState( - nameError = nameError, - languagesError = languagesError, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = CreateTermbaseUiErrorState(), - ) - - val languagesUiState = combine( - availableLanguages, - currentAvailableLanguage, - selectedLanguages, - currentSelectedLanguage, - ) { availableLanguages, currentAvailableLanguage, selectedLanguages, currentSelectedLanguage -> - CreateTermbaseLanguageUiState( - availableLanguages = availableLanguages, - currentAvailableLanguage = currentAvailableLanguage, - selectedLanguages = selectedLanguages, - currentSelectedLanguage = currentSelectedLanguage, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = CreateTermbaseLanguageUiState(), - ) - - override fun onDestroy() { - viewModelScope.cancel() + override val done = MutableSharedFlow>>() + private lateinit var viewModelScope: CoroutineScope + + override lateinit var uiState: StateFlow + override lateinit var errorUiState: StateFlow + override lateinit var languagesUiState: StateFlow + + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + name, + description, + ) { name, description -> + CreateTermbaseStepOneUiState( + name = name, + description = description, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = CreateTermbaseStepOneUiState(), + ) + errorUiState = combine( + nameError, + languagesError, + ) { nameError, languagesError -> + CreateTermbaseUiErrorState( + nameError = nameError, + languagesError = languagesError, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = CreateTermbaseUiErrorState(), + ) + languagesUiState = combine( + availableLanguages, + currentAvailableLanguage, + selectedLanguages, + currentSelectedLanguage, + ) { availableLanguages, currentAvailableLanguage, selectedLanguages, currentSelectedLanguage -> + CreateTermbaseLanguageUiState( + availableLanguages = availableLanguages, + currentAvailableLanguage = currentAvailableLanguage, + selectedLanguages = selectedLanguages, + currentSelectedLanguage = currentSelectedLanguage, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = CreateTermbaseLanguageUiState(), + ) + + reset() + } + doOnDestroy { + viewModelScope.cancel() + } + } } - fun reset() { + override fun reset() { val languages = languageRepository.getDefaultLanguages().map { val flag = flagsRepository.getFlag(it.code) val name = languageNameRepository.getName(it.code) @@ -109,7 +116,7 @@ class CreateTermbaseWizardStepOneViewModel( languagesError.value = "" } - fun loadInitial(termbase: TermbaseModel) { + override fun loadInitial(termbase: TermbaseModel) { name.value = termbase.name description.value = termbase.description @@ -130,19 +137,19 @@ class CreateTermbaseWizardStepOneViewModel( } } - fun setName(value: String) { + override fun setName(value: String) { name.value = value } - fun setDescription(value: String) { + override fun setDescription(value: String) { description.value = value } - fun onAvailableClick(language: LanguageModel) { + override fun onAvailableClick(language: LanguageModel) { currentAvailableLanguage.value = language } - fun onArrowRight() { + override fun onArrowRight() { val language = currentAvailableLanguage.getAndUpdate { null } ?: return selectedLanguages.update { (it + language).sortedBy { e -> e.name } @@ -152,11 +159,11 @@ class CreateTermbaseWizardStepOneViewModel( } } - fun onSelectedClick(language: LanguageModel) { + override fun onSelectedClick(language: LanguageModel) { currentSelectedLanguage.value = language } - fun onArrowLeft() { + override fun onArrowLeft() { val language = currentSelectedLanguage.getAndUpdate { null } ?: return availableLanguages.update { (it + language).sortedBy { e -> e.name } @@ -166,12 +173,12 @@ class CreateTermbaseWizardStepOneViewModel( } } - fun clearErrors() { + override fun clearErrors() { nameError.value = "" languagesError.value = "" } - fun submit() { + override fun submit() { clearErrors() val name = name.value if (name.trim().isEmpty()) { @@ -190,7 +197,7 @@ class CreateTermbaseWizardStepOneViewModel( ) viewModelScope.launch { - _done.emit(termbase to selectedLanguages) + done.emit(termbase to selectedLanguages) } } } diff --git a/feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/TermbaseLanguageSelector.kt b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/TermbaseLanguageSelector.kt similarity index 98% rename from feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/TermbaseLanguageSelector.kt rename to feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/TermbaseLanguageSelector.kt index 2b36a21..e49ef2f 100644 --- a/feature-termbases/metadata/src/jvmMain/kotlin/ui/dialog/create/stepone/TermbaseLanguageSelector.kt +++ b/feature-termbases/wizard/metadata/src/jvmMain/kotlin/dialogcreate/stepone/ui/TermbaseLanguageSelector.kt @@ -1,4 +1,4 @@ -package ui.dialog.create.stepone +package dialogcreate.stepone.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import data.LanguageModel -import ui.theme.Spacing +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/feature-terms/build.gradle.kts b/feature-terms/build.gradle.kts index 4f77ac7..3035f5e 100644 --- a/feature-terms/build.gradle.kts +++ b/feature-terms/build.gradle.kts @@ -22,7 +22,8 @@ kotlin { dependencies { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) + implementation(libs.decompose.extensions) implementation(compose.foundation) implementation(compose.animation) @@ -36,8 +37,8 @@ kotlin { implementation(projects.coreLocalization) api(projects.featureTerms.list) - api(projects.featureTerms.filter) api(projects.featureTerms.detail) + api(projects.featureTerms.dialog.filter) } } val jvmTest by getting diff --git a/feature-terms/detail/build.gradle.kts b/feature-terms/detail/build.gradle.kts index 8eed3c6..8224499 100644 --- a/feature-terms/detail/build.gradle.kts +++ b/feature-terms/detail/build.gradle.kts @@ -23,7 +23,8 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) + implementation(libs.decompose.extensions) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-terms/detail/gradle.properties b/feature-terms/detail/gradle.properties index b70abdd..15a3af6 100644 --- a/feature-terms/detail/gradle.properties +++ b/feature-terms/detail/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-terms/detail/src/jvmMain/kotlin/termdetail/di/Module.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/di/Module.kt new file mode 100644 index 0000000..9360970 --- /dev/null +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/di/Module.kt @@ -0,0 +1,27 @@ +package termdetail.di + +import org.koin.dsl.module +import termdetail.ui.DefaultTermDetailComponent +import termdetail.ui.TermDetailComponent + +val termDetailModule = module { + factory { + DefaultTermDetailComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + fileManager = get(), + termRepository = get(), + flagsRepository = get(), + entryPropertyValueRepository = get(), + languagePropertyValueRepository = get(), + termPropertyValueRepository = get(), + languageNameRepository = get(), + notificationCenter = get(), + deleteTermUseCase = get(), + propertyRepository = get(), + inputDescriptorRepository = get(), + log = get(), + ) + } +} \ No newline at end of file diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetailViewModel.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/DefaultTermDetailComponent.kt similarity index 88% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetailViewModel.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/DefaultTermDetailComponent.kt index 209c94a..e00b317 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetailViewModel.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/DefaultTermDetailComponent.kt @@ -1,7 +1,9 @@ -package ui.detail +package termdetail.ui -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider import data.InputDescriptorModel import data.LanguageModel import data.PropertyLevel @@ -10,37 +12,33 @@ import data.PropertyType import data.PropertyValueModel import data.SearchCriterion import data.TermModel -import files.FileManager +import common.files.FileManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.getAndUpdate -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import log.LogManager -import notification.NotificationCenter -import repository.EntryPropertyValueRepository -import repository.FlagsRepository -import repository.InputDescriptorRepository -import repository.LanguageNameRepository -import repository.LanguagePropertyValueRepository -import repository.PropertyRepository -import repository.TermPropertyValueRepository -import repository.TermRepository +import common.log.LogManager +import common.notification.NotificationCenter +import kotlinx.coroutines.flow.* +import repo.EntryPropertyValueRepository +import repo.FlagsRepository +import repo.InputDescriptorRepository +import repo.LanguageNameRepository +import repo.LanguagePropertyValueRepository +import repo.PropertyRepository +import repo.TermPropertyValueRepository +import repo.TermRepository import usecase.DeleteTermUseCase import java.io.File import java.util.* +import kotlin.coroutines.CoroutineContext -class TermDetailViewModel( +internal class DefaultTermDetailComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, private val dispatcherProvider: CoroutineDispatcherProvider, private val fileManager: FileManager, private val termRepository: TermRepository, @@ -54,7 +52,7 @@ class TermDetailViewModel( private val deleteTermUseCase: DeleteTermUseCase, private val notificationCenter: NotificationCenter, private val log: LogManager, -) : InstanceKeeper.Instance { +) : TermDetailComponent, ComponentContext by componentContext { private val items = MutableStateFlow>(emptyList()) private val loading = MutableStateFlow(false) @@ -72,57 +70,63 @@ class TermDetailViewModel( private val termsToDelete = mutableListOf() private val propertiesToDelete = mutableListOf>() private val mutex = Mutex() - private val viewModelScope = CoroutineScope(SupervisorJob()) + private lateinit var viewModelScope: CoroutineScope - val uiState = combine(items, loading) { items, loading -> - TermDetailUiState( - items = items, - loading = loading, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = TermDetailUiState(), - ) - - val availablePropertiesUiState = combine( - entryLevelProperties, - languageLevelProperties, - termLevelProperties, - ) { entryLevelProperties, languageLevelProperties, termLevelProperties -> - TermDetailAvailablePropertiesUiState( - entryLevelProperties = entryLevelProperties, - languageLevelProperties = languageLevelProperties, - termLevelProperties = termLevelProperties, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = TermDetailAvailablePropertiesUiState(), - ) + override lateinit var uiState: StateFlow + override lateinit var availablePropertiesUiState: StateFlow init { - viewModelScope.launch(dispatcherProvider.io) { - launch { - notificationCenter.events.filter { it is NotificationCenter.Event.CurrentLanguagesEdited }.collect { - mutex.withLock { - reloadItems() + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine(items, loading) { items, loading -> + TermDetailUiState( + items = items, + loading = loading, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = TermDetailUiState(), + ) + availablePropertiesUiState = combine( + entryLevelProperties, + languageLevelProperties, + termLevelProperties, + ) { entryLevelProperties, languageLevelProperties, termLevelProperties -> + TermDetailAvailablePropertiesUiState( + entryLevelProperties = entryLevelProperties, + languageLevelProperties = languageLevelProperties, + termLevelProperties = termLevelProperties, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = TermDetailAvailablePropertiesUiState(), + ) + + viewModelScope.launch(dispatcherProvider.io) { + launch { + notificationCenter.events.filter { it is NotificationCenter.Event.CurrentLanguagesEdited }.collect { + mutex.withLock { + reloadItems() + } + } + } + launch { + notificationCenter.events.filter { it is NotificationCenter.Event.SaveEntry }.collect { + save() + } } } } - launch { - notificationCenter.events.filter { it is NotificationCenter.Event.SaveEntry }.collect { - save() - } + doOnDestroy { + viewModelScope.cancel() } } } - override fun onDestroy() { - viewModelScope.cancel() - } - - fun setEditMode(value: Boolean) { + override fun setEditMode(value: Boolean) { val oldValue = editMode if (oldValue != value) { viewModelScope.launch(dispatcherProvider.io) { @@ -159,17 +163,17 @@ class TermDetailViewModel( return conditions.all { it } } - fun load( + override fun load( entryId: Int?, termbaseId: Int?, - newEntry: Boolean = false, - searchCriteria: List = emptyList(), + newEntry: Boolean, + searchCriteria: List, ) { viewModelScope.launch(dispatcherProvider.io) { mutex.withLock { - this@TermDetailViewModel.entryId = entryId ?: 0 - this@TermDetailViewModel.termbaseId = termbaseId ?: 0 - this@TermDetailViewModel.searchCriteria = searchCriteria + this@DefaultTermDetailComponent.entryId = entryId ?: 0 + this@DefaultTermDetailComponent.termbaseId = termbaseId ?: 0 + this@DefaultTermDetailComponent.searchCriteria = searchCriteria // if the entry is new, wait for the setEditMode(true) afterwards if (!newEntry) { reloadItems() @@ -178,7 +182,7 @@ class TermDetailViewModel( } } - fun setLanguages(languages: List) { + override fun setLanguages(languages: List) { this.languages = languages viewModelScope.launch(dispatcherProvider.io) { mutex.withLock { @@ -269,7 +273,7 @@ class TermDetailViewModel( loading.value = false } - fun calculateAvailableEntryProperties() { + override fun calculateAvailableEntryProperties() { viewModelScope.launch(dispatcherProvider.io) { val usedPropertyIds = items.value.mapNotNull { when { @@ -284,7 +288,7 @@ class TermDetailViewModel( } } - fun calculateAvailableLanguageProperties(index: Int) { + override fun calculateAvailableLanguageProperties(index: Int) { viewModelScope.launch(dispatcherProvider.io) { val usedPropertyIds = mutableListOf() if (index > 0) { @@ -303,7 +307,7 @@ class TermDetailViewModel( } } - fun calculateAvailableTermProperties(index: Int) { + override fun calculateAvailableTermProperties(index: Int) { viewModelScope.launch(dispatcherProvider.io) { val usedPropertyIds = mutableListOf() if (index > 0) { @@ -322,7 +326,7 @@ class TermDetailViewModel( } } - fun save() { + override fun save() { if (loading.value) { return } @@ -590,7 +594,7 @@ class TermDetailViewModel( } } - fun startInsertTermAt(index: Int, langCode: String) { + override fun startInsertTermAt(index: Int, langCode: String) { items.getAndUpdate { val newItems = mutableListOf() for (i in it.indices) { @@ -606,7 +610,7 @@ class TermDetailViewModel( } } - fun markTermFormDeletion(index: Int) { + override fun markTermFormDeletion(index: Int) { items.getAndUpdate { val newItems = mutableListOf() var deletingPropertiesAfterTerm: Boolean @@ -639,7 +643,7 @@ class TermDetailViewModel( } } - fun setTermLemma(index: Int, lemma: String) { + override fun setTermLemma(index: Int, lemma: String) { items.getAndUpdate { val newItems = mutableListOf() for (i in it.indices) { @@ -687,14 +691,14 @@ class TermDetailViewModel( // Property management /////////////////////////////////////////////////////////////////////////// - fun startInsertPropertyAt( + override fun startInsertPropertyAt( index: Int, valueId: Int, propertyId: Int, entryId: Int?, languageId: Int?, termId: Int?, - new: Boolean = false, + new: Boolean, ) { viewModelScope.launch(dispatcherProvider.io) { val property = propertyRepository.getById(propertyId) @@ -747,7 +751,7 @@ class TermDetailViewModel( } } - fun markPropertyForDeletion(index: Int) { + override fun markPropertyForDeletion(index: Int) { items.getAndUpdate { val newItems = mutableListOf() for (i in it.indices) { @@ -765,7 +769,7 @@ class TermDetailViewModel( } } - fun setPropertyValue(value: String, index: Int) { + override fun setPropertyValue(value: String, index: Int) { items.getAndUpdate { val newItems = mutableListOf() for (i in it.indices) { @@ -862,7 +866,7 @@ class TermDetailViewModel( res = languagePropertyValueRepository.create( model = newValue, languageId = languageId, - entryId = this@TermDetailViewModel.entryId, + entryId = this@DefaultTermDetailComponent.entryId, ) } } diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetail.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetail.kt similarity index 89% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetail.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetail.kt index 126123f..4b0cc7a 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetail.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetail.kt @@ -1,4 +1,4 @@ -package ui.detail +package termdetail.ui import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -30,40 +30,34 @@ import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.arkivanov.essenty.instancekeeper.getOrCreate import data.EntryModel import data.PropertyModel import data.SearchCriterion import localized -import org.koin.java.KoinJavaComponent.inject -import ui.detail.widgets.CreateButton -import ui.detail.widgets.EntryIdWidget -import ui.detail.widgets.LanguageTitleWidget -import ui.detail.widgets.PropertyInputWidget -import ui.detail.widgets.PropertyWidget -import ui.detail.widgets.TermInputWidget -import ui.detail.widgets.TermWidget -import ui.theme.DeepPurple300 -import ui.theme.DeepPurple800 -import ui.theme.Indigo800 -import ui.theme.Purple800 -import ui.theme.Spacing -import utils.AppBusiness +import termdetail.ui.widgets.CreateButton +import termdetail.ui.widgets.EntryIdWidget +import termdetail.ui.widgets.LanguageTitleWidget +import termdetail.ui.widgets.PropertyInputWidget +import termdetail.ui.widgets.PropertyWidget +import termdetail.ui.widgets.TermInputWidget +import termdetail.ui.widgets.TermWidget +import common.ui.theme.DeepPurple300 +import common.ui.theme.DeepPurple800 +import common.ui.theme.Indigo800 +import common.ui.theme.Purple800 +import common.ui.theme.Spacing @OptIn(ExperimentalComposeUiApi::class) @Composable fun TermDetail( + component: TermDetailComponent, entry: EntryModel?, modifier: Modifier = Modifier, editMode: Boolean = false, searchCriteria: List = emptyList(), ) { - val viewModel: TermDetailViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: TermDetailViewModel by inject(TermDetailViewModel::class.java) - res - } LaunchedEffect(entry, searchCriteria) { - viewModel.load( + component.load( entryId = entry?.id, termbaseId = entry?.termbaseId, newEntry = entry?.new ?: false, @@ -71,10 +65,10 @@ fun TermDetail( ) } LaunchedEffect(editMode) { - viewModel.setEditMode(editMode) + component.setEditMode(editMode) } - val uiState by viewModel.uiState.collectAsState() - val availablePropertiesUiState by viewModel.availablePropertiesUiState.collectAsState() + val uiState by component.uiState.collectAsState() + val availablePropertiesUiState by component.availablePropertiesUiState.collectAsState() Box( modifier = modifier.padding(horizontal = Spacing.s).fillMaxSize() @@ -138,7 +132,7 @@ fun TermDetail( color = DeepPurple300, title = "term_detail_add_term".localized(), onClick = { - viewModel.startInsertTermAt(index = idx, langCode = termDetailItem.langCode) + component.startInsertTermAt(index = idx, langCode = termDetailItem.langCode) }, ) } @@ -147,10 +141,10 @@ fun TermDetail( TermInputWidget( value = termDetailItem.lemma, onValueChange = { - viewModel.setTermLemma(index = idx, lemma = it) + component.setTermLemma(index = idx, lemma = it) }, onDelete = { - viewModel.markTermFormDeletion(idx) + component.markTermFormDeletion(idx) }, ) } @@ -178,15 +172,15 @@ fun TermDetail( onClick = { when { termDetailItem.entryId != null -> { - viewModel.calculateAvailableEntryProperties() + component.calculateAvailableEntryProperties() } termDetailItem.languageId != null -> { - viewModel.calculateAvailableLanguageProperties(index = idx) + component.calculateAvailableLanguageProperties(index = idx) } termDetailItem.termId != null -> { - viewModel.calculateAvailableTermProperties(index = idx) + component.calculateAvailableTermProperties(index = idx) } else -> Unit @@ -220,7 +214,7 @@ fun TermDetail( .onPointerEvent(PointerEventType.Exit) { hoveredValue = null } .padding(horizontal = Spacing.s, vertical = Spacing.xxs), onClick = { - viewModel.startInsertPropertyAt( + component.startInsertPropertyAt( propertyId = property.id, entryId = termDetailItem.entryId, languageId = termDetailItem.languageId, @@ -270,10 +264,10 @@ fun TermDetail( picklistValues = termDetailItem.picklistValues, color = color, onValueChange = { - viewModel.setPropertyValue(value = it, index = idx) + component.setPropertyValue(value = it, index = idx) }, onDelete = { - viewModel.markPropertyForDeletion(index = idx) + component.markPropertyForDeletion(index = idx) }, ) } diff --git a/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetailComponent.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetailComponent.kt new file mode 100644 index 0000000..d3fd172 --- /dev/null +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetailComponent.kt @@ -0,0 +1,38 @@ +package termdetail.ui + +import data.LanguageModel +import data.SearchCriterion +import kotlinx.coroutines.flow.StateFlow + +interface TermDetailComponent { + val uiState: StateFlow + val availablePropertiesUiState: StateFlow + fun setEditMode(value: Boolean) + fun load( + entryId: Int?, + termbaseId: Int?, + newEntry: Boolean = false, + searchCriteria: List = emptyList(), + ) + + fun setLanguages(languages: List) + fun calculateAvailableEntryProperties() + fun calculateAvailableLanguageProperties(index: Int) + fun calculateAvailableTermProperties(index: Int) + fun save() + fun startInsertTermAt(index: Int, langCode: String) + fun markTermFormDeletion(index: Int) + fun setTermLemma(index: Int, lemma: String) + fun startInsertPropertyAt( + index: Int, + valueId: Int, + propertyId: Int, + entryId: Int?, + languageId: Int?, + termId: Int?, + new: Boolean = false, + ) + + fun markPropertyForDeletion(index: Int) + fun setPropertyValue(value: String, index: Int) +} \ No newline at end of file diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetailUiState.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetailUiState.kt similarity index 98% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetailUiState.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetailUiState.kt index 8da1d61..cd7be64 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/TermDetailUiState.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/TermDetailUiState.kt @@ -1,4 +1,4 @@ -package ui.detail +package termdetail.ui import data.LanguageModel import data.PropertyModel diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/CreateButton.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/CreateButton.kt similarity index 95% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/CreateButton.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/CreateButton.kt index ec7df0c..251743f 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/CreateButton.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/CreateButton.kt @@ -1,4 +1,4 @@ -package ui.detail.widgets +package termdetail.ui.widgets import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Row @@ -16,7 +16,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import ui.theme.Spacing +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/EntryIdWidget.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/EntryIdWidget.kt similarity index 94% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/EntryIdWidget.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/EntryIdWidget.kt index 816253c..baf6a9d 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/EntryIdWidget.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/EntryIdWidget.kt @@ -1,4 +1,4 @@ -package ui.detail.widgets +package termdetail.ui.widgets import androidx.compose.material.MaterialTheme import androidx.compose.material.Text diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/LanguageTitleWidget.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/LanguageTitleWidget.kt similarity index 94% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/LanguageTitleWidget.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/LanguageTitleWidget.kt index c05c9dc..6f235b1 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/LanguageTitleWidget.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/LanguageTitleWidget.kt @@ -1,4 +1,4 @@ -package ui.detail.widgets +package termdetail.ui.widgets import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row @@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp -import ui.theme.Spacing +import common.ui.theme.Spacing @Composable fun LanguageTitleWidget( diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/PropertyInputWidget.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/PropertyInputWidget.kt similarity index 98% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/PropertyInputWidget.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/PropertyInputWidget.kt index c512d3a..1b09a97 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/PropertyInputWidget.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/PropertyInputWidget.kt @@ -1,4 +1,4 @@ -package ui.detail.widgets +package termdetail.ui.widgets import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image @@ -40,9 +40,9 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import data.PropertyType import localized -import ui.components.CustomOpenFileDialog -import ui.components.CustomSpinner -import ui.theme.Spacing +import common.ui.components.CustomOpenFileDialog +import common.ui.components.CustomSpinner +import common.ui.theme.Spacing import java.io.File @OptIn(ExperimentalFoundationApi::class) diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/PropertyWidget.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/PropertyWidget.kt similarity index 96% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/PropertyWidget.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/PropertyWidget.kt index 6af5f1d..f5a2c7e 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/PropertyWidget.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/PropertyWidget.kt @@ -1,4 +1,4 @@ -package ui.detail.widgets +package termdetail.ui.widgets import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column @@ -23,8 +23,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import data.PropertyType import localized -import ui.components.StyledLabel -import ui.theme.Spacing +import common.ui.components.StyledLabel +import common.ui.theme.Spacing import java.io.File @Composable diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/TermInputWidget.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/TermInputWidget.kt similarity index 94% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/TermInputWidget.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/TermInputWidget.kt index a1431d7..0f126cf 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/TermInputWidget.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/TermInputWidget.kt @@ -1,4 +1,4 @@ -package ui.detail.widgets +package termdetail.ui.widgets import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -21,9 +21,9 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import localized -import ui.components.CustomTextField -import ui.theme.DeepPurple300 -import ui.theme.Spacing +import common.ui.components.CustomTextField +import common.ui.theme.DeepPurple300 +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/TermWidget.kt b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/TermWidget.kt similarity index 86% rename from feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/TermWidget.kt rename to feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/TermWidget.kt index 99e4070..8a5614f 100644 --- a/feature-terms/detail/src/jvmMain/kotlin/ui/detail/widgets/TermWidget.kt +++ b/feature-terms/detail/src/jvmMain/kotlin/termdetail/ui/widgets/TermWidget.kt @@ -1,4 +1,4 @@ -package ui.detail.widgets +package termdetail.ui.widgets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.MaterialTheme @@ -7,8 +7,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.sp import localized -import ui.components.StyledLabel -import ui.theme.DeepPurple300 +import common.ui.components.StyledLabel +import common.ui.theme.DeepPurple300 @Composable fun TermWidget( diff --git a/feature-terms/dialog/.gitignore b/feature-terms/dialog/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/feature-terms/dialog/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/feature-terms/dialog/build.gradle.kts b/feature-terms/dialog/build.gradle.kts new file mode 100644 index 0000000..026728b --- /dev/null +++ b/feature-terms/dialog/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") +} + +group = "feature.terms.dialog" +version = libs.versions.appVersion.get() + +repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") +} + +kotlin { + jvm { + jvmToolchain(11) + withJava() + } + sourceSets { + val jvmMain by getting { + dependencies { + implementation(compose.desktop.currentOs) + } + } + val jvmTest by getting + } +} \ No newline at end of file diff --git a/feature-terms/dialog/filter/.gitignore b/feature-terms/dialog/filter/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/feature-terms/dialog/filter/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/feature-terms/filter/build.gradle.kts b/feature-terms/dialog/filter/build.gradle.kts similarity index 93% rename from feature-terms/filter/build.gradle.kts rename to feature-terms/dialog/filter/build.gradle.kts index 480f898..6cb6828 100644 --- a/feature-terms/filter/build.gradle.kts +++ b/feature-terms/dialog/filter/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) implementation(libs.koin) - implementation(libs.essenty.instancekeeper) + implementation(libs.decompose) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-terms/dialog/filter/gradle.properties b/feature-terms/dialog/filter/gradle.properties new file mode 100644 index 0000000..15a3af6 --- /dev/null +++ b/feature-terms/dialog/filter/gradle.properties @@ -0,0 +1,4 @@ +kotlin.code.style=official +kotlin.version=1.8.0 +agp.version=7.3.0 +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-terms/dialog/filter/gradle/wrapper/gradle-wrapper.jar b/feature-terms/dialog/filter/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/feature-terms/dialog/filter/gradle/wrapper/gradle-wrapper.jar differ diff --git a/feature-terms/dialog/filter/gradle/wrapper/gradle-wrapper.properties b/feature-terms/dialog/filter/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ae04661 --- /dev/null +++ b/feature-terms/dialog/filter/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/feature-terms/dialog/filter/gradlew b/feature-terms/dialog/filter/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/feature-terms/dialog/filter/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/feature-terms/dialog/filter/gradlew.bat b/feature-terms/dialog/filter/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/feature-terms/dialog/filter/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/di/Module.kt b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/di/Module.kt new file mode 100644 index 0000000..7c76420 --- /dev/null +++ b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/di/Module.kt @@ -0,0 +1,18 @@ +package dialogfilter.di + +import dialogfilter.ui.DefaultTermFilterComponent +import org.koin.dsl.module + +val dialogFilterModule = module { + factory { + DefaultTermFilterComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + propertyRepository = get(), + languageRepository = get(), + flagsRepository = get(), + languageNameRepository = get(), + ) + } +} \ No newline at end of file diff --git a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterViewModel.kt b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/DefaultTermFilterComponent.kt similarity index 81% rename from feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterViewModel.kt rename to feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/DefaultTermFilterComponent.kt index d0f2e8e..ba319c8 100644 --- a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterViewModel.kt +++ b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/DefaultTermFilterComponent.kt @@ -1,37 +1,31 @@ -package ui.dialog.filter - -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import data.LanguageModel -import data.PropertyLevel -import data.PropertyType -import data.SearchCriterion -import data.TermbaseModel -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.updateAndGet -import kotlinx.coroutines.launch -import localized -import repository.FlagsRepository -import repository.LanguageNameRepository -import repository.LanguageRepository -import repository.PropertyRepository -import coroutines.CoroutineDispatcherProvider +package dialogfilter.ui + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider +import data.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel - -class TermFilterViewModel( +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import localized +import repo.FlagsRepository +import repo.LanguageNameRepository +import repo.LanguageRepository +import repo.PropertyRepository +import kotlin.coroutines.CoroutineContext + +internal class DefaultTermFilterComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, private val dispatcherProvider: CoroutineDispatcherProvider, private val propertyRepository: PropertyRepository, private val languageRepository: LanguageRepository, private val flagsRepository: FlagsRepository, private val languageNameRepository: LanguageNameRepository, -) : InstanceKeeper.Instance { +) : TermFilterComponent, ComponentContext by componentContext { private var termbaseId: Int = 0 private var sourceLanguage: LanguageModel? = null @@ -49,46 +43,54 @@ class TermFilterViewModel( private val currentValue = MutableStateFlow("") private val availableValues = MutableStateFlow?>(null) private val _done = MutableSharedFlow>() - val done = _done.asSharedFlow() + override val done = _done.asSharedFlow() private val configurations = MutableStateFlow(mapOf()) - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val propertiesUiState = combine( - filterableItems, selectedItem, configurations - ) { items, selectedItem, configurations -> - TermFilterPropertiesUiState( - items = items, - selectedItem = selectedItem, - configurations = configurations - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = TermFilterPropertiesUiState() - ) - - val uiState = combine( - currentName, - availableMatchTypes, - currentMatchType, - currentValue, - availableValues, - ) { currentName, availableMatchTypes, currentMatchType, currentValue, availableValues -> - TermFilterUiState( - currentName = currentName, - availableMatchTypes = availableMatchTypes, - currentMatchType = currentMatchType, - currentValue = currentValue, - availableValues = availableValues, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = TermFilterUiState(), - ) - - override fun onDestroy() { - viewModelScope.cancel() + private lateinit var viewModelScope: CoroutineScope + + override lateinit var propertiesUiState: StateFlow + override lateinit var uiState: StateFlow + + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + currentName, + availableMatchTypes, + currentMatchType, + currentValue, + availableValues, + ) { currentName, availableMatchTypes, currentMatchType, currentValue, availableValues -> + TermFilterUiState( + currentName = currentName, + availableMatchTypes = availableMatchTypes, + currentMatchType = currentMatchType, + currentValue = currentValue, + availableValues = availableValues, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = TermFilterUiState(), + ) + propertiesUiState = combine( + filterableItems, selectedItem, configurations + ) { items, selectedItem, configurations -> + TermFilterPropertiesUiState( + items = items, + selectedItem = selectedItem, + configurations = configurations + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = TermFilterPropertiesUiState() + ) + } + doOnDestroy { + viewModelScope.cancel() + } + } } private suspend fun loadCriteria(criteria: List) { @@ -203,7 +205,7 @@ class TermFilterViewModel( } } - fun loadInitial(termbase: TermbaseModel, criteria: List, sourceLanguage: LanguageModel?) { + override fun loadInitial(termbase: TermbaseModel, criteria: List, sourceLanguage: LanguageModel?) { termbaseId = termbase.id this.sourceLanguage = sourceLanguage @@ -213,7 +215,7 @@ class TermFilterViewModel( } } - fun toggleSelection(item: FilterableItem) { + override fun toggleSelection(item: FilterableItem) { selectedItem.updateAndGet { old -> if (old != null) { val configuration = FilterConfiguration(currentMatchType.value, currentValue.value) @@ -303,15 +305,15 @@ class TermFilterViewModel( } } - fun setCurrentMatchType(value: MatchType) { + override fun setCurrentMatchType(value: MatchType) { currentMatchType.value = value } - fun setCurrentValue(value: String) { + override fun setCurrentValue(value: String) { currentValue.value = value } - fun clearCurrent() { + override fun clearCurrent() { currentValue.value = "" currentMatchType.value = null selectedItem.value?.also { item -> @@ -326,7 +328,7 @@ class TermFilterViewModel( } } - fun clearAll() { + override fun clearAll() { currentValue.value = "" currentMatchType.value = null configurations.value = mapOf() @@ -345,7 +347,7 @@ class TermFilterViewModel( } } - fun submit() { + override fun submit() { val old = selectedItem.value if (old != null) { val configuration = FilterConfiguration(currentMatchType.value, currentValue.value) diff --git a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/FilterableItem.kt b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/FilterableItem.kt similarity index 95% rename from feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/FilterableItem.kt rename to feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/FilterableItem.kt index d538c2b..0ef633d 100644 --- a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/FilterableItem.kt +++ b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/FilterableItem.kt @@ -1,4 +1,4 @@ -package ui.dialog.filter +package dialogfilter.ui import data.PropertyLevel import data.PropertyModel diff --git a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/MatchType.kt b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/MatchType.kt similarity index 92% rename from feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/MatchType.kt rename to feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/MatchType.kt index 89a1a83..941d874 100644 --- a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/MatchType.kt +++ b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/MatchType.kt @@ -1,4 +1,4 @@ -package ui.dialog.filter +package dialogfilter.ui import localized diff --git a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/PropertyTree.kt b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/PropertyTree.kt similarity index 98% rename from feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/PropertyTree.kt rename to feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/PropertyTree.kt index b5aa280..018f6d7 100644 --- a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/PropertyTree.kt +++ b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/PropertyTree.kt @@ -1,4 +1,4 @@ -package ui.dialog.filter +package dialogfilter.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -20,8 +20,8 @@ import androidx.compose.ui.unit.dp import data.PropertyLevel import data.toReadableString import localized -import ui.components.TreeItem -import ui.theme.Spacing +import common.ui.components.TreeItem +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterComponent.kt b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterComponent.kt new file mode 100644 index 0000000..bad0025 --- /dev/null +++ b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterComponent.kt @@ -0,0 +1,20 @@ +package dialogfilter.ui + +import data.LanguageModel +import data.SearchCriterion +import data.TermbaseModel +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow + +interface TermFilterComponent { + val done: SharedFlow> + val propertiesUiState: StateFlow + val uiState: StateFlow + fun loadInitial(termbase: TermbaseModel, criteria: List, sourceLanguage: LanguageModel?) + fun toggleSelection(item: FilterableItem) + fun setCurrentMatchType(value: MatchType) + fun setCurrentValue(value: String) + fun clearCurrent() + fun clearAll() + fun submit() +} \ No newline at end of file diff --git a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterDialog.kt b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterDialog.kt similarity index 90% rename from feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterDialog.kt rename to feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterDialog.kt index 733d116..2cab20d 100644 --- a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterDialog.kt +++ b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterDialog.kt @@ -1,4 +1,4 @@ -package ui.dialog.filter +package dialogfilter.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -33,36 +33,29 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.rememberWindowState -import com.arkivanov.essenty.instancekeeper.getOrCreate import data.LanguageModel import data.SearchCriterion import data.TermbaseModel import kotlinx.coroutines.launch import localized -import org.koin.java.KoinJavaComponent.inject -import ui.components.CustomSpinner -import ui.components.CustomTextField -import ui.components.CustomTooltipArea -import ui.components.StyledLabel -import ui.theme.Indigo800 -import ui.theme.MetaTermTheme -import ui.theme.Spacing -import utils.AppBusiness +import common.ui.components.CustomSpinner +import common.ui.components.CustomTextField +import common.ui.components.CustomTooltipArea +import common.ui.components.StyledLabel +import common.ui.theme.Indigo800 +import common.ui.theme.MetaTermTheme +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable fun TermFilterDialog( + component: TermFilterComponent, termbase: TermbaseModel, sourceLanguage: LanguageModel?, criteria: List = emptyList(), onConfirm: (List) -> Unit, onClose: () -> Unit, ) { - val viewModel: TermFilterViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: TermFilterViewModel by inject(TermFilterViewModel::class.java) - res - } - MetaTermTheme { Window( title = "dialog_title_filter".localized(), @@ -73,16 +66,16 @@ fun TermFilterDialog( }, ) { LaunchedEffect(termbase) { - viewModel.loadInitial( + component.loadInitial( termbase = termbase, criteria = criteria, sourceLanguage = sourceLanguage, ) } - LaunchedEffect(viewModel) { + LaunchedEffect(component) { launch { - viewModel.done.collect { + component.done.collect { onConfirm(it) } } @@ -106,7 +99,7 @@ fun TermFilterDialog( CustomTooltipArea(text = "tooltip_clear_current_filter".localized()) { Icon( modifier = Modifier.size(20.dp).onClick { - viewModel.clearCurrent() + component.clearCurrent() }, imageVector = Icons.Filled.RemoveCircle, tint = MaterialTheme.colors.primary, @@ -116,7 +109,7 @@ fun TermFilterDialog( CustomTooltipArea(text = "tooltip_clear_all_filters".localized()) { Icon( modifier = Modifier.size(20.dp).onClick { - viewModel.clearAll() + component.clearAll() }, imageVector = Icons.Filled.ClearAll, tint = MaterialTheme.colors.primary, @@ -126,8 +119,8 @@ fun TermFilterDialog( } Spacer(modifier = Modifier.height(Spacing.s)) - val propertiesUiState by viewModel.propertiesUiState.collectAsState() - val uiState by viewModel.uiState.collectAsState() + val propertiesUiState by component.propertiesUiState.collectAsState() + val uiState by component.uiState.collectAsState() Row( modifier = Modifier.fillMaxWidth().weight(1f), @@ -139,7 +132,7 @@ fun TermFilterDialog( configurations = propertiesUiState.configurations, selectedItem = propertiesUiState.selectedItem, onSelectionChanged = { - viewModel.toggleSelection(it) + component.toggleSelection(it) } ) Column( @@ -205,7 +198,7 @@ fun TermFilterDialog( valueColor = if (uiState.currentMatchType == null) Color.Gray else Color.Black, modifier = Modifier.fillMaxWidth().height(20.dp), onValueChanged = { index -> - viewModel.setCurrentMatchType(uiState.availableMatchTypes[index]) + component.setCurrentMatchType(uiState.availableMatchTypes[index]) } ) @@ -219,7 +212,7 @@ fun TermFilterDialog( onValueChanged = { index -> val newValue = uiState.availableValues?.getOrNull(index) if (newValue != null) { - viewModel.setCurrentValue(newValue) + component.setCurrentValue(newValue) } } ) @@ -233,7 +226,7 @@ fun TermFilterDialog( hint = "dialog_filter_header_match_value_placeholder".localized(), value = uiState.currentValue, onValueChange = { - viewModel.setCurrentValue(it) + component.setCurrentValue(it) } ) } @@ -264,7 +257,7 @@ fun TermFilterDialog( modifier = Modifier.heightIn(max = 25.dp), contentPadding = PaddingValues(0.dp), onClick = { - viewModel.submit() + component.submit() }, ) { Text(text = "button_apply".localized(), style = MaterialTheme.typography.button) diff --git a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterUiState.kt b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterUiState.kt similarity index 96% rename from feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterUiState.kt rename to feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterUiState.kt index d8b43c2..b5f1b84 100644 --- a/feature-terms/filter/src/jvmMain/kotlin/ui/dialog/filter/TermFilterUiState.kt +++ b/feature-terms/dialog/filter/src/jvmMain/kotlin/dialogfilter/ui/TermFilterUiState.kt @@ -1,4 +1,4 @@ -package ui.dialog.filter +package dialogfilter.ui data class TermFilterUiState( val currentName: String = "", diff --git a/feature-termbases/definitionmodel/gradle.properties b/feature-terms/dialog/gradle.properties similarity index 98% rename from feature-termbases/definitionmodel/gradle.properties rename to feature-terms/dialog/gradle.properties index 056070a..b70abdd 100644 --- a/feature-termbases/definitionmodel/gradle.properties +++ b/feature-terms/dialog/gradle.properties @@ -1,5 +1,4 @@ kotlin.code.style=official - kotlin.version=1.8.0 agp.version=7.3.0 compose.version=1.3.0 \ No newline at end of file diff --git a/feature-terms/dialog/gradle/wrapper/gradle-wrapper.jar b/feature-terms/dialog/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/feature-terms/dialog/gradle/wrapper/gradle-wrapper.jar differ diff --git a/feature-terms/dialog/gradle/wrapper/gradle-wrapper.properties b/feature-terms/dialog/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ae04661 --- /dev/null +++ b/feature-terms/dialog/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/feature-terms/dialog/gradlew b/feature-terms/dialog/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/feature-terms/dialog/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/feature-terms/dialog/gradlew.bat b/feature-terms/dialog/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/feature-terms/dialog/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/feature-terms/gradle.properties b/feature-terms/gradle.properties index 47c80dc..15a3af6 100644 --- a/feature-terms/gradle.properties +++ b/feature-terms/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.1 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-terms/list/build.gradle.kts b/feature-terms/list/build.gradle.kts index 9f30c59..37b9a75 100644 --- a/feature-terms/list/build.gradle.kts +++ b/feature-terms/list/build.gradle.kts @@ -22,7 +22,6 @@ kotlin { dependencies { implementation(compose.desktop.currentOs) implementation(compose.materialIconsExtended) - implementation(libs.essenty.instancekeeper) implementation(projects.coreCommon) implementation(projects.coreData) diff --git a/feature-terms/list/gradle.properties b/feature-terms/list/gradle.properties index 056070a..ca25374 100644 --- a/feature-terms/list/gradle.properties +++ b/feature-terms/list/gradle.properties @@ -2,4 +2,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.0 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/feature-terms/list/src/jvmMain/kotlin/ui/list/TermCell.kt b/feature-terms/list/src/jvmMain/kotlin/termlist/ui/TermCell.kt similarity index 96% rename from feature-terms/list/src/jvmMain/kotlin/ui/list/TermCell.kt rename to feature-terms/list/src/jvmMain/kotlin/termlist/ui/TermCell.kt index 8a6891a..91da9d0 100644 --- a/feature-terms/list/src/jvmMain/kotlin/ui/list/TermCell.kt +++ b/feature-terms/list/src/jvmMain/kotlin/termlist/ui/TermCell.kt @@ -1,4 +1,4 @@ -package ui.list +package termlist.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -13,7 +13,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import localized -import ui.theme.Spacing +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/feature-terms/list/src/jvmMain/kotlin/ui/list/TermsList.kt b/feature-terms/list/src/jvmMain/kotlin/termlist/ui/TermsList.kt similarity index 98% rename from feature-terms/list/src/jvmMain/kotlin/ui/list/TermsList.kt rename to feature-terms/list/src/jvmMain/kotlin/termlist/ui/TermsList.kt index 3bb7a11..fa1c263 100644 --- a/feature-terms/list/src/jvmMain/kotlin/ui/list/TermsList.kt +++ b/feature-terms/list/src/jvmMain/kotlin/termlist/ui/TermsList.kt @@ -1,4 +1,4 @@ -package ui.list +package termlist.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box diff --git a/feature-terms/src/jvmMain/kotlin/di/TermsModule.kt b/feature-terms/src/jvmMain/kotlin/di/TermsModule.kt deleted file mode 100644 index f9e1fbe..0000000 --- a/feature-terms/src/jvmMain/kotlin/di/TermsModule.kt +++ /dev/null @@ -1,48 +0,0 @@ -package di - -import org.koin.dsl.module -import ui.TermsViewModel -import ui.detail.TermDetailViewModel -import ui.dialog.filter.TermFilterViewModel - - -val termsKoinModule = module { - factory { - TermsViewModel( - dispatcherProvider = get(), - languageRepository = get(), - languageNameRepository = get(), - entryRepository = get(), - deleteEntryUseCase = get(), - searchTermsUseCase = get(), - notificationCenter = get(), - log = get(), - ) - } - factory { - TermDetailViewModel( - dispatcherProvider = get(), - fileManager = get(), - termRepository = get(), - flagsRepository = get(), - entryPropertyValueRepository = get(), - languagePropertyValueRepository = get(), - termPropertyValueRepository = get(), - languageNameRepository = get(), - notificationCenter = get(), - deleteTermUseCase = get(), - propertyRepository = get(), - inputDescriptorRepository = get(), - log = get(), - ) - } - factory { - TermFilterViewModel( - dispatcherProvider = get(), - propertyRepository = get(), - languageRepository = get(), - flagsRepository = get(), - languageNameRepository = get(), - ) - } -} \ No newline at end of file diff --git a/feature-terms/src/jvmMain/kotlin/terms/di/TermsModule.kt b/feature-terms/src/jvmMain/kotlin/terms/di/TermsModule.kt new file mode 100644 index 0000000..6aad799 --- /dev/null +++ b/feature-terms/src/jvmMain/kotlin/terms/di/TermsModule.kt @@ -0,0 +1,27 @@ +package terms.di + +import dialogfilter.di.dialogFilterModule +import org.koin.dsl.module +import termdetail.di.termDetailModule +import terms.ui.DefaultTermsComponent +import terms.ui.TermsComponent + + +val termsModule = module { + includes(termDetailModule, dialogFilterModule) + + factory { + DefaultTermsComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + languageRepository = get(), + languageNameRepository = get(), + entryRepository = get(), + deleteEntryUseCase = get(), + searchTermsUseCase = get(), + notificationCenter = get(), + log = get(), + ) + } +} \ No newline at end of file diff --git a/feature-terms/src/jvmMain/kotlin/terms/ui/DefaultTermsComponent.kt b/feature-terms/src/jvmMain/kotlin/terms/ui/DefaultTermsComponent.kt new file mode 100644 index 0000000..b6a041b --- /dev/null +++ b/feature-terms/src/jvmMain/kotlin/terms/ui/DefaultTermsComponent.kt @@ -0,0 +1,372 @@ +package terms.ui + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.arkivanov.decompose.router.slot.childSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider +import data.EntryModel +import data.LanguageModel +import data.SearchCriterion +import data.TermModel +import data.TermbaseModel +import data.includingSearch +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import common.log.LogManager +import common.notification.NotificationCenter +import common.utils.getByInjection +import dialogfilter.ui.TermFilterComponent +import kotlinx.coroutines.flow.* +import repo.EntryRepository +import repo.LanguageNameRepository +import repo.LanguageRepository +import termdetail.ui.TermDetailComponent +import usecase.DeleteEntryUseCase +import usecase.SearchTermsUseCase +import kotlin.coroutines.CoroutineContext + +internal class DefaultTermsComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, + private val dispatcherProvider: CoroutineDispatcherProvider, + private val languageRepository: LanguageRepository, + private val languageNameRepository: LanguageNameRepository, + private val entryRepository: EntryRepository, + private val notificationCenter: NotificationCenter, + private val deleteEntryUseCase: DeleteEntryUseCase, + private val searchTermsUseCase: SearchTermsUseCase, + private val log: LogManager, +) : TermsComponent, ComponentContext by componentContext { + + private val currentTermbase = MutableStateFlow(null) + private val terms = MutableStateFlow(listOf()) + private val selectedTerm = MutableStateFlow(null) + private val selectedEntry = MutableStateFlow(null) + private val entryEditMode = MutableStateFlow(false) + private val sourceLanguage = MutableStateFlow(null) + private val sourceLanguages = MutableStateFlow>(emptyList()) + private val targetLanguage = MutableStateFlow(null) + private val targetLanguages = MutableStateFlow>(emptyList()) + private val searchText = MutableStateFlow("") + private var searchCriteria = MutableStateFlow>(emptyList()) + private var termsJob: Job? = null + private lateinit var viewModelScope: CoroutineScope + private val termDetailNavigation = SlotNavigation() + private val dialogNavigation = SlotNavigation() + + override lateinit var uiState: StateFlow + override lateinit var searchUiState: StateFlow + override lateinit var toolbarUiState: StateFlow + override val termDetail: Value> = childSlot( + source = termDetailNavigation, + key = "TermsTermDetailSlot", + childFactory = { _, component -> + getByInjection(component, coroutineContext) + } + ) + override val dialog: Value> = childSlot( + source = dialogNavigation, + key = "TermsDialogSlot", + childFactory = { _, component -> + getByInjection(component, coroutineContext) + } + ) + + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + currentTermbase, + terms, + selectedEntry, + entryEditMode, + selectedTerm, + ) { currentTermbase, terms, selectedEntry, entryEditMode, selectedTerm -> + TermsUiState( + currentTermbase = currentTermbase, + terms = terms, + selectedEntry = selectedEntry, + entryEditMode = entryEditMode, + selectedTerm = selectedTerm, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = TermsUiState(), + ) + searchUiState = combine( + searchText, + searchCriteria, + ) { searchText, searchCriteria -> + TermsSearchUiState( + searchText = searchText, + searchCriteria = searchCriteria, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = TermsSearchUiState(), + ) + toolbarUiState = combine( + sourceLanguages, + sourceLanguage, + targetLanguages, + targetLanguage, + + ) { sourceLanguages, sourceLanguage, targetLanguages, targetLanguage -> + TermsToolbarUiState( + sourceLanguages = sourceLanguages, + sourceLanguage = sourceLanguage, + targetLanguages = targetLanguages, + targetLanguage = targetLanguage, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = TermsToolbarUiState(), + ) + + viewModelScope.launch(dispatcherProvider.io) { + launch { + handleCurrentLanguageTermsChanges() + } + launch { + notificationCenter.events.filter { it is NotificationCenter.Event.OpenEntryEditMode }.collect { + if (selectedEntry.value != null) { + setEntryEditMode(true) + } + } + } + launch { + notificationCenter.events.filter { it is NotificationCenter.Event.CloseEntryEditMode }.collect { + if (selectedEntry.value != null) { + setEntryEditMode(false) + } + } + } + launch { + selectedEntry.collect { + entryRepository.setCurrentEntry(it) + } + } + launch { + entryEditMode.collect { + entryRepository.setEditMode(it) + } + } + launch { + notificationCenter.events.filter { it is NotificationCenter.Event.DeleteEntry }.collect { + deleteCurrentEntry() + } + } + launch { + notificationCenter.events.filter { it is NotificationCenter.Event.CreateEntry }.collect { + val termbase = currentTermbase.value ?: return@collect + selectedEntry.value = null + setEntryEditMode(false) + + val termbaseId = termbase.id + val newEntry = EntryModel(termbaseId = termbaseId) + val entryId = entryRepository.create(newEntry) + selectedEntry.value = newEntry.copy(id = entryId, new = true) + selectedTerm.value = TermModel(entryId = entryId) + // needed to wait for the reloadItems to finish in TermDetailViewModel + delay(100) + setEntryEditMode(true) + } + } + } + + termDetailNavigation.activate(TermsComponent.TermDetailConfig) + } + doOnDestroy { + viewModelScope.cancel() + } + } + } + + override fun load(termbase: TermbaseModel) { + currentTermbase.value = termbase + viewModelScope.launch(dispatcherProvider.io) { + loadLanguages(termbaseId = termbase.id) + loadTerms() + } + } + + private suspend fun handleCurrentLanguageTermsChanges() { + notificationCenter.events.filter { it is NotificationCenter.Event.CurrentLanguageTermsEdited }.collect { + loadTerms() + } + } + + override fun changeSourceLanguage(language: LanguageModel) { + sourceLanguage.update { language } + viewModelScope.launch(dispatcherProvider.io) { + loadTerms() + } + } + + override fun changeTargetLanguage(language: LanguageModel) { + targetLanguage.update { language } + } + + override fun switchLanguages() { + val oldTarget = targetLanguage.value + targetLanguage.update { sourceLanguage.value } + sourceLanguage.update { oldTarget } + val oldTargets = targetLanguages.value + targetLanguages.update { sourceLanguages.value } + sourceLanguages.update { oldTargets } + + viewModelScope.launch(dispatcherProvider.io) { + loadTerms() + } + } + + override fun selectTerm(value: TermModel?) { + setEntryEditMode(false) + if (value == null) { + selectedTerm.update { null } + selectedEntry.update { null } + return + } + selectedTerm.update { value } + viewModelScope.launch(dispatcherProvider.io) { + val entryId = value.entryId + val entry = entryRepository.getById(entryId) + selectedEntry.update { entry } + } + } + + override fun sendCreateEntryEvent() { + if (currentTermbase.value == null) { + return + } + val event = NotificationCenter.Event.CreateEntry + notificationCenter.send(event) + } + + override fun deleteCurrentEntry() { + val entry = selectedEntry.value ?: return + viewModelScope.launch(dispatcherProvider.io) { + deleteEntryUseCase(entry) + selectedEntry.update { null } + terms.updateAndGet { oldList -> + // selects new term at the beginning + val newList = oldList.filter { it.entryId != entry.id } + val idxToSelect = (oldList.indexOfFirst { it.entryId == entry.id }).coerceIn(0, newList.size - 1) - 1 + if (idxToSelect >= 0) { + val newTermToSelect = newList[idxToSelect] + val newSelectedEntryId = newTermToSelect.entryId + selectedEntry.value = entryRepository.getById(newSelectedEntryId) + selectedTerm.value = newTermToSelect + } + + newList + } + } + } + + private suspend fun loadLanguages(termbaseId: Int) { + val localLanguages = + languageRepository.getAll(termbaseId).map { it.copy(name = languageNameRepository.getName(it.code)) } + val source = localLanguages.firstOrNull() + val target = (localLanguages - source).firstOrNull() + sourceLanguage.update { source } + sourceLanguages.update { + if (target != null) { + localLanguages - target + } else { + localLanguages + } + } + if (source != null) { + searchCriteria.value = SearchCriterion.getDefault(sourceLang = source.code) + } + targetLanguage.update { target } + targetLanguages.update { + if (source != null) { + localLanguages - source + } else { + localLanguages + } + } + } + + private fun loadTerms() { + termsJob?.cancel() + termsJob = null + terms.update { emptyList() } + val lang = sourceLanguage.value?.code ?: return + val termbase = currentTermbase.value ?: return + + termsJob = viewModelScope.launch(dispatcherProvider.io) { + entryRepository.observeEntries(termbase.id).distinctUntilChanged(areEquivalent = { lhs, rhs -> + lhs.all { e -> rhs.contains(e) } && rhs.all { e -> lhs.contains(e) } + }).onEach { + val terms = searchTermsUseCase( + termbaseId = termbase.id, + mainLang = lang, + criteria = searchCriteria.value.includingSearch( + text = searchText.value.takeIf { e -> e.isNotEmpty() }, + ), + ) + val newTerms = terms.sortedBy { e -> e.lemma } + log.debug("Loaded ${newTerms.count()} terms") + if (selectedEntry.value?.id !in newTerms.map { it.entryId }) { + if (selectedEntry.value?.new == false) { + selectedTerm.value = null + selectedEntry.value = null + } + } else { + selectedTerm.value = newTerms.firstOrNull { it.entryId == selectedEntry.value?.id } + } + this@DefaultTermsComponent.terms.update { + newTerms + } + }.launchIn(this) + } + } + + override fun setEntryEditMode(value: Boolean) { + log.debug("Entering entry edit mode: $value") + entryEditMode.value = value + } + + override fun setSearch(value: String) { + searchText.value = value + } + + override fun setSearchCriteria(value: List) { + searchCriteria.value = value + + // reloads after a search + loadTerms() + } + + override fun searchTerms() { + loadTerms() + } + + override fun openDialog(config: TermsComponent.DialogConfig) { + viewModelScope.launch(dispatcherProvider.main) { + dialogNavigation.activate(config) + } + } + + override fun closeDialog() { + viewModelScope.launch(dispatcherProvider.main) { + dialogNavigation.activate(TermsComponent.DialogConfig.None) + } + } +} diff --git a/feature-terms/src/jvmMain/kotlin/terms/ui/TermsComponent.kt b/feature-terms/src/jvmMain/kotlin/terms/ui/TermsComponent.kt new file mode 100644 index 0000000..be90c91 --- /dev/null +++ b/feature-terms/src/jvmMain/kotlin/terms/ui/TermsComponent.kt @@ -0,0 +1,46 @@ +package terms.ui + +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import data.LanguageModel +import data.SearchCriterion +import data.TermModel +import data.TermbaseModel +import kotlinx.coroutines.flow.StateFlow + +interface TermsComponent { + val uiState: StateFlow + val searchUiState: StateFlow + val toolbarUiState: StateFlow + val termDetail: Value> + val dialog: Value> + + fun load(termbase: TermbaseModel) + fun changeSourceLanguage(language: LanguageModel) + fun changeTargetLanguage(language: LanguageModel) + fun switchLanguages() + fun selectTerm(value: TermModel?) + fun sendCreateEntryEvent() + fun deleteCurrentEntry() + fun setEntryEditMode(value: Boolean) + fun setSearch(value: String) + fun setSearchCriteria(value: List) + fun searchTerms() + + fun openDialog(config: DialogConfig) + fun closeDialog() + + @Parcelize + object TermDetailConfig : Parcelable + + sealed interface DialogConfig : Parcelable { + + @Parcelize + object None : DialogConfig + + @Parcelize + object Filter : DialogConfig + } +} \ No newline at end of file diff --git a/feature-terms/src/jvmMain/kotlin/ui/TermsScreen.kt b/feature-terms/src/jvmMain/kotlin/terms/ui/TermsScreen.kt similarity index 51% rename from feature-terms/src/jvmMain/kotlin/ui/TermsScreen.kt rename to feature-terms/src/jvmMain/kotlin/terms/ui/TermsScreen.kt index 5ee06e4..56a4580 100644 --- a/feature-terms/src/jvmMain/kotlin/ui/TermsScreen.kt +++ b/feature-terms/src/jvmMain/kotlin/terms/ui/TermsScreen.kt @@ -1,17 +1,8 @@ -package ui +package terms.ui import androidx.compose.foundation.focusable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.* import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -21,32 +12,25 @@ import androidx.compose.ui.input.key.KeyEventType.Companion.KeyDown import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type -import com.arkivanov.essenty.instancekeeper.getOrCreate -import org.koin.java.KoinJavaComponent -import ui.detail.TermDetail -import ui.detail.TermDetailViewModel -import ui.dialog.filter.TermFilterDialog -import ui.dialog.filter.TermFilterViewModel -import ui.list.TermsList -import ui.theme.Spacing -import utils.AppBusiness +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import common.ui.theme.Spacing +import dialogfilter.ui.TermFilterComponent +import termdetail.ui.TermDetail +import termdetail.ui.TermDetailComponent +import dialogfilter.ui.TermFilterDialog +import termlist.ui.TermsList @OptIn(ExperimentalComposeUiApi::class) @Composable fun TermsScreen( + component: TermsComponent, modifier: Modifier = Modifier, ) { - val viewModel: TermsViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: TermsViewModel by KoinJavaComponent.inject(TermsViewModel::class.java) - res - } - val termDetailViewModel: TermDetailViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: TermDetailViewModel by KoinJavaComponent.inject(TermDetailViewModel::class.java) - res - } - val uiState by viewModel.uiState.collectAsState() - val toolbarUiState by viewModel.toolbarUiState.collectAsState() - val searchUiState by viewModel.searchUiState.collectAsState() + val uiState by component.uiState.collectAsState() + val toolbarUiState by component.toolbarUiState.collectAsState() + val searchUiState by component.searchUiState.collectAsState() + val termDetail by component.termDetail.subscribeAsState() + val dialog by component.dialog.subscribeAsState() val languagesState by derivedStateOf { buildList { @@ -60,13 +44,6 @@ fun TermsScreen( } } } - LaunchedEffect(languagesState) { - termDetailViewModel.setLanguages(languagesState) - } - - var filterDialogOpen by remember { - mutableStateOf(false) - } Column(modifier = modifier) { TermsToolBar( @@ -78,47 +55,47 @@ fun TermsScreen( entryEditMode = uiState.entryEditMode, currentSearch = searchUiState.searchText, onSourceLanguageChanged = { - viewModel.changeSourceLanguage(it) + component.changeSourceLanguage(it) }, onTargetLanguageChanged = { - viewModel.changeTargetLanguage(it) + component.changeTargetLanguage(it) }, onSwitchLanguages = { - viewModel.switchLanguages() + component.switchLanguages() }, onNewEntry = { - viewModel.sendCreateEntryEvent() + component.sendCreateEntryEvent() }, onDeleteEntry = { - viewModel.deleteCurrentEntry() + component.deleteCurrentEntry() }, onToggleEditMode = { if (uiState.selectedEntry != null) { - viewModel.setEntryEditMode(!uiState.entryEditMode) + component.setEntryEditMode(!uiState.entryEditMode) } }, onSave = { - termDetailViewModel.save() + (termDetail.child?.instance as? TermDetailComponent)?.save() }, onSearchChanged = { - viewModel.setSearch(it) + component.setSearch(it) }, onSearchFired = { - viewModel.searchTerms() + component.searchTerms() }, onOpenFilter = { - filterDialogOpen = true + component.openDialog(TermsComponent.DialogConfig.Filter) }, ) - Row(modifier = Modifier.padding(start = Spacing.s, bottom = Spacing.xxs, top = Spacing.xxs)) { + Row(modifier = Modifier.padding(start = Spacing.s, bottom = Spacing.xxs, top = Spacing.xxs).fillMaxSize()) { val termsFocusRequester = remember { FocusRequester() } TermsList( - modifier = Modifier.weight(0.25f).onPreviewKeyEvent { + modifier = Modifier.weight(0.25f).fillMaxHeight().onPreviewKeyEvent { when { it.type == KeyDown && it.key == Key.DirectionUp -> { val index = uiState.terms.indexOfFirst { t -> t.id == uiState.selectedTerm?.id } if (index > 0) { - viewModel.selectTerm(uiState.terms[index - 1]) + component.selectTerm(uiState.terms[index - 1]) } true } @@ -126,7 +103,7 @@ fun TermsScreen( it.type == KeyDown && it.key == Key.DirectionDown -> { val index = uiState.terms.indexOfFirst { t -> t.id == uiState.selectedTerm?.id } if (index < uiState.terms.count() - 1) { - viewModel.selectTerm(uiState.terms[index + 1]) + component.selectTerm(uiState.terms[index + 1]) } true } @@ -142,39 +119,56 @@ fun TermsScreen( current = uiState.selectedTerm, onSelected = { if (it.id == uiState.selectedTerm?.id) { - viewModel.selectTerm(null) + component.selectTerm(null) termsFocusRequester.freeFocus() } else { - viewModel.selectTerm(it) + component.selectTerm(it) termsFocusRequester.requestFocus() } }, ) - TermDetail( - modifier = Modifier.weight(1f), - entry = uiState.selectedEntry, - editMode = uiState.entryEditMode, - searchCriteria = searchUiState.searchCriteria, - ) + when (termDetail.child?.configuration) { + TermsComponent.TermDetailConfig -> { + val childComponent = termDetail.child?.instance as TermDetailComponent + LaunchedEffect(childComponent, languagesState) { + childComponent.setLanguages(languagesState) + } + TermDetail( + component = childComponent, + modifier = Modifier.weight(1f).fillMaxHeight(), + entry = uiState.selectedEntry, + editMode = uiState.entryEditMode, + searchCriteria = searchUiState.searchCriteria, + ) + } + + else -> Unit + } } } - if (filterDialogOpen) { - val termbase = uiState.currentTermbase - if (termbase != null) { - TermFilterDialog( - termbase = termbase, - sourceLanguage = toolbarUiState.sourceLanguage, - criteria = searchUiState.searchCriteria, - onConfirm = { criteria -> - viewModel.setSearchCriteria(criteria) - filterDialogOpen = false - }, - onClose = { - filterDialogOpen = false - }, - ) + when (dialog.child?.configuration) { + TermsComponent.DialogConfig.Filter -> { + val childComponent = dialog.child?.instance as TermFilterComponent + val termbase = uiState.currentTermbase + if (termbase != null) { + TermFilterDialog( + component = childComponent, + termbase = termbase, + sourceLanguage = toolbarUiState.sourceLanguage, + criteria = searchUiState.searchCriteria, + onConfirm = { criteria -> + component.setSearchCriteria(criteria) + component.closeDialog() + }, + onClose = { + component.closeDialog() + }, + ) + } } + + else -> Unit } } diff --git a/feature-terms/src/jvmMain/kotlin/ui/TermsToolBar.kt b/feature-terms/src/jvmMain/kotlin/terms/ui/TermsToolBar.kt similarity index 97% rename from feature-terms/src/jvmMain/kotlin/ui/TermsToolBar.kt rename to feature-terms/src/jvmMain/kotlin/terms/ui/TermsToolBar.kt index 4e15507..bea218e 100644 --- a/feature-terms/src/jvmMain/kotlin/ui/TermsToolBar.kt +++ b/feature-terms/src/jvmMain/kotlin/terms/ui/TermsToolBar.kt @@ -1,4 +1,4 @@ -package ui +package terms.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Arrangement @@ -35,10 +35,10 @@ import androidx.compose.ui.input.key.type import androidx.compose.ui.unit.dp import data.LanguageModel import localized -import ui.components.CustomSpinner -import ui.components.CustomTextField -import ui.components.CustomTooltipArea -import ui.theme.Spacing +import common.ui.components.CustomSpinner +import common.ui.components.CustomTextField +import common.ui.components.CustomTooltipArea +import common.ui.theme.Spacing @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) @Composable diff --git a/feature-terms/src/jvmMain/kotlin/ui/TermsUiState.kt b/feature-terms/src/jvmMain/kotlin/terms/ui/TermsUiState.kt similarity index 97% rename from feature-terms/src/jvmMain/kotlin/ui/TermsUiState.kt rename to feature-terms/src/jvmMain/kotlin/terms/ui/TermsUiState.kt index 562f801..abe3b94 100644 --- a/feature-terms/src/jvmMain/kotlin/ui/TermsUiState.kt +++ b/feature-terms/src/jvmMain/kotlin/terms/ui/TermsUiState.kt @@ -1,4 +1,4 @@ -package ui +package terms.ui import data.EntryModel import data.LanguageModel diff --git a/feature-terms/src/jvmMain/kotlin/ui/TermsViewModel.kt b/feature-terms/src/jvmMain/kotlin/ui/TermsViewModel.kt deleted file mode 100644 index 74b0415..0000000 --- a/feature-terms/src/jvmMain/kotlin/ui/TermsViewModel.kt +++ /dev/null @@ -1,332 +0,0 @@ -package ui - -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider -import data.EntryModel -import data.LanguageModel -import data.SearchCriterion -import data.TermModel -import data.TermbaseModel -import data.includingSearch -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.updateAndGet -import kotlinx.coroutines.launch -import log.LogManager -import notification.NotificationCenter -import repository.EntryRepository -import repository.LanguageNameRepository -import repository.LanguageRepository -import usecase.DeleteEntryUseCase -import usecase.SearchTermsUseCase - -class TermsViewModel( - private val dispatcherProvider: CoroutineDispatcherProvider, - private val languageRepository: LanguageRepository, - private val languageNameRepository: LanguageNameRepository, - private val entryRepository: EntryRepository, - private val notificationCenter: NotificationCenter, - private val deleteEntryUseCase: DeleteEntryUseCase, - private val searchTermsUseCase: SearchTermsUseCase, - private val log: LogManager, -) : InstanceKeeper.Instance { - - private val currentTermbase = MutableStateFlow(null) - private val terms = MutableStateFlow(listOf()) - private val selectedTerm = MutableStateFlow(null) - private val selectedEntry = MutableStateFlow(null) - private val entryEditMode = MutableStateFlow(false) - private val sourceLanguage = MutableStateFlow(null) - private val sourceLanguages = MutableStateFlow>(emptyList()) - private val targetLanguage = MutableStateFlow(null) - private val targetLanguages = MutableStateFlow>(emptyList()) - private val searchText = MutableStateFlow("") - private var searchCriteria = MutableStateFlow>(emptyList()) - private var termsJob: Job? = null - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val uiState = combine( - currentTermbase, - terms, - selectedEntry, - entryEditMode, - selectedTerm, - ) { currentTermbase, terms, selectedEntry, entryEditMode, selectedTerm -> - TermsUiState( - currentTermbase = currentTermbase, - terms = terms, - selectedEntry = selectedEntry, - entryEditMode = entryEditMode, - selectedTerm = selectedTerm, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = TermsUiState(), - ) - - val searchUiState = combine( - searchText, - searchCriteria, - ) { searchText, searchCriteria -> - TermsSearchUiState( - searchText = searchText, - searchCriteria = searchCriteria, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = TermsSearchUiState(), - ) - - val toolbarUiState = combine( - sourceLanguages, - sourceLanguage, - targetLanguages, - targetLanguage, - - ) { sourceLanguages, sourceLanguage, targetLanguages, targetLanguage -> - TermsToolbarUiState( - sourceLanguages = sourceLanguages, - sourceLanguage = sourceLanguage, - targetLanguages = targetLanguages, - targetLanguage = targetLanguage, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = TermsToolbarUiState(), - ) - - init { - viewModelScope.launch(dispatcherProvider.io) { - launch { - handleCurrentLanguageTermsChanges() - } - launch { - notificationCenter.events.filter { it is NotificationCenter.Event.OpenEntryEditMode }.collect { - if (selectedEntry.value != null) { - setEntryEditMode(true) - } - } - } - launch { - notificationCenter.events.filter { it is NotificationCenter.Event.CloseEntryEditMode }.collect { - if (selectedEntry.value != null) { - setEntryEditMode(false) - } - } - } - launch { - selectedEntry.collect { - entryRepository.setCurrentEntry(it) - } - } - launch { - entryEditMode.collect { - entryRepository.setEditMode(it) - } - } - launch { - notificationCenter.events.filter { it is NotificationCenter.Event.DeleteEntry }.collect { - deleteCurrentEntry() - } - } - launch { - notificationCenter.events.filter { it is NotificationCenter.Event.CreateEntry }.collect { - val termbase = currentTermbase.value ?: return@collect - selectedEntry.value = null - setEntryEditMode(false) - - val termbaseId = termbase.id - val newEntry = EntryModel(termbaseId = termbaseId) - val entryId = entryRepository.create(newEntry) - selectedEntry.value = newEntry.copy(id = entryId, new = true) - selectedTerm.value = TermModel(entryId = entryId) - // needed to wait for the reloadItems to finish in TermDetailViewModel - delay(100) - setEntryEditMode(true) - } - } - } - } - - override fun onDestroy() { - viewModelScope.cancel() - } - - fun load(termbase: TermbaseModel) { - currentTermbase.value = termbase - viewModelScope.launch(dispatcherProvider.io) { - loadLanguages(termbaseId = termbase.id) - loadTerms() - } - } - - private suspend fun handleCurrentLanguageTermsChanges() { - notificationCenter.events.filter { it is NotificationCenter.Event.CurrentLanguageTermsEdited }.collect { - loadTerms() - } - } - - fun changeSourceLanguage(language: LanguageModel) { - sourceLanguage.update { language } - viewModelScope.launch(dispatcherProvider.io) { - loadTerms() - } - } - - fun changeTargetLanguage(language: LanguageModel) { - targetLanguage.update { language } - } - - fun switchLanguages() { - val oldTarget = targetLanguage.value - targetLanguage.update { sourceLanguage.value } - sourceLanguage.update { oldTarget } - val oldTargets = targetLanguages.value - targetLanguages.update { sourceLanguages.value } - sourceLanguages.update { oldTargets } - - viewModelScope.launch(dispatcherProvider.io) { - loadTerms() - } - } - - fun selectTerm(value: TermModel?) { - setEntryEditMode(false) - if (value == null) { - selectedTerm.update { null } - selectedEntry.update { null } - return - } - selectedTerm.update { value } - viewModelScope.launch(dispatcherProvider.io) { - val entryId = value.entryId - val entry = entryRepository.getById(entryId) - selectedEntry.update { entry } - } - } - - fun sendCreateEntryEvent() { - if (currentTermbase.value == null) { - return - } - val event = NotificationCenter.Event.CreateEntry - notificationCenter.send(event) - } - - fun deleteCurrentEntry() { - val entry = selectedEntry.value ?: return - viewModelScope.launch(dispatcherProvider.io) { - deleteEntryUseCase(entry) - selectedEntry.update { null } - terms.updateAndGet { oldList -> - // selects new term at the beginning - val newList = oldList.filter { it.entryId != entry.id } - val idxToSelect = (oldList.indexOfFirst { it.entryId == entry.id }).coerceIn(0, newList.size - 1) - 1 - if (idxToSelect >= 0) { - val newTermToSelect = newList[idxToSelect] - val newSelectedEntryId = newTermToSelect.entryId - selectedEntry.value = entryRepository.getById(newSelectedEntryId) - selectedTerm.value = newTermToSelect - } - - newList - } - } - } - - private suspend fun loadLanguages(termbaseId: Int) { - val localLanguages = - languageRepository.getAll(termbaseId).map { it.copy(name = languageNameRepository.getName(it.code)) } - val source = localLanguages.firstOrNull() - val target = (localLanguages - source).firstOrNull() - sourceLanguage.update { source } - sourceLanguages.update { - if (target != null) { - localLanguages - target - } else { - localLanguages - } - } - if (source != null) { - searchCriteria.value = SearchCriterion.getDefault(sourceLang = source.code) - } - targetLanguage.update { target } - targetLanguages.update { - if (source != null) { - localLanguages - source - } else { - localLanguages - } - } - } - - private fun loadTerms() { - termsJob?.cancel() - termsJob = null - terms.update { emptyList() } - val lang = sourceLanguage.value?.code ?: return - val termbase = currentTermbase.value ?: return - - termsJob = viewModelScope.launch(dispatcherProvider.io) { - entryRepository.observeEntries(termbase.id).distinctUntilChanged(areEquivalent = { lhs, rhs -> - lhs.all { e -> rhs.contains(e) } && rhs.all { e -> lhs.contains(e) } - }).onEach { - val terms = searchTermsUseCase( - termbaseId = termbase.id, - mainLang = lang, - criteria = searchCriteria.value.includingSearch( - text = searchText.value.takeIf { e -> e.isNotEmpty() }, - ), - ) - val newTerms = terms.sortedBy { e -> e.lemma } - log.debug("Loaded ${newTerms.count()} terms") - if (selectedEntry.value?.id !in newTerms.map { it.entryId }) { - if (selectedEntry.value?.new == false) { - selectedTerm.value = null - selectedEntry.value = null - } - } else { - selectedTerm.value = newTerms.firstOrNull { it.entryId == selectedEntry.value?.id } - } - this@TermsViewModel.terms.update { - newTerms - } - }.launchIn(this) - } - } - - fun setEntryEditMode(value: Boolean) { - log.debug("Entering entry edit mode: $value") - entryEditMode.value = value - } - - fun setSearch(value: String) { - searchText.value = value - } - - fun setSearchCriteria(value: List) { - searchCriteria.value = value - - // reloads after a search - loadTerms() - } - - fun searchTerms() { - loadTerms() - } -} diff --git a/gradle.properties b/gradle.properties index 47c80dc..15a3af6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.version=1.8.0 agp.version=7.3.0 -compose.version=1.3.1 \ No newline at end of file +compose.version=1.4.0 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5e1669c..8e591dc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,10 @@ [versions] androidx_datastore = "1.0.0" +decompose = "2.0.0-alpha-02" exposed = "0.41.1" h2 = "2.1.214" koin = "3.2.0" -kotlinx_coroutines = "1.6.4" +kotlinx_coroutines = "1.7.1" kotlinx_serialization = "1.5.0" log4j = "2.20.0" essenty = "1.1.0" @@ -18,12 +19,18 @@ buildNumber = "2" koin = { module = "io.insert-koin:koin-core", version.ref = "koin" } koin_test = { module = "io.insert-koin:koin-test", version.ref = "koin" } -# Essenty +# Decompose +decompose = { module = "com.arkivanov.decompose:decompose", version.ref = "decompose" } +decompose_extensions = { module = "com.arkivanov.decompose:extensions-compose-jetbrains", version.ref = "decompose" } essenty_instancekeeper = { module = "com.arkivanov.essenty:instance-keeper", version.ref = "essenty" } # AndroidX androidx_datastore = { module = "androidx.datastore:datastore-preferences-core", version.ref = "androidx_datastore" } +# coroutines +kotlinx_coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx_coroutines" } +kotlinx_coroutines_test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx_coroutines" } + # persistence h2database = { module = "com.h2database:h2", version.ref = "h2" } exposed_core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" } @@ -40,8 +47,5 @@ log4j_core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "lo log4j_impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" } sl4j_api = { module = "org.slf4j:slf4j-api", version.ref = "sl4j" } -# tests -kotlinx_coroutines_test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx_coroutines" } - [bundles] log4j = ["sl4j.api", "log4j.api", "log4j.core", "log4j.impl"] diff --git a/settings.gradle.kts b/settings.gradle.kts index f1f3b51..e00c8f7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,16 +30,16 @@ include( ":feature-base:main", ":feature-terms", ":feature-terms:detail", - ":feature-terms:filter", + ":feature-terms:dialog:filter", ":feature-terms:list", ":feature-termbases", - ":feature-termbases:statistics", - ":feature-termbases:management", - ":feature-termbases:metadata", - ":feature-termbases:definitionmodel", - ":feature-termbases:inputmodel", - ":feature-termbases:create", - ":feature-termbases:edit", + ":feature-termbases:dialog:statistics", + ":feature-termbases:dialog:management", + ":feature-termbases:dialog:create", + ":feature-termbases:dialog:edit", + ":feature-termbases:wizard:metadata", + ":feature-termbases:wizard:definitionmodel", + ":feature-termbases:wizard:inputmodel", ":core-common:tests", ":core-localization:tests", diff --git a/src/jvmMain/kotlin/AppViewModel.kt b/src/jvmMain/kotlin/AppViewModel.kt deleted file mode 100644 index bef62fa..0000000 --- a/src/jvmMain/kotlin/AppViewModel.kt +++ /dev/null @@ -1,212 +0,0 @@ -import com.arkivanov.essenty.instancekeeper.InstanceKeeper -import coroutines.CoroutineDispatcherProvider -import data.TermbaseModel -import keystore.TemporaryKeyStore -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.getAndUpdate -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.updateAndGet -import kotlinx.coroutines.launch -import log.LogManager -import notification.NotificationCenter -import repository.EntryRepository -import repository.PropertyRepository -import repository.TermbaseRepository -import usecase.ExportCsvUseCase -import usecase.ExportTbxUseCase -import usecase.ImportCsvUseCase -import usecase.ImportTbxUseCase -import java.io.File -import java.lang.Integer.min - -class AppViewModel( - private val dispatcherProvider: CoroutineDispatcherProvider, - private val termbaseRepository: TermbaseRepository, - entryRepository: EntryRepository, - propertyRepository: PropertyRepository, - private val exportTbxUseCase: ExportTbxUseCase, - private val exportCsvUseCase: ExportCsvUseCase, - private val importCsvUseCase: ImportCsvUseCase, - private val importTbxUseCase: ImportTbxUseCase, - private val notificationCenter: NotificationCenter, - private val temporaryKeystore: TemporaryKeyStore, - private val log: LogManager, -) : InstanceKeeper.Instance { - - companion object { - private const val LAST_OPENED_TERMBASE_ID_KEY = "lastOpenTermbaseId" - } - - var shouldOpenNewTermbaseOnDialogClose: Boolean = false - var termbaseToEdit: TermbaseModel? = null - - private val currentTermbase = MutableStateFlow(null) - private val openedTermbases = MutableStateFlow>(emptyList()) - private val viewModelScope = CoroutineScope(SupervisorJob()) - - val uiState = combine( - currentTermbase, - entryRepository.currentEntry, - openedTermbases, - entryRepository.editMode, - propertyRepository.editMode, - ) { currentTermbase, currentEntry, availableTermbases, entryEditMode, definitionModelEditMode -> - AppUiState( - currentTermbase = currentTermbase, - currentEntry = currentEntry, - openedTermbases = availableTermbases, - entryEditMode = entryEditMode, - definitionModelEditMode = definitionModelEditMode, - ) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = AppUiState(), - ) - - init { - viewModelScope.launch(dispatcherProvider.io) { - val lastOpenedTb = temporaryKeystore.get(LAST_OPENED_TERMBASE_ID_KEY, -1) - if (lastOpenedTb >= 0) { - openTermbase(lastOpenedTb) - } - - launch { - notificationCenter.events.filter { it is NotificationCenter.Event.OpenTermbase }.collect { - val termbaseId = (it as NotificationCenter.Event.OpenTermbase).termbaseId - temporaryKeystore.save(LAST_OPENED_TERMBASE_ID_KEY, termbaseId) - openTermbase(termbaseId) - } - } - - launch { - termbaseRepository.all.collect { newTermbasesFromDb -> - openedTermbases.updateAndGet { oldList -> - val newList = mutableListOf() - for (tb in oldList) { - val newTb = newTermbasesFromDb.firstOrNull { e -> e.id == tb.id } - newList += if (newTb != null && tb.name != newTb.name) { - tb.copy(name = newTb.name) - } else { - tb - } - } - newList - } - } - } - } - } - - override fun onDestroy() { - viewModelScope.cancel() - } - - private suspend fun openTermbase(termbaseId: Int) { - val termbase = termbaseRepository.getById(termbaseId) - if (termbase != null) { - log.debug("Opening ${termbase.name}") - openedTermbases.getAndUpdate { termbases -> - if (termbases.isEmpty()) { - setCurrentTermbase(termbase) - } - if (termbases.none { e -> e.id == termbaseId }) { - termbases + termbase - } else { - termbases - } - } - } - } - - fun closeTermbase(termbase: TermbaseModel) { - openedTermbases.getAndUpdate { termbases -> - val oldIndex = termbases.indexOfFirst { it.id == termbase.id } - val res = termbases.filter { it.id != termbase.id } - if (currentTermbase.value?.id == termbase.id) { - if (res.isNotEmpty()) { - val newIndex = min(oldIndex, res.size - 1) - setCurrentTermbase(res[newIndex]) - } else { - setCurrentTermbase(null) - } - } - res - } - } - - fun setCurrentTermbase(termbase: TermbaseModel?) { - currentTermbase.value = termbase - viewModelScope.launch(dispatcherProvider.io) { - temporaryKeystore.save(LAST_OPENED_TERMBASE_ID_KEY, termbase?.id ?: -1) - } - } - - fun exportTbx(path: String) { - val termbase = currentTermbase.value ?: return - viewModelScope.launch(dispatcherProvider.io) { - runCatching { - val destination = File(path) - destination.createNewFile() - - exportTbxUseCase( - termbase = termbase, - destination = destination, - ) - } - } - } - - fun exportCsv(path: String) { - val termbase = currentTermbase.value ?: return - viewModelScope.launch(dispatcherProvider.io) { - runCatching { - val destination = File(path) - destination.createNewFile() - - exportCsvUseCase( - termbase = termbase, - destination = destination, - ) - } - } - } - - fun importCsv(path: String) { - val termbase = currentTermbase.value ?: return - viewModelScope.launch(dispatcherProvider.io) { - try { - val source = File(path) - - importCsvUseCase( - termbase = termbase, - source = source, - ) - } catch (e: Throwable) { - e.printStackTrace() - } - } - } - - fun importTbx(path: String) { - val termbase = currentTermbase.value ?: return - viewModelScope.launch(dispatcherProvider.io) { - try { - val source = File(path) - - importTbxUseCase( - termbase = termbase, - source = source, - ) - } catch (e: Throwable) { - e.printStackTrace() - } - } - } -} diff --git a/src/jvmMain/kotlin/Main.kt b/src/jvmMain/kotlin/Main.kt new file mode 100644 index 0000000..ea2693d --- /dev/null +++ b/src/jvmMain/kotlin/Main.kt @@ -0,0 +1,425 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyShortcut +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.MenuBar +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import androidx.compose.ui.window.rememberWindowState +import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle.LifecycleController +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.essenty.lifecycle.LifecycleRegistry +import common.di.commonModule +import data.ExportType +import dialogcreate.ui.CreateTermbaseComponent +import dialogcreate.ui.CreateTermbaseWizardDialog +import dialogedit.ui.EditTermbaseComponent +import dialogedit.ui.EditTermbaseDialog +import dialogmanage.ui.ManageTermbasesComponent +import dialogmanage.ui.ManageTermbasesDialog +import dialogstatistics.ui.TermbaseStatisticsComponent +import dialogstatistics.ui.TermbaseStatisticsDialog +import intro.ui.IntroComponent +import intro.ui.IntroScreen +import common.keystore.TemporaryKeyStore +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.runBlocking +import common.log.LogManager +import main.ui.MainComponent +import main.ui.MainScreen +import common.notification.NotificationCenter +import org.koin.core.context.startKoin +import org.koin.java.KoinJavaComponent.inject +import persistence.di.databaseModule +import repository.di.repositoryModule +import root.di.rootModule +import termbases.di.termbasesModule +import terms.di.termsModule +import ui.RootComponent +import common.ui.components.CustomOpenFileDialog +import common.ui.components.CustomSaveFileDialog +import common.ui.components.CustomTabBar +import common.ui.theme.MetaTermTheme +import common.ui.theme.SelectedBackground +import common.ui.theme.Spacing +import common.utils.getByInjection +import common.utils.runOnUiThread +import java.util.* + +fun initKoin() { + startKoin { + modules( + commonModule, + databaseModule, + repositoryModule, + + rootModule, + termbasesModule, + termsModule, + ) + } +} + +@OptIn(ExperimentalDecomposeApi::class, ExperimentalComposeUiApi::class) +fun main() { + // init DI + initKoin() + + val log: LogManager = getByInjection() + + log.debug("App initialized") + Thread.setDefaultUncaughtExceptionHandler { t, e -> + log.exception("Exception in ${t.name}", cause = e) + } + // init root component in the main thread outside the application lifecycle + val lifecycle = LifecycleRegistry() + val mainScope = CoroutineScope(SupervisorJob()) + val rootComponent = runOnUiThread { + getByInjection( + DefaultComponentContext(lifecycle = lifecycle), + mainScope.coroutineContext, + ) + } + + // init l10n + runBlocking { + val keystore: TemporaryKeyStore = getByInjection() + val systemLanguage = Locale.getDefault().language + val lang = keystore.get("lang", "") + L10n.setLanguage(lang.ifEmpty { systemLanguage }) + if (lang.isEmpty()) { + keystore.save("lang", "lang".localized()) + } + } + + application { + log.debug("Application starting") + + // ties component lifecycle to the window + val windowState = rememberWindowState() + LifecycleController(lifecycle, windowState) + + Window( + onCloseRequest = ::exitApplication, + title = "app_name".localized(), + state = rememberWindowState(size = DpSize.Unspecified), + ) { + val uiState by rootComponent.uiState.collectAsState() + val dialog by rootComponent.dialog.subscribeAsState() + + val notificationCenter: NotificationCenter by inject(NotificationCenter::class.java) + + MenuBar { + Menu("menu_termbase".localized()) { + Item( + text = "menu_termbase_new".localized(), + shortcut = KeyShortcut(key = Key.N, meta = true), + ) { + rootComponent.shouldOpenNewTermbaseOnDialogClose = true + rootComponent.openDialog(RootComponent.DialogConfig.NewTermbase) + } + Item( + text = "menu_termbase_edit".localized(), + shortcut = KeyShortcut(key = Key.D, meta = true), + enabled = uiState.currentTermbase != null, + ) { + rootComponent.termbaseToEdit = uiState.currentTermbase + rootComponent.openDialog(RootComponent.DialogConfig.EditTermbase) + } + Item( + text = "menu_termbase_manage".localized(), + shortcut = KeyShortcut(key = Key.O, meta = true), + ) { + rootComponent.openDialog(RootComponent.DialogConfig.ManageTermbases) + } + Separator() + Item( + text = "menu_termbase_settings".localized(), + shortcut = KeyShortcut(key = Key.Comma, meta = true), + ) { + rootComponent.openDialog(RootComponent.DialogConfig.Settings) + } + Item( + text = "menu_termbase_statistics".localized(), + shortcut = KeyShortcut(key = Key.Period, meta = true), + enabled = uiState.currentTermbase != null, + ) { + rootComponent.openDialog(RootComponent.DialogConfig.Statistics) + } + Separator() + Menu("menu_termbase_import".localized()) { + Item( + text = "menu_termbase_import_csv".localized(), + enabled = uiState.currentTermbase != null, + ) { + rootComponent.openDialog(RootComponent.DialogConfig.Import(ExportType.CSV)) + } + Item( + text = "menu_termbase_import_tbx".localized(), + enabled = uiState.currentTermbase != null, + ) { + rootComponent.openDialog(RootComponent.DialogConfig.Import(ExportType.TBX)) + } + } + Menu("menu_termbase_export".localized()) { + Item( + text = "menu_termbase_export_csv".localized(), + enabled = uiState.currentTermbase != null, + ) { + rootComponent.openDialog(RootComponent.DialogConfig.Export(ExportType.CSV)) + } + Item( + text = "menu_termbase_export_tbx".localized(), + enabled = uiState.currentTermbase != null, + ) { + rootComponent.openDialog(RootComponent.DialogConfig.Export(ExportType.TBX)) + } + } + Separator() + Item( + text = "button_close".localized(), + shortcut = KeyShortcut(key = Key.W, meta = true), + enabled = uiState.currentTermbase != null, + ) { + exitApplication() + } + } + Menu( + text = "menu_entry".localized(), + ) { + Item( + text = "menu_entry_new".localized(), + enabled = uiState.currentTermbase != null, + shortcut = KeyShortcut(key = Key.Plus, meta = true), + ) { + notificationCenter.send(NotificationCenter.Event.CreateEntry) + } + if (uiState.entryEditMode) { + Item( + text = "menu_entry_close_edit".localized(), + enabled = uiState.currentTermbase != null && uiState.currentEntry != null, + shortcut = KeyShortcut(key = Key.E, meta = true), + ) { + notificationCenter.send(NotificationCenter.Event.CloseEntryEditMode) + } + } else { + Item( + text = "menu_entry_edit".localized(), + enabled = uiState.currentTermbase != null && uiState.currentEntry != null, + shortcut = KeyShortcut(key = Key.E, meta = true), + ) { + notificationCenter.send(NotificationCenter.Event.OpenEntryEditMode) + } + } + Item( + text = "menu_entry_save".localized(), + enabled = uiState.currentTermbase != null && uiState.currentEntry != null && uiState.entryEditMode, + shortcut = KeyShortcut(key = Key.S, meta = true), + ) { + notificationCenter.send(NotificationCenter.Event.SaveEntry) + } + Item( + text = "menu_entry_delete".localized(), + enabled = uiState.currentTermbase != null && uiState.currentEntry != null, + shortcut = KeyShortcut(key = Key.Backspace, meta = true), + ) { + notificationCenter.send(NotificationCenter.Event.DeleteEntry) + } + } + } + + App(rootComponent = rootComponent) + + when (val dialogConfig = dialog.child?.configuration) { + RootComponent.DialogConfig.NewTermbase -> { + val childComponent = dialog.child?.instance as CreateTermbaseComponent + CreateTermbaseWizardDialog( + component = childComponent, + openNewlyCreated = rootComponent.shouldOpenNewTermbaseOnDialogClose, + onClose = { + rootComponent.closeDialog() + rootComponent.shouldOpenNewTermbaseOnDialogClose = false + }, + ) + } + + RootComponent.DialogConfig.EditTermbase -> { + val termbase = rootComponent.termbaseToEdit + val childComponent = dialog.child?.instance as EditTermbaseComponent + if (termbase != null) { + EditTermbaseDialog( + component = childComponent, + initialTermbase = termbase, + onClose = { + rootComponent.termbaseToEdit = null + rootComponent.closeDialog() + }, + ) + } + } + + RootComponent.DialogConfig.ManageTermbases -> { + val childComponent = dialog.child?.instance as ManageTermbasesComponent + ManageTermbasesDialog( + component = childComponent, + onNew = { + rootComponent.openDialog(RootComponent.DialogConfig.NewTermbase) + }, + onClose = { + rootComponent.closeDialog() + }, + onEdit = { + rootComponent.termbaseToEdit = it + rootComponent.openDialog(RootComponent.DialogConfig.EditTermbase) + }, + ) + } + + is RootComponent.DialogConfig.Export -> { + when (dialogConfig.type) { + ExportType.CSV -> { + CustomSaveFileDialog( + title = "dialog_title_export".localized(), + initialFileName = "termbase.csv", + nameFilter = { it.endsWith(".csv") }, + onCloseRequest = { dest -> + if (!dest.isNullOrEmpty()) { + rootComponent.exportCsv(dest) + } + rootComponent.closeDialog() + }, + ) + } + + ExportType.TBX -> { + CustomSaveFileDialog( + title = "dialog_title_export".localized(), + initialFileName = "termbase.tbx", + nameFilter = { it.endsWith(".tbx") }, + onCloseRequest = { dest -> + if (!dest.isNullOrEmpty()) { + rootComponent.exportTbx(dest) + } + rootComponent.closeDialog() + }, + ) + } + } + } + + is RootComponent.DialogConfig.Import -> { + when (dialogConfig.type) { + ExportType.CSV -> { + CustomOpenFileDialog( + title = "dialog_title_import".localized(), + nameFilter = { it.endsWith(".csv") }, + onCloseRequest = { path -> + if (!path.isNullOrEmpty()) { + rootComponent.importCsv(path) + } + rootComponent.closeDialog() + }, + ) + } + + ExportType.TBX -> { + CustomOpenFileDialog( + title = "dialog_title_import".localized(), + nameFilter = { it.endsWith(".tbx") }, + onCloseRequest = { path -> + if (!path.isNullOrEmpty()) { + rootComponent.importTbx(path) + } + rootComponent.closeDialog() + }, + ) + } + } + } + + RootComponent.DialogConfig.Statistics -> { + val childComponent = dialog.child?.instance as TermbaseStatisticsComponent + uiState.currentTermbase?.also { + TermbaseStatisticsDialog( + component = childComponent, + termbase = it, + onClose = { + rootComponent.closeDialog() + }, + ) + } + } + + else -> Unit + } + } + } +} + +@Composable +private fun App(rootComponent: RootComponent) { + MetaTermTheme { + val uiState by rootComponent.uiState.collectAsState() + val openedTermbases = uiState.openedTermbases + val termbase = uiState.currentTermbase + val currentIndex = openedTermbases.indexOfFirst { it.id == termbase?.id }.takeIf { it >= 0 } + val mainConfig by rootComponent.main.subscribeAsState() + Column( + modifier = Modifier.background( + color = MaterialTheme.colors.background, + ).padding(top = Spacing.xs).fillMaxSize(), + ) { + CustomTabBar( + modifier = Modifier.fillMaxWidth(), + tabs = openedTermbases.map { it.name }, + current = currentIndex, + onTabSelected = { + val tb = openedTermbases[it] + rootComponent.setCurrentTermbase(tb) + }, + rightIcon = Icons.Default.Close, + onRightIconClicked = { + val tb = openedTermbases[it] + rootComponent.closeTermbase(tb) + }, + ) + when (val config = mainConfig.child?.configuration) { + RootComponent.MainConfig.Intro -> { + val childComponent = mainConfig.child?.instance as IntroComponent + IntroScreen( + component = childComponent + ) + } + + is RootComponent.MainConfig.Main -> { + val childComponent = mainConfig.child?.instance as MainComponent + MainScreen( + component = childComponent, + modifier = Modifier.fillMaxWidth() + .weight(1f) + .background(color = SelectedBackground, shape = RoundedCornerShape(4.dp)) + .padding(Spacing.xs), + termbase = config.termbase, + ) + } + } + } + } +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/MetaTerm.kt b/src/jvmMain/kotlin/MetaTerm.kt deleted file mode 100644 index c61a054..0000000 --- a/src/jvmMain/kotlin/MetaTerm.kt +++ /dev/null @@ -1,478 +0,0 @@ -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.MaterialTheme -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close -import androidx.compose.runtime.* -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.key.Key -import androidx.compose.ui.input.key.KeyShortcut -import androidx.compose.ui.unit.DpSize -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.MenuBar -import androidx.compose.ui.window.Window -import androidx.compose.ui.window.application -import androidx.compose.ui.window.rememberWindowState -import com.arkivanov.essenty.instancekeeper.getOrCreate -import di.* -import keystore.TemporaryKeyStore -import kotlinx.coroutines.runBlocking -import log.LogManager -import notification.NotificationCenter -import org.koin.core.context.startKoin -import org.koin.java.KoinJavaComponent.inject -import ui.components.CustomOpenFileDialog -import ui.components.CustomSaveFileDialog -import ui.components.CustomTabBar -import ui.dialog.create.CreateTermbaseWizardDialog -import ui.dialog.create.stepone.CreateTermbaseWizardStepOneViewModel -import ui.dialog.create.stepthree.CreateTermbaseWizardStepThreeViewModel -import ui.dialog.create.steptwo.CreateTermbaseWizardStepTwoViewModel -import ui.dialog.edit.EditTermbaseDialog -import ui.dialog.edit.EditTermbaseViewModel -import ui.dialog.manage.ManageTermbasesDialog -import ui.dialog.statistics.TermbaseStatisticsDialog -import ui.screens.intro.IntroScreen -import ui.screens.main.MainScreen -import ui.theme.MetaTermTheme -import ui.theme.SelectedBackground -import ui.theme.Spacing -import utils.AppBusiness -import java.util.* - -fun initKoin() { - startKoin { - modules( - commonKoinModule, - databaseKoinModule, - repositoryKoinModule, - - mainKoinModule, - termbasesKoinModule, - termsKoinModule, - ) - } -} - - -@Composable -@Preview -private fun App() { - val viewModel: AppViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: AppViewModel by inject(AppViewModel::class.java) - res - } - MetaTermTheme { - val uiState by viewModel.uiState.collectAsState() - val openedTermbases = uiState.openedTermbases - val termbase = uiState.currentTermbase - val currentIndex = openedTermbases.indexOfFirst { it.id == termbase?.id }.takeIf { it >= 0 } - Column( - modifier = Modifier.background( - color = MaterialTheme.colors.background, - ).padding(top = Spacing.xs), - ) { - CustomTabBar( - modifier = Modifier.fillMaxWidth(), - tabs = openedTermbases.map { it.name }, - current = currentIndex, - onTabSelected = { - val tb = openedTermbases[it] - viewModel.setCurrentTermbase(tb) - }, - rightIcon = Icons.Default.Close, - onRightIconClicked = { - val tb = openedTermbases[it] - viewModel.closeTermbase(tb) - }, - ) - if (termbase != null) { - - MainScreen( - modifier = Modifier.fillMaxWidth() - .weight(1f) - .background(color = SelectedBackground, shape = RoundedCornerShape(4.dp)) - .padding(Spacing.xs), - termbase = termbase, - ) - } else { - - IntroScreen() - } - } - } -} - -@OptIn(ExperimentalComposeUiApi::class) -fun main() = application { - // init DI - initKoin() - - // init log - val log: LogManager by inject(LogManager::class.java) - log.debug("App initialized") - Thread.setDefaultUncaughtExceptionHandler { t, e -> - log.exception("Exception in ${t.name}", cause = e) - } - - // init l10n - val keyStore: TemporaryKeyStore by inject(TemporaryKeyStore::class.java) - runBlocking { - val currentLang = keyStore.get("lang", Locale.getDefault().language) - L10n.setLanguage(currentLang) - } - - Window( - onCloseRequest = ::exitApplication, - title = "app_name".localized(), - state = rememberWindowState(size = DpSize.Unspecified), - ) { - val appViewModel: AppViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: AppViewModel by inject(AppViewModel::class.java) - res - } - - val uiState by appViewModel.uiState.collectAsState() - var newTermbaseWizardOpen by remember { - mutableStateOf(false) - } - var manageTermbasesDialogOpen by remember { - mutableStateOf(false) - } - var editTermbaseWizardOpen by remember { - mutableStateOf(false) - } - var exportTbxDialogOpen by remember { - mutableStateOf(false) - } - var exportCsvDialogOpen by remember { - mutableStateOf(false) - } - var importTbxDialogOpen by remember { - mutableStateOf(false) - } - var importCsvDialogOpen by remember { - mutableStateOf(false) - } - var statisticsDialogOpen by remember { - mutableStateOf(false) - } - - val notificationCenter: NotificationCenter by inject(NotificationCenter::class.java) - - MenuBar { - Menu("menu_termbase".localized()) { - Item( - text = "menu_termbase_new".localized(), - shortcut = KeyShortcut(key = Key.N, meta = true), - ) { - val viewModel: EditTermbaseViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: EditTermbaseViewModel by inject(EditTermbaseViewModel::class.java) - res - } - viewModel.reset() - val stepOneViewModel: CreateTermbaseWizardStepOneViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepOneViewModel by inject(CreateTermbaseWizardStepOneViewModel::class.java) - res - } - stepOneViewModel.reset() - val stepTwoViewModel: CreateTermbaseWizardStepTwoViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepTwoViewModel by inject(CreateTermbaseWizardStepTwoViewModel::class.java) - res - } - stepTwoViewModel.reset() - val stepThreeViewModel: CreateTermbaseWizardStepThreeViewModel = - AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepThreeViewModel by inject( - CreateTermbaseWizardStepThreeViewModel::class.java) - res - } - stepThreeViewModel.reset() - appViewModel.shouldOpenNewTermbaseOnDialogClose = true - newTermbaseWizardOpen = true - } - Item( - text = "menu_termbase_edit".localized(), - shortcut = KeyShortcut(key = Key.D, meta = true), - enabled = uiState.currentTermbase != null, - ) { - appViewModel.termbaseToEdit = uiState.currentTermbase - val viewModel: EditTermbaseViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: EditTermbaseViewModel by inject(EditTermbaseViewModel::class.java) - res - } - viewModel.reset() - val stepOneViewModel: CreateTermbaseWizardStepOneViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepOneViewModel by inject(CreateTermbaseWizardStepOneViewModel::class.java) - res - } - stepOneViewModel.reset() - val stepTwoViewModel: CreateTermbaseWizardStepTwoViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepTwoViewModel by inject(CreateTermbaseWizardStepTwoViewModel::class.java) - res - } - stepTwoViewModel.reset() - val stepThreeViewModel: CreateTermbaseWizardStepThreeViewModel = - AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepThreeViewModel by inject( - CreateTermbaseWizardStepThreeViewModel::class.java) - res - } - stepThreeViewModel.reset() - editTermbaseWizardOpen = true - } - Item( - text = "menu_termbase_manage".localized(), - shortcut = KeyShortcut(key = Key.O, meta = true), - ) { - manageTermbasesDialogOpen = true - } - Separator() - Item( - text = "menu_termbase_settings".localized(), - shortcut = KeyShortcut(key = Key.Comma, meta = true), - ) {} - Item( - text = "menu_termbase_statistics".localized(), - shortcut = KeyShortcut(key = Key.Period, meta = true), - enabled = uiState.currentTermbase != null, - ) { - statisticsDialogOpen = true - } - Separator() - Menu("menu_termbase_import".localized()) { - Item( - text = "menu_termbase_import_csv".localized(), - enabled = uiState.currentTermbase != null, - ) { - importCsvDialogOpen = true - } - Item( - text = "menu_termbase_import_tbx".localized(), - enabled = uiState.currentTermbase != null, - ) { - importTbxDialogOpen = true - } - } - Menu("menu_termbase_export".localized()) { - Item( - text = "menu_termbase_export_csv".localized(), - enabled = uiState.currentTermbase != null, - ) { - exportCsvDialogOpen = true - } - Item( - text = "menu_termbase_export_tbx".localized(), - enabled = uiState.currentTermbase != null, - ) { - exportTbxDialogOpen = true - } - } - Separator() - Item( - text = "button_close".localized(), - shortcut = KeyShortcut(key = Key.W, meta = true), - enabled = uiState.currentTermbase != null, - ) { - exitApplication() - } - } - Menu( - text = "menu_entry".localized(), - ) { - Item( - text = "menu_entry_new".localized(), - enabled = uiState.currentTermbase != null, - shortcut = KeyShortcut(key = Key.Plus, meta = true), - ) { - notificationCenter.send(NotificationCenter.Event.CreateEntry) - } - if (uiState.entryEditMode) { - Item( - text = "menu_entry_close_edit".localized(), - enabled = uiState.currentTermbase != null && uiState.currentEntry != null, - shortcut = KeyShortcut(key = Key.E, meta = true), - ) { - notificationCenter.send(NotificationCenter.Event.CloseEntryEditMode) - } - } else { - Item( - text = "menu_entry_edit".localized(), - enabled = uiState.currentTermbase != null && uiState.currentEntry != null, - shortcut = KeyShortcut(key = Key.E, meta = true), - ) { - notificationCenter.send(NotificationCenter.Event.OpenEntryEditMode) - } - } - Item( - text = "menu_entry_save".localized(), - enabled = uiState.currentTermbase != null && uiState.currentEntry != null && uiState.entryEditMode, - shortcut = KeyShortcut(key = Key.S, meta = true), - ) { - notificationCenter.send(NotificationCenter.Event.SaveEntry) - } - Item( - text = "menu_entry_delete".localized(), - enabled = uiState.currentTermbase != null && uiState.currentEntry != null, - shortcut = KeyShortcut(key = Key.Backspace, meta = true), - ) { - notificationCenter.send(NotificationCenter.Event.DeleteEntry) - } - } - } - - App() - - if (manageTermbasesDialogOpen) { - ManageTermbasesDialog( - onNew = { - val viewModel: EditTermbaseViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: EditTermbaseViewModel by inject(EditTermbaseViewModel::class.java) - res - } - viewModel.reset() - val stepOneViewModel: CreateTermbaseWizardStepOneViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepOneViewModel by inject(CreateTermbaseWizardStepOneViewModel::class.java) - res - } - stepOneViewModel.reset() - val stepTwoViewModel: CreateTermbaseWizardStepTwoViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepTwoViewModel by inject(CreateTermbaseWizardStepTwoViewModel::class.java) - res - } - stepTwoViewModel.reset() - val stepThreeViewModel: CreateTermbaseWizardStepThreeViewModel = - AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepThreeViewModel by inject( - CreateTermbaseWizardStepThreeViewModel::class.java) - res - } - stepThreeViewModel.reset() - newTermbaseWizardOpen = true - }, - onClose = { - manageTermbasesDialogOpen = false - }, - onEdit = { - appViewModel.termbaseToEdit = it - val viewModel: EditTermbaseViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: EditTermbaseViewModel by inject(EditTermbaseViewModel::class.java) - res - } - viewModel.reset() - val stepOneViewModel: CreateTermbaseWizardStepOneViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepOneViewModel by inject(CreateTermbaseWizardStepOneViewModel::class.java) - res - } - stepOneViewModel.reset() - val stepTwoViewModel: CreateTermbaseWizardStepTwoViewModel = AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepTwoViewModel by inject(CreateTermbaseWizardStepTwoViewModel::class.java) - res - } - stepTwoViewModel.reset() - val stepThreeViewModel: CreateTermbaseWizardStepThreeViewModel = - AppBusiness.instanceKeeper.getOrCreate { - val res: CreateTermbaseWizardStepThreeViewModel by inject( - CreateTermbaseWizardStepThreeViewModel::class.java) - res - } - stepThreeViewModel.reset() - editTermbaseWizardOpen = true - }, - ) - } - - if (newTermbaseWizardOpen) { - CreateTermbaseWizardDialog( - openNewlyCreated = appViewModel.shouldOpenNewTermbaseOnDialogClose, - onClose = { - newTermbaseWizardOpen = false - appViewModel.shouldOpenNewTermbaseOnDialogClose = false - }, - ) - } - - if (editTermbaseWizardOpen) { - val termbase = appViewModel.termbaseToEdit - if (termbase != null) { - EditTermbaseDialog( - initialTermbase = termbase, - onClose = { - appViewModel.termbaseToEdit = null - editTermbaseWizardOpen = false - }, - ) - } - } - - if (exportTbxDialogOpen) { - CustomSaveFileDialog( - title = "dialog_title_export".localized(), - initialFileName = "termbase.tbx", - nameFilter = { it.endsWith(".tbx") }, - onCloseRequest = { dest -> - if (!dest.isNullOrEmpty()) { - appViewModel.exportTbx(dest) - } - exportTbxDialogOpen = false - }, - ) - } - - if (exportCsvDialogOpen) { - CustomSaveFileDialog( - title = "dialog_title_export".localized(), - initialFileName = "termbase.csv", - nameFilter = { it.endsWith(".csv") }, - onCloseRequest = { dest -> - if (!dest.isNullOrEmpty()) { - appViewModel.exportCsv(dest) - } - exportTbxDialogOpen = false - }, - ) - } - - if (importCsvDialogOpen) { - CustomOpenFileDialog( - title = "dialog_title_import".localized(), - nameFilter = { it.endsWith(".csv") }, - onCloseRequest = { path -> - if (!path.isNullOrEmpty()) { - appViewModel.importCsv(path) - } - importCsvDialogOpen = false - }, - ) - } - - if (importTbxDialogOpen) { - CustomOpenFileDialog( - title = "dialog_title_import".localized(), - nameFilter = { it.endsWith(".tbx") }, - onCloseRequest = { path -> - if (!path.isNullOrEmpty()) { - appViewModel.importTbx(path) - } - importTbxDialogOpen = false - }, - ) - } - - if (statisticsDialogOpen) { - uiState.currentTermbase?.also { - TermbaseStatisticsDialog( - termbase = it, - onClose = { - statisticsDialogOpen = false - }, - ) - } - } - } -} diff --git a/src/jvmMain/kotlin/di/MainModule.kt b/src/jvmMain/kotlin/di/MainModule.kt deleted file mode 100644 index 7ac0078..0000000 --- a/src/jvmMain/kotlin/di/MainModule.kt +++ /dev/null @@ -1,37 +0,0 @@ -package di - -import AppViewModel -import org.koin.dsl.module -import ui.screens.intro.IntroViewModel -import ui.screens.main.MainViewModel - -val mainKoinModule = module { - factory { - MainViewModel( - dispatcherProvider = get(), - entryRepository = get(), - ) - } - factory { - IntroViewModel( - dispatcherProvider = get(), - termbaseRepository = get(), - notificationCenter = get(), - ) - } - factory { - AppViewModel( - dispatcherProvider = get(), - termbaseRepository = get(), - entryRepository = get(), - propertyRepository = get(), - exportTbxUseCase = get(), - exportCsvUseCase = get(), - importCsvUseCase = get(), - importTbxUseCase = get(), - notificationCenter = get(), - temporaryKeystore = get(), - log = get(), - ) - } -} \ No newline at end of file diff --git a/src/jvmMain/kotlin/root/di/Module.kt b/src/jvmMain/kotlin/root/di/Module.kt new file mode 100644 index 0000000..b436b46 --- /dev/null +++ b/src/jvmMain/kotlin/root/di/Module.kt @@ -0,0 +1,32 @@ +package root.di + +import intro.di.introModule +import main.di.mainModule +import org.koin.dsl.module +import ui.DefaultRootComponent +import ui.RootComponent + +val rootModule = module { + includes( + introModule, + mainModule, + ) + + factory { + DefaultRootComponent( + componentContext = it[0], + coroutineContext = it[1], + dispatcherProvider = get(), + termbaseRepository = get(), + entryRepository = get(), + propertyRepository = get(), + exportTbxUseCase = get(), + exportCsvUseCase = get(), + importCsvUseCase = get(), + importTbxUseCase = get(), + notificationCenter = get(), + temporaryKeystore = get(), + log = get(), + ) + } +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/root/ui/DefaultRootComponent.kt b/src/jvmMain/kotlin/root/ui/DefaultRootComponent.kt new file mode 100644 index 0000000..ba718fd --- /dev/null +++ b/src/jvmMain/kotlin/root/ui/DefaultRootComponent.kt @@ -0,0 +1,295 @@ +package ui + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.arkivanov.decompose.router.slot.childSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.lifecycle.doOnCreate +import com.arkivanov.essenty.lifecycle.doOnDestroy +import common.coroutines.CoroutineDispatcherProvider +import data.TermbaseModel +import dialogcreate.ui.CreateTermbaseComponent +import dialogedit.ui.EditTermbaseComponent +import dialogmanage.ui.ManageTermbasesComponent +import dialogstatistics.ui.TermbaseStatisticsComponent +import intro.ui.IntroComponent +import common.keystore.TemporaryKeyStore +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import common.log.LogManager +import main.ui.MainComponent +import common.notification.NotificationCenter +import repo.EntryRepository +import repo.PropertyRepository +import repo.TermbaseRepository +import usecase.ExportCsvUseCase +import usecase.ExportTbxUseCase +import usecase.ImportCsvUseCase +import usecase.ImportTbxUseCase +import common.utils.getByInjection +import java.io.File +import kotlin.coroutines.CoroutineContext + +internal class DefaultRootComponent( + componentContext: ComponentContext, + coroutineContext: CoroutineContext, + private val dispatcherProvider: CoroutineDispatcherProvider, + private val termbaseRepository: TermbaseRepository, + entryRepository: EntryRepository, + propertyRepository: PropertyRepository, + private val exportTbxUseCase: ExportTbxUseCase, + private val exportCsvUseCase: ExportCsvUseCase, + private val importCsvUseCase: ImportCsvUseCase, + private val importTbxUseCase: ImportTbxUseCase, + private val notificationCenter: NotificationCenter, + private val temporaryKeystore: TemporaryKeyStore, + private val log: LogManager, +) : RootComponent, ComponentContext by componentContext { + + companion object { + private const val LAST_OPENED_TERMBASE_ID_KEY = "lastOpenTermbaseId" + } + + override var shouldOpenNewTermbaseOnDialogClose: Boolean = false + override var termbaseToEdit: TermbaseModel? = null + + private val currentTermbase = MutableStateFlow(null) + private val openedTermbases = MutableStateFlow>(emptyList()) + private lateinit var viewModelScope: CoroutineScope + private val dialogNavigation = SlotNavigation() + private val mainNavigation = SlotNavigation() + + override lateinit var uiState: StateFlow + override val dialog: Value> = childSlot( + source = dialogNavigation, + key = "RootDialogSlot", + childFactory = { config, context -> + when (config) { + RootComponent.DialogConfig.NewTermbase -> getByInjection( + context, + coroutineContext + ) + + RootComponent.DialogConfig.EditTermbase -> getByInjection( + context, + coroutineContext + ) + + RootComponent.DialogConfig.ManageTermbases -> getByInjection( + context, + coroutineContext + ) + + RootComponent.DialogConfig.Statistics -> getByInjection( + context, + coroutineContext + ) + + else -> Unit + } + } + ) + override val main: Value> = childSlot( + source = mainNavigation, + key = "RootMainSlot", + childFactory = { config, context -> + when (config) { + RootComponent.MainConfig.Intro -> getByInjection(context, coroutineContext) + is RootComponent.MainConfig.Main -> getByInjection(context, coroutineContext) + + else -> Unit + } + + } + ) + + init { + with(lifecycle) { + doOnCreate { + viewModelScope = CoroutineScope(coroutineContext + SupervisorJob()) + uiState = combine( + currentTermbase, + entryRepository.currentEntry, + openedTermbases, + entryRepository.editMode, + propertyRepository.editMode, + ) { currentTermbase, currentEntry, availableTermbases, entryEditMode, definitionModelEditMode -> + RootUiState( + currentTermbase = currentTermbase, + currentEntry = currentEntry, + openedTermbases = availableTermbases, + entryEditMode = entryEditMode, + definitionModelEditMode = definitionModelEditMode, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = RootUiState(), + ) + + viewModelScope.launch(dispatcherProvider.io) { + val lastOpenedTb = temporaryKeystore.get(LAST_OPENED_TERMBASE_ID_KEY, -1) + if (lastOpenedTb >= 0) { + openTermbase(lastOpenedTb) + } else { + setCurrentTermbase(null) + } + + launch { + notificationCenter.events.filter { it is NotificationCenter.Event.OpenTermbase }.collect { + val termbaseId = (it as NotificationCenter.Event.OpenTermbase).termbaseId + temporaryKeystore.save(LAST_OPENED_TERMBASE_ID_KEY, termbaseId) + openTermbase(termbaseId) + } + } + + launch { + termbaseRepository.all.collect { newTermbasesFromDb -> + openedTermbases.updateAndGet { oldList -> + val newList = mutableListOf() + for (tb in oldList) { + val newTb = newTermbasesFromDb.firstOrNull { e -> e.id == tb.id } + newList += if (newTb != null && tb.name != newTb.name) { + tb.copy(name = newTb.name) + } else { + tb + } + } + newList + } + } + } + } + } + doOnDestroy { + viewModelScope.cancel() + } + } + } + + private suspend fun openTermbase(termbaseId: Int) { + val termbase = termbaseRepository.getById(termbaseId) + if (termbase != null) { + log.debug("Opening ${termbase.name}") + openedTermbases.getAndUpdate { termbases -> + if (termbases.isEmpty()) { + setCurrentTermbase(termbase) + } + if (termbases.none { e -> e.id == termbaseId }) { + termbases + termbase + } else { + termbases + } + } + } + } + + override fun closeTermbase(termbase: TermbaseModel) { + openedTermbases.getAndUpdate { termbases -> + val oldIndex = termbases.indexOfFirst { it.id == termbase.id } + val res = termbases.filter { it.id != termbase.id } + if (currentTermbase.value?.id == termbase.id) { + if (res.isNotEmpty()) { + val newIndex = Integer.min(oldIndex, res.size - 1) + setCurrentTermbase(res[newIndex]) + } else { + setCurrentTermbase(null) + } + } + res + } + } + + override fun setCurrentTermbase(termbase: TermbaseModel?) { + currentTermbase.value = termbase + viewModelScope.launch(dispatcherProvider.io) { + temporaryKeystore.save(LAST_OPENED_TERMBASE_ID_KEY, termbase?.id ?: -1) + } + viewModelScope.launch(dispatcherProvider.main) { + if (termbase == null) { + mainNavigation.activate(RootComponent.MainConfig.Intro) + } else { + mainNavigation.activate(RootComponent.MainConfig.Main(termbase)) + } + } + } + + override fun exportTbx(path: String) { + val termbase = currentTermbase.value ?: return + viewModelScope.launch(dispatcherProvider.io) { + runCatching { + val destination = File(path) + destination.createNewFile() + + exportTbxUseCase( + termbase = termbase, + destination = destination, + ) + } + } + } + + override fun exportCsv(path: String) { + val termbase = currentTermbase.value ?: return + viewModelScope.launch(dispatcherProvider.io) { + runCatching { + val destination = File(path) + destination.createNewFile() + + exportCsvUseCase( + termbase = termbase, + destination = destination, + ) + } + } + } + + override fun importCsv(path: String) { + val termbase = currentTermbase.value ?: return + viewModelScope.launch(dispatcherProvider.io) { + try { + val source = File(path) + + importCsvUseCase( + termbase = termbase, + source = source, + ) + } catch (e: Throwable) { + e.printStackTrace() + } + } + } + + override fun importTbx(path: String) { + val termbase = currentTermbase.value ?: return + viewModelScope.launch(dispatcherProvider.io) { + try { + val source = File(path) + + importTbxUseCase( + termbase = termbase, + source = source, + ) + } catch (e: Throwable) { + e.printStackTrace() + } + } + } + + override fun openDialog(config: RootComponent.DialogConfig) { + viewModelScope.launch(dispatcherProvider.main) { + dialogNavigation.activate(config) + } + } + + override fun closeDialog() { + viewModelScope.launch(dispatcherProvider.main) { + dialogNavigation.activate(RootComponent.DialogConfig.None) + } + } +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/root/ui/RootComponent.kt b/src/jvmMain/kotlin/root/ui/RootComponent.kt new file mode 100644 index 0000000..aaba449 --- /dev/null +++ b/src/jvmMain/kotlin/root/ui/RootComponent.kt @@ -0,0 +1,61 @@ +package ui + +import com.arkivanov.decompose.router.slot.ChildSlot +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import data.ExportType +import data.TermbaseModel +import kotlinx.coroutines.flow.StateFlow + +interface RootComponent { + val uiState: StateFlow + var shouldOpenNewTermbaseOnDialogClose: Boolean + var termbaseToEdit: TermbaseModel? + val dialog: Value> + val main: Value> + + fun closeTermbase(termbase: TermbaseModel) + fun setCurrentTermbase(termbase: TermbaseModel?) + fun exportTbx(path: String) + fun exportCsv(path: String) + fun importCsv(path: String) + fun importTbx(path: String) + fun openDialog(config: DialogConfig) + fun closeDialog() + + interface DialogConfig : Parcelable { + @Parcelize + object None : DialogConfig + + @Parcelize + object NewTermbase : DialogConfig + + @Parcelize + object EditTermbase : DialogConfig + + @Parcelize + object ManageTermbases : DialogConfig + + @Parcelize + data class Export(val type: ExportType) : DialogConfig + + @Parcelize + data class Import(val type: ExportType) : DialogConfig + + @Parcelize + object Statistics : DialogConfig + + @Parcelize + object Settings : DialogConfig + } + + interface MainConfig : Parcelable { + @Parcelize + object Intro : MainConfig + + @Parcelize + data class Main(val termbase: TermbaseModel) : MainConfig + + } +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/AppUiState.kt b/src/jvmMain/kotlin/root/ui/RootUiState.kt similarity index 89% rename from src/jvmMain/kotlin/AppUiState.kt rename to src/jvmMain/kotlin/root/ui/RootUiState.kt index fb01bf4..d1dd869 100644 --- a/src/jvmMain/kotlin/AppUiState.kt +++ b/src/jvmMain/kotlin/root/ui/RootUiState.kt @@ -1,7 +1,9 @@ +package ui + import data.EntryModel import data.TermbaseModel -data class AppUiState( +data class RootUiState( val currentTermbase: TermbaseModel? = null, val currentEntry: EntryModel? = null, val openedTermbases: List = emptyList(),