First version with MVVM
This commit is contained in:
@@ -4,11 +4,17 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
|
||||
|
||||
val lifecycle_version = "2.6.1"
|
||||
val arch_version = "2.2.0"
|
||||
|
||||
plugins {
|
||||
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
|
||||
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@@ -66,10 +72,19 @@ kotlin {
|
||||
implementation(compose.ui)
|
||||
implementation(compose.components.resources)
|
||||
implementation(compose.components.uiToolingPreview)
|
||||
implementation(libs.androidx.lifecycle.livedata.core)
|
||||
implementation(libs.androidx.lifecycle.viewmodel)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
implementation(libs.lifecycle.runtime.compose)
|
||||
implementation(libs.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(projects.shared)
|
||||
|
||||
implementation(project.dependencies.platform(libs.koin.bom))
|
||||
implementation(libs.koin.core)
|
||||
implementation(libs.koin.compose)
|
||||
implementation(libs.koin.compose.viewmodel)
|
||||
|
||||
|
||||
}
|
||||
desktopMain.dependencies {
|
||||
implementation(compose.desktop.currentOs)
|
||||
@@ -106,6 +121,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.lifecycle.livedata.core.ktx)
|
||||
debugImplementation(compose.uiTooling)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,76 +29,23 @@ import service.TermService
|
||||
import service.TermServiceImpl
|
||||
import service.SearchType
|
||||
import service.DatabaseFactory
|
||||
import org.koin.compose.KoinApplication
|
||||
import org.koin.compose.koinInject
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.neutrino.ktans.di.appModule
|
||||
import org.koin.compose.viewmodel.dsl.viewModelOf
|
||||
import org.neutrino.ktans.view.MainView
|
||||
|
||||
@Composable
|
||||
fun SearchBarTextField() {
|
||||
val query = remember { mutableStateOf("") }
|
||||
val termService = TermServiceImpl()
|
||||
var showResults by remember { mutableStateOf(false) }
|
||||
var terms: List<TermFull>? = null
|
||||
|
||||
TextField(
|
||||
value = query.value,
|
||||
onValueChange = {
|
||||
query.value = it
|
||||
var sType = SearchType.START
|
||||
sType = if (query.value.length > 2) {
|
||||
SearchType.START
|
||||
} else {
|
||||
SearchType.EXACT
|
||||
}
|
||||
|
||||
val translation = getTranslationForLanguages("an","sl") ?: return@TextField
|
||||
println(translation)
|
||||
terms = termService.getTranslationForTerm(it,translation,sType)
|
||||
showResults = terms != null
|
||||
println(terms)
|
||||
|
||||
},
|
||||
placeholder = { Text("Prelož...") },
|
||||
singleLine = true,
|
||||
leadingIcon = { Icon(Icons.Filled.Search, contentDescription = "Search Icon") },
|
||||
trailingIcon = {
|
||||
if (query.value.isNotEmpty()) {
|
||||
IconButton(onClick = { query.value = "" }) {
|
||||
Icon(Icons.Filled.Clear, contentDescription = "Clear Text")
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
)
|
||||
|
||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) {
|
||||
if (showResults && terms != null) {
|
||||
for (term in terms!!) {
|
||||
Text(term.string1)
|
||||
Text("${term.string2},")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun App() {
|
||||
DatabaseFactory.connectAndMigrate()
|
||||
DatabaseFactory.connectAll()
|
||||
|
||||
MaterialTheme {
|
||||
var showContent by remember { mutableStateOf(false) }
|
||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
SearchBarTextField()
|
||||
Button(onClick = { showContent = !showContent }) {
|
||||
Text("Click me!")
|
||||
}
|
||||
AnimatedVisibility(showContent) {
|
||||
val greeting = remember { Greeting().greet() }
|
||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Image(painterResource(Res.drawable.compose_multiplatform), null)
|
||||
Text("Compose: $greeting")
|
||||
}
|
||||
}
|
||||
KoinApplication(application = {
|
||||
modules(appModule())
|
||||
}) {
|
||||
MaterialTheme {
|
||||
MainView()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.neutrino.ktans.di
|
||||
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.module
|
||||
import service.TermServiceImpl
|
||||
import service.DatabaseFactory
|
||||
import org.koin.compose.viewmodel.dsl.viewModelOf
|
||||
import org.neutrino.ktans.viewmodel.MainModelView
|
||||
import org.koin.core.module.dsl.factoryOf
|
||||
|
||||
fun appModule() = module {
|
||||
singleOf(::TermServiceImpl)
|
||||
viewModelOf(::MainModelView)
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.neutrino.ktans.view
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.runtime.Composable
|
||||
import kotlinx.coroutines.flow.observeOn
|
||||
import org.koin.compose.koinInject
|
||||
import org.neutrino.ktans.viewmodel.MainModelView
|
||||
import org.neutrino.ktans.viewmodel.Status
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
||||
@Composable
|
||||
fun RowScope.TableCell(
|
||||
text: String,
|
||||
weight: Float
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
Modifier
|
||||
.border(1.dp, Color.Black)
|
||||
.weight(weight)
|
||||
.padding(8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun SearchBarTextField(viewModel: MainModelView) {
|
||||
val query = remember { mutableStateOf("") }
|
||||
Column {
|
||||
TextField(
|
||||
value = query.value,
|
||||
onValueChange = {
|
||||
query.value = it
|
||||
viewModel.getTerms(query.value)
|
||||
},
|
||||
placeholder = { Text("Prelož...") },
|
||||
singleLine = true,
|
||||
leadingIcon = { Icon(Icons.Filled.Search, contentDescription = "Search Icon") },
|
||||
trailingIcon = {
|
||||
if (query.value.isNotEmpty()) {
|
||||
IconButton(onClick = {
|
||||
query.value = ""
|
||||
viewModel.clearSearch()
|
||||
}) {
|
||||
Icon(Icons.Filled.Clear, contentDescription = "Clear Text")
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
)
|
||||
|
||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) {
|
||||
val terms by viewModel.terms.collectAsState()
|
||||
|
||||
if (terms != null) {
|
||||
SelectionContainer {
|
||||
Column {
|
||||
Row(Modifier.background(Color.Gray)) {
|
||||
TableCell(text = "Anglicky", weight = .5f)
|
||||
TableCell(text = "Slovensky", weight = .5f)
|
||||
}
|
||||
val column1Weight = .5f // 50%
|
||||
val column2Weight = .5f // 50%
|
||||
// The LazyColumn will be our table. Notice the use of the weights below
|
||||
LazyColumn(Modifier.fillMaxSize().padding(0.dp)) {
|
||||
// Here is the header
|
||||
|
||||
items(items = terms!!, itemContent = { t ->
|
||||
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
TableCell(text = t.string1, weight = column1Weight)
|
||||
TableCell(text = t.string2, weight = column2Weight)
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Composable
|
||||
fun MainView(viewModel: MainModelView = koinInject<MainModelView>(),) {
|
||||
SearchBarTextField(viewModel)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.neutrino.ktans.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import models.TermFull
|
||||
import service.TermService
|
||||
import service.TermServiceImpl
|
||||
import service.DatabaseFactory.getTranslationForLanguages
|
||||
import service.SearchType
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
class MainModelView(private val repository: TermServiceImpl): ViewModel() {
|
||||
val terms : StateFlow<List<TermFull>?> get() = _terms
|
||||
private val _terms = MutableStateFlow<List<TermFull>?>(null)
|
||||
|
||||
fun getTerms(term: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
var sType = SearchType.START
|
||||
sType = if (term.length > 2) SearchType.START else SearchType.EXACT
|
||||
val trans = getTranslationForLanguages("an","sl") ?: return@launch
|
||||
val transTerms = repository.getTranslationForTerm(term,trans,sType)
|
||||
_terms.value = transTerms
|
||||
|
||||
}
|
||||
}
|
||||
fun clearSearch() {
|
||||
_terms.value = listOf()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.neutrino.ktans.viewmodel
|
||||
|
||||
/**
|
||||
* A generic class that holds a value with its loading status.
|
||||
* @param <T>
|
||||
</T> */
|
||||
data class State<out T>(val status: Status, val data: T?, val message: String?)
|
||||
{
|
||||
companion object {
|
||||
fun <T> success(data: T?): State<T> {
|
||||
return State(Status.SUCCESS, data, null)
|
||||
}
|
||||
|
||||
fun <T> empty(): State<T> {
|
||||
return State(Status.SUCCESS, null, null)
|
||||
}
|
||||
|
||||
fun <T> error(msg: String?): State<T> {
|
||||
return State(Status.ERROR, null, msg)
|
||||
}
|
||||
|
||||
fun <T> loading(): State<T> {
|
||||
return State(Status.LOADING, null, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class Status {
|
||||
SUCCESS,
|
||||
ERROR,
|
||||
LOADING
|
||||
}
|
||||
@@ -2,12 +2,15 @@ package org.neutrino.ktans
|
||||
|
||||
import androidx.compose.ui.window.Window
|
||||
import androidx.compose.ui.window.application
|
||||
import service.DatabaseFactory
|
||||
|
||||
fun main() = application {
|
||||
Window(
|
||||
onCloseRequest = ::exitApplication,
|
||||
title = "KTrans",
|
||||
) {
|
||||
DatabaseFactory.connectAndMigrate()
|
||||
DatabaseFactory.connectAll()
|
||||
App()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user