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(),