Compare commits
7 Commits
0d424ac413
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f07ef47de0 | |||
| e02c600ccd | |||
| 1f9ff73a6d | |||
| 56e7c53f70 | |||
| 66617923b7 | |||
| 47e1b47028 | |||
| 52eef8dbd4 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -11,8 +11,8 @@ captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
*.xcodeproj/*
|
||||
data/**
|
||||
composeApp/data/**
|
||||
data/*
|
||||
composeApp/data/*
|
||||
composeApp/src/commonMain/resources/media/**
|
||||
!*.xcodeproj/project.pbxproj
|
||||
!*.xcodeproj/xcshareddata/
|
||||
|
||||
@@ -69,12 +69,15 @@ kotlin {
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material)
|
||||
implementation(compose.material3)
|
||||
implementation(compose.ui)
|
||||
implementation(compose.components.resources)
|
||||
implementation(compose.components.uiToolingPreview)
|
||||
implementation(compose.animation)
|
||||
implementation(libs.androidx.lifecycle.livedata.core)
|
||||
implementation(libs.androidx.lifecycle.viewmodel)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.lifecycle.runtime.compose)
|
||||
implementation(libs.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
@@ -86,7 +89,8 @@ kotlin {
|
||||
implementation(libs.composeIcons.tablerIcons)
|
||||
implementation(libs.composeIcons.fontAwesome)
|
||||
implementation(libs.korau)
|
||||
|
||||
implementation(libs.flagkit)
|
||||
implementation(libs.navigation.compose.v280alpha10)
|
||||
|
||||
}
|
||||
desktopMain.dependencies {
|
||||
@@ -94,6 +98,9 @@ kotlin {
|
||||
implementation(libs.kotlinx.coroutines.swing)
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation(libs.androidx.navigation.runtime.android)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
@@ -34,8 +34,7 @@ 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
|
||||
|
||||
import org.neutrino.ktans.ui.MainView
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.neutrino.ktans.model.repository
|
||||
|
||||
import service.TermServiceImpl
|
||||
|
||||
class TermRepository {
|
||||
private val termService = TermServiceImpl()
|
||||
|
||||
suspend fun getTerms
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.neutrino.ktans.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import org.neutrino.ktans.ui.navigation.NavGraph
|
||||
|
||||
@Composable
|
||||
fun MainView() {
|
||||
NavGraph()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.neutrino.ktans.ui.navigation
|
||||
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import org.neutrino.ktans.ui.screens.MainScreen
|
||||
import org.neutrino.ktans.ui.screens.SettingsScreen
|
||||
|
||||
sealed class Screen(val route: String) {
|
||||
object Home : Screen("home")
|
||||
object Settings : Screen("settings")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavGraph(startDestination: String = Screen.Home.route) {
|
||||
val navController = rememberNavController()
|
||||
NavHost(navController = navController, startDestination = startDestination) {
|
||||
composable(Screen.Home.route) { MainScreen(navController) }
|
||||
composable(Screen.Settings.route) { SettingsScreen(navController) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
package org.neutrino.ktans.ui.screens
|
||||
|
||||
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.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
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.material.icons.filled.Settings
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.runtime.Composable
|
||||
import org.koin.compose.koinInject
|
||||
import org.neutrino.ktans.viewmodel.MainModelView
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.Table
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import compose.icons.FontAwesomeIcons
|
||||
import compose.icons.fontawesomeicons.Solid
|
||||
import compose.icons.tablericons.ArrowBigRight
|
||||
import compose.icons.tablericons.FileText
|
||||
import compose.icons.fontawesomeicons.solid.PlayCircle
|
||||
import compose.icons.tablericons.ArrowBigLeft
|
||||
import models.TermFull
|
||||
import service.DatabaseFactory.getTranslationForLanguages
|
||||
import service.DatabaseFactory.getDictionaies
|
||||
import service.DatabaseFactory.getLanguageForId
|
||||
import dev.carlsen.flagkit.FlagKit
|
||||
import models.Dictionary
|
||||
import androidx.navigation.NavController
|
||||
import org.neutrino.ktans.ui.navigation.Screen
|
||||
|
||||
|
||||
@Composable
|
||||
fun RowScope.TableCell(
|
||||
text: String,
|
||||
weight: Float,
|
||||
color: Color = Color.Gray
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
Modifier
|
||||
.border(1.dp, Color.LightGray)
|
||||
.weight(weight)
|
||||
.padding(4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LangugeDropdown(onChangeClick: (dict: Dictionary) -> Unit,dictionary: Dictionary?) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(onClick = {
|
||||
expanded = !expanded
|
||||
}) {
|
||||
getLanguageForId(dictionary!!.lang1Id)?.let{ l ->
|
||||
FlagKit.getFlag(countryCode = l.alphaCode!!)?.let {
|
||||
Image(
|
||||
imageVector = it,
|
||||
contentDescription = "English",
|
||||
)
|
||||
}
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
for ((id, dict) in getDictionaies()) {
|
||||
getLanguageForId(dict.lang1Id)?.let { l ->
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Image(
|
||||
imageVector = FlagKit.getFlag(countryCode = l.alphaCode!!)!!,
|
||||
contentDescription = l.name
|
||||
)
|
||||
},
|
||||
text = { Text(l.name) },
|
||||
onClick = { onChangeClick(dict); expanded = false },
|
||||
trailingIcon = {
|
||||
if (dictionary.id == id)
|
||||
Icon(Icons.Default.Check,contentDescription = "Selected")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun groupByString(terms: List<TermFull>, direction: Int): MutableList<MutableList<TermFull>> {
|
||||
val fullList: MutableList<MutableList<TermFull>> = mutableListOf()
|
||||
var lastTerm: TermFull? = null
|
||||
var termsEq: MutableList<TermFull> = mutableListOf()
|
||||
for (t in terms) {
|
||||
if (lastTerm == null) {
|
||||
termsEq += t
|
||||
} else {
|
||||
val s1 = if (direction == 1) lastTerm.string1 else lastTerm.string2
|
||||
val s2 = if (direction == 1) t.string1 else t.string2
|
||||
if (s1 == s2) {
|
||||
termsEq += t
|
||||
} else {
|
||||
fullList += termsEq
|
||||
termsEq = mutableListOf()
|
||||
termsEq += t
|
||||
}
|
||||
|
||||
}
|
||||
lastTerm = t
|
||||
}
|
||||
fullList += termsEq
|
||||
return fullList
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SearchBarTextField(navController: NavController) {
|
||||
val viewModel: MainModelView = koinInject<MainModelView>()
|
||||
val query = remember { mutableStateOf("") }
|
||||
var columnViewType = remember { mutableStateOf(false) }
|
||||
var tDirection = remember { mutableStateOf(1) }
|
||||
|
||||
Column {
|
||||
val dict by viewModel.dictionary.collectAsState()
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.End,
|
||||
modifier = Modifier.background(Color.LightGray)
|
||||
) {
|
||||
|
||||
TextField(
|
||||
value = query.value,
|
||||
onValueChange = {
|
||||
query.value = it
|
||||
if (dict == null) return@TextField
|
||||
val l1 = getLanguageForId(dict!!.lang1Id)
|
||||
val l2 = getLanguageForId(dict!!.lang2Id)
|
||||
if (l1 != null && l2 != null) {
|
||||
val trans = if (tDirection.value == 1) getTranslationForLanguages(l1.shortName,l2.shortName) else getTranslationForLanguages(l2.shortName,l1.shortName)
|
||||
if (trans == null) return@TextField
|
||||
viewModel.getTerms(query.value, trans)
|
||||
}
|
||||
},
|
||||
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.weight(3f)
|
||||
)
|
||||
Row(Modifier.width(200.dp).border(width = 2.dp, Color.Black)) {
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
IconButton(onClick = {
|
||||
columnViewType.value = !columnViewType.value
|
||||
}) {
|
||||
if (columnViewType.value)
|
||||
Icon(
|
||||
TablerIcons.Table,
|
||||
contentDescription = "Table View",
|
||||
modifier = Modifier.size(width = 30.dp, height = 30.dp)
|
||||
)
|
||||
else
|
||||
Icon(
|
||||
TablerIcons.FileText,
|
||||
contentDescription = "List View",
|
||||
modifier = Modifier.size(width = 30.dp, height = 30.dp)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
IconButton(onClick = {
|
||||
tDirection.value = if (tDirection.value == 1) 2 else 1
|
||||
}) {
|
||||
if (tDirection.value == 1)
|
||||
Icon(
|
||||
TablerIcons.ArrowBigRight,
|
||||
contentDescription = "Forward",
|
||||
modifier = Modifier.size(width = 30.dp, height = 30.dp)
|
||||
)
|
||||
else
|
||||
Icon(
|
||||
TablerIcons.ArrowBigLeft,
|
||||
contentDescription = "Resverse",
|
||||
modifier = Modifier.size(width = 30.dp, height = 30.dp)
|
||||
)
|
||||
}
|
||||
LangugeDropdown(
|
||||
onChangeClick = viewModel::changeDictionary,
|
||||
dictionary = dict
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
IconButton(onClick = {
|
||||
navController.navigate(Screen.Settings.route)
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Filled.Settings,
|
||||
contentDescription = "Settings",
|
||||
modifier = Modifier.size(width = 30.dp, height = 30.dp)
|
||||
)}
|
||||
|
||||
//Spacer(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
val terms by viewModel.terms.collectAsState()
|
||||
|
||||
if (terms != null) {
|
||||
SelectionContainer {
|
||||
Column {
|
||||
if (columnViewType.value) {
|
||||
Row(Modifier.background(Color.Gray)) {
|
||||
TableCell(text = if (tDirection.value == 1) "Anglicky" else "Slovensky", weight = .5f)
|
||||
TableCell(text = if (tDirection.value == 1) "Slovensky" else "Anglicky", 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)) {
|
||||
itemsIndexed(items = terms!!, itemContent = { i, t ->
|
||||
val color = if (i % 2 == 1) Color.hsl(
|
||||
hue = 168f,
|
||||
saturation = .77f,
|
||||
lightness = .68f,
|
||||
alpha = 1f
|
||||
) else Color.hsl(hue = 217f, saturation = .77f, lightness = .68f, alpha = 1f)
|
||||
|
||||
Row(Modifier.fillMaxWidth().background(color)) {
|
||||
TableCell(
|
||||
text = if (tDirection.value == 1) t.string1 else t.string2,
|
||||
weight = column1Weight
|
||||
)
|
||||
TableCell(
|
||||
text = if (tDirection.value == 1) t.string2 else t.string1,
|
||||
weight = column2Weight
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
} else {
|
||||
val terms2: MutableList<MutableList<TermFull>> = groupByString(terms!!, tDirection.value)
|
||||
|
||||
LazyColumn(Modifier.fillMaxSize().padding(0.dp)) {
|
||||
var rowBackground = Color.LightGray
|
||||
itemsIndexed(items = terms2, itemContent = { i, t2 ->
|
||||
var t: TermFull? = null
|
||||
if (t2.size > 0) t = t2[0]
|
||||
else return@itemsIndexed
|
||||
when (i % 2) {
|
||||
0 -> rowBackground = Color.LightGray
|
||||
1 -> rowBackground = Color.Gray
|
||||
}
|
||||
Row(Modifier.fillMaxWidth().background(color = rowBackground)) {
|
||||
Column {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
if (tDirection.value == 1) t.string1 else t.string2,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
for (p in t.pronunciations!!) {
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
if (p.ipa != null) {
|
||||
var color = Color.Blue
|
||||
when (p.typeId) {
|
||||
1 -> color = Color.Green
|
||||
2 -> color = Color.Red
|
||||
3 -> color = Color.Blue
|
||||
}
|
||||
Text("[${p.ipa}]", color = color)
|
||||
}
|
||||
if (p.filename != null) {
|
||||
IconButton(
|
||||
modifier = Modifier.size(20.dp),
|
||||
onClick = {
|
||||
viewModel.playSound("media/${p.filename}")
|
||||
}) {
|
||||
Icon(
|
||||
FontAwesomeIcons.Solid.PlayCircle,
|
||||
contentDescription = "Play sound",
|
||||
Modifier.size(20.dp).padding(start = 2.dp),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
}
|
||||
}
|
||||
|
||||
val str2 =
|
||||
if (tDirection.value == 1) t2.joinToString(separator = ", ") { it.string2 } else t2.joinToString(
|
||||
separator = ", "
|
||||
) { it.string1 }
|
||||
Text(text = str2, modifier = Modifier.padding(start = 20.dp))
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.border(1.dp, Color.Black)
|
||||
.padding(1.dp).fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun MainScreen(navController: NavController) {
|
||||
|
||||
|
||||
SearchBarTextField(navController)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.neutrino.ktans.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
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.ui.text.font.FontWeight
|
||||
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.material.icons.filled.Settings
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
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
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.Table
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import compose.icons.FontAwesomeIcons
|
||||
import compose.icons.fontawesomeicons.Solid
|
||||
import compose.icons.tablericons.ArrowBigRight
|
||||
import compose.icons.tablericons.FileText
|
||||
import compose.icons.fontawesomeicons.solid.PlayCircle
|
||||
import compose.icons.tablericons.ArrowBigLeft
|
||||
import models.Term
|
||||
import models.TermFull
|
||||
import kotlinx.coroutines.*
|
||||
import service.DatabaseFactory.getTranslationForLanguages
|
||||
import service.DatabaseFactory.getLanguageForCode
|
||||
import service.DatabaseFactory.getDictionaies
|
||||
import service.DatabaseFactory.getLanguageForId
|
||||
import dev.carlsen.flagkit.FlagKit
|
||||
import dev.carlsen.flagkit.FlagIcons
|
||||
import models.Dictionary
|
||||
import models.Settings
|
||||
import androidx.navigation.NavController
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(navController: NavController) {
|
||||
val viewModel: MainModelView = koinInject<MainModelView>()
|
||||
Column {
|
||||
Text("Setting Screen")
|
||||
Button(onClick = { navController.popBackStack() }) {
|
||||
Text("Back to Home Screen")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
package org.neutrino.ktans.view
|
||||
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
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.ui.text.font.FontWeight
|
||||
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.material.icons.filled.Settings
|
||||
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
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.Table
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import compose.icons.FontAwesomeIcons
|
||||
import compose.icons.fontawesomeicons.Solid
|
||||
import compose.icons.tablericons.ArrowBigRight
|
||||
import compose.icons.fontawesomeicons.solid.PlayCircle
|
||||
import models.Term
|
||||
import models.TermFull
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun RowScope.TableCell(
|
||||
text: String,
|
||||
weight: Float
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
Modifier
|
||||
.border(1.dp, Color.Black)
|
||||
.weight(weight)
|
||||
.padding(4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun groupByString1(terms: List<TermFull>): MutableList<MutableList<TermFull>> {
|
||||
val fullList: MutableList<MutableList<TermFull>> = mutableListOf()
|
||||
var lastTerm: TermFull? = null
|
||||
var termsEq: MutableList<TermFull> = mutableListOf()
|
||||
for (t in terms) {
|
||||
if (lastTerm == null) {
|
||||
termsEq += t
|
||||
} else {
|
||||
if (lastTerm.string1 == t.string1){
|
||||
termsEq += t
|
||||
} else {
|
||||
fullList += termsEq
|
||||
termsEq = mutableListOf()
|
||||
termsEq += t
|
||||
}
|
||||
|
||||
}
|
||||
lastTerm = t
|
||||
}
|
||||
fullList += termsEq
|
||||
return fullList
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SearchBarTextField(viewModel: MainModelView) {
|
||||
val query = remember { mutableStateOf("") }
|
||||
val columnViewType = remember { mutableStateOf(false) }
|
||||
Column {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.End,
|
||||
modifier = Modifier.background(Color.LightGray)
|
||||
) {
|
||||
|
||||
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.weight(3f)
|
||||
)
|
||||
Row (Modifier.width(155.dp).border(width = 2.dp, Color.Black)) {
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
Icon(TablerIcons.Table, contentDescription = "Table View", modifier = Modifier.size(width = 50.dp, height = 50.dp))
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
Icon(TablerIcons.ArrowBigRight, contentDescription = "Table View", modifier = Modifier.size(width = 50.dp, height = 50.dp))
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
Icon(Icons.Filled.Settings, contentDescription = "Table View", modifier = Modifier.size(width = 50.dp, height = 50.dp))
|
||||
//Spacer(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) {
|
||||
val terms by viewModel.terms.collectAsState()
|
||||
|
||||
if (terms != null) {
|
||||
SelectionContainer {
|
||||
Column {
|
||||
if (columnViewType.value) {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
} else {
|
||||
val terms2: MutableList<MutableList<TermFull>> = groupByString1(terms!!)
|
||||
|
||||
LazyColumn(Modifier.fillMaxSize().padding(0.dp)) {
|
||||
var i=0
|
||||
var rowBackground = Color.LightGray
|
||||
items(items = terms2, itemContent = { t2 ->
|
||||
var t : TermFull? = null
|
||||
if (t2.size > 0) t = t2[0]
|
||||
else return@items
|
||||
when ( i++ % 2 ) {
|
||||
0 -> rowBackground = Color.LightGray
|
||||
1 -> rowBackground = Color.Gray
|
||||
}
|
||||
Row(Modifier.fillMaxWidth().background(color = rowBackground)) {
|
||||
Column {
|
||||
Row( verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(t.string1, fontWeight = FontWeight.Bold)
|
||||
for (p in t.pronunciations!!) {
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
if (p.ipa != null) {
|
||||
var color = Color.Blue
|
||||
when (p.typeId) {
|
||||
1 -> color = Color.Green
|
||||
2 -> color = Color.Red
|
||||
3 -> color = Color.Blue
|
||||
}
|
||||
Text("[${p.ipa}]", color = color)
|
||||
}
|
||||
if (p.filename != null) {
|
||||
IconButton(
|
||||
modifier = Modifier.size(20.dp),
|
||||
onClick = {
|
||||
viewModel.playSound("media/${p.filename}")
|
||||
}) {
|
||||
Icon(FontAwesomeIcons.Solid.PlayCircle,contentDescription = "Play sound",Modifier.size(20.dp).padding(start = 2.dp),)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.padding(2.dp))
|
||||
}
|
||||
}
|
||||
|
||||
val str2 = t2.joinToString(separator = ", ") { it.string2 }
|
||||
Text(text = str2, modifier = Modifier.padding(start = 20.dp))
|
||||
Spacer(modifier = Modifier
|
||||
.border(1.dp, Color.Black)
|
||||
.padding(1.dp).fillMaxWidth())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Composable
|
||||
fun MainView(viewModel: MainModelView = koinInject<MainModelView>(),) {
|
||||
SearchBarTextField(viewModel)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import korlibs.audio.sound.*
|
||||
import korlibs.io.file.std.resourcesVfs
|
||||
import korlibs.io.file.std.ZipVfs
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -13,21 +14,38 @@ import service.DatabaseFactory.getTranslationForLanguages
|
||||
import service.SearchType
|
||||
import service.TermServiceImpl
|
||||
import korlibs.audio.format.*
|
||||
import korlibs.io.compression.zip.ZipEntry
|
||||
import korlibs.io.file.VfsFile
|
||||
import korlibs.io.file.std.openAsZip
|
||||
import models.Dictionary
|
||||
import models.Translation
|
||||
import service.DatabaseFactory.getDictionaies
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
|
||||
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) {
|
||||
val dictionary: StateFlow<Dictionary?> get() = _dictionary
|
||||
private val _dictionary = MutableStateFlow<Dictionary?>(getDictionaies()[1])
|
||||
fun getTerms(term: String, trans: Translation) {
|
||||
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 changeDictionary(dict: Dictionary) {
|
||||
_dictionary.value = dict
|
||||
}
|
||||
|
||||
fun clearSearch() {
|
||||
_terms.value = listOf()
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ assertjVersion = "3.26.3"
|
||||
compose-multiplatform = "1.7.0"
|
||||
coreKtx = "1.9.0"
|
||||
exposedVersion = "0.55.0"
|
||||
flagkit = "1.1.0"
|
||||
flywayVersion = "10.20.1"
|
||||
h2 = "2.3.232"
|
||||
hikariCpVersion = "5.1.0"
|
||||
@@ -67,6 +68,9 @@ lifecycleViewmodelKtx = "2.6.1"
|
||||
lifecycleViewmodelCompose = "2.8.4"
|
||||
logback = "1.5.12"
|
||||
material = "1.7.6"
|
||||
navigationCompose = "2.8.0-alpha10"
|
||||
okio = "3.10.2"
|
||||
androidx-navigation = "2.7.0"
|
||||
psqlVersion = "42.7.4"
|
||||
restAssuredVersion = "5.5.0"
|
||||
runtimeLivedata = "1.7.6"
|
||||
@@ -75,6 +79,7 @@ symbolProcessingApi = "2.1.0-1.0.29"
|
||||
lifecycleLivedataCoreKtx = "2.8.7"
|
||||
composeIcons = "1.1.1"
|
||||
korau = "4.0.10"
|
||||
navigationRuntimeAndroid = "2.9.0-alpha06"
|
||||
|
||||
|
||||
[libraries]
|
||||
@@ -89,6 +94,7 @@ composeIcons-linea = { module = "br.com.devsrsouza.compose.icons:linea", version
|
||||
composeIcons-octicons = { module = "br.com.devsrsouza.compose.icons:octicons", version.ref = "composeIcons" }
|
||||
composeIcons-simpleIcons = { module = "br.com.devsrsouza.compose.icons:simple-icons", version.ref = "composeIcons" }
|
||||
composeIcons-tablerIcons = { module = "br.com.devsrsouza.compose.icons:tabler-icons", version.ref = "composeIcons" }
|
||||
flagkit = { module = "dev.carlsen.flagkit:flagkit", version.ref = "flagkit" }
|
||||
korau = { module = "com.soywiz.korlibs.korau:korau", version.ref = "korau" }
|
||||
|
||||
androidx-core-ktx-v190 = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
|
||||
@@ -182,12 +188,16 @@ ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "kto
|
||||
ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor" }
|
||||
ktor-server-tests = { module = "io.ktor:ktor-server-tests-jvm", version.ref = "ktor" }
|
||||
material = { module = "androidx.compose.material:material", version.ref = "material" }
|
||||
navigation-compose-v280alpha10 = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||
okio = { module = "com.squareup.okio:okio-bom", version.ref = "okio" }
|
||||
postgresql = { module = "org.postgresql:postgresql", version.ref = "psqlVersion" }
|
||||
androidx-navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }
|
||||
rest-assured = { module = "io.rest-assured:rest-assured", version.ref = "restAssuredVersion" }
|
||||
symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "symbolProcessingApi" }
|
||||
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" }
|
||||
koin-core = { module = "io.insert-koin:koin-core" }
|
||||
androidx-lifecycle-livedata-core-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-core-ktx", version.ref = "lifecycleLivedataCoreKtx" }
|
||||
androidx-navigation-runtime-android = { group = "androidx.navigation", name = "navigation-runtime-android", version.ref = "navigationRuntimeAndroid" }
|
||||
|
||||
[plugins]
|
||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
@@ -9,10 +9,12 @@ class LanguageDao(id: EntityID<Int>) : IntEntity(id) {
|
||||
|
||||
var name by Languages.name
|
||||
var shortName by Languages.shortName
|
||||
var alphaCode by Languages.alphaCode
|
||||
|
||||
fun toModel() : Language = Language(
|
||||
id = id.value,
|
||||
name = name,
|
||||
shortName = shortName
|
||||
shortName = shortName,
|
||||
alphaCode = alphaCode
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package db.migration
|
||||
|
||||
import dao.DictionaryDao
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import tables.*
|
||||
import org.flywaydb.core.api.migration.BaseJavaMigration
|
||||
import org.flywaydb.core.api.migration.Context
|
||||
import org.jetbrains.exposed.dao.id.EntityID
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class V4__add_lang_code: BaseJavaMigration() {
|
||||
override fun migrate(context: Context?) {
|
||||
transaction {
|
||||
val mapOfCodes = mapOf(1 to "SK",2 to "CZ", 3 to "GB",4 to "NL", 5 to "FR", 6 to "DE", 7 to "IT", 8 to "IT",
|
||||
9 to "HU", 10 to "PL", 11 to "PT", 12 to "RU", 13 to "ES", 14 to "SE" )
|
||||
SchemaUtils.createMissingTablesAndColumns(Languages)
|
||||
for (l in Languages.selectAll()) {
|
||||
val id = l[Languages.id]
|
||||
Languages.update({ Languages.id eq id}) {
|
||||
it[alphaCode] = mapOfCodes[id.value]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package db.migration
|
||||
|
||||
import dao.DictionaryDao
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import tables.*
|
||||
import org.flywaydb.core.api.migration.BaseJavaMigration
|
||||
import org.flywaydb.core.api.migration.Context
|
||||
import org.jetbrains.exposed.dao.id.EntityID
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import kotlin.system.exitProcess
|
||||
import service.DatabaseFactory
|
||||
|
||||
class V5__add_indexes: BaseJavaMigration() {
|
||||
override fun migrate(context: Context?) {
|
||||
DatabaseFactory.connectAll()
|
||||
// for ((id,dict) in DatabaseFactory.getDictionaies()) {
|
||||
transaction (1) {
|
||||
SchemaUtils.createMissingTablesAndColumns(Terms)
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import org.jetbrains.exposed.sql.Table
|
||||
data class Language(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val shortName: String
|
||||
val shortName: String,
|
||||
val alphaCode: String?
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import javax.sql.DataSource
|
||||
import tables.*
|
||||
import dao.*
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import models.Dictionary
|
||||
import models.Language
|
||||
import models.Translation
|
||||
|
||||
object DatabaseFactory {
|
||||
@@ -21,7 +23,9 @@ object DatabaseFactory {
|
||||
private val dbs : MutableMap<Int,Database> = mutableMapOf()
|
||||
private val hdb : MutableMap<String,Database> = mutableMapOf()
|
||||
private val htrans: MutableMap<String,Translation> = mutableMapOf()
|
||||
|
||||
private val halpha: MutableMap<String,Language> = mutableMapOf()
|
||||
private val dictById: MutableMap<Int,Dictionary> = mutableMapOf()
|
||||
private val hLang1: MutableMap<Int,Language> = mutableMapOf()
|
||||
fun connectAndMigrate() {
|
||||
log.info("Initialising database")
|
||||
val pool = hikari()
|
||||
@@ -43,10 +47,17 @@ object DatabaseFactory {
|
||||
return HikariDataSource(config)
|
||||
}
|
||||
|
||||
fun getTranslationForLanguages(l1: String, l2: String): Translation? = htrans["${l1}${l2}"]?: null
|
||||
fun getTranslationForLanguages(l1: String, l2: String): Translation? = htrans["${l1.lowercase()}${l2.lowercase()}"]?: null
|
||||
fun getLanguageForCode(l: String): Language? = halpha[l.uppercase()]?: null
|
||||
fun getLanguageForId(id: Int): Language? = hLang1[id]?: null
|
||||
|
||||
fun getDictionaies(): Map<Int,Dictionary> = dictById
|
||||
fun getAllLanguages(): List<Language> {
|
||||
return halpha.values.toList()
|
||||
}
|
||||
fun connectAll() {
|
||||
if (getDictionaies().any()) return
|
||||
|
||||
fun connectAll(): Map<Int,Database> {
|
||||
val hMap : MutableMap<Int, Database> = mutableMapOf()
|
||||
transaction {
|
||||
for (trans in TranslationDao.all()) {
|
||||
val l1 = trans.lang1.shortName.lowercase()
|
||||
@@ -55,6 +66,11 @@ object DatabaseFactory {
|
||||
htrans["${l1}${l2}"] = trans.toModel()
|
||||
}
|
||||
|
||||
for (lang in LanguageDao.all()) {
|
||||
halpha[lang.shortName] = lang.toModel()
|
||||
hLang1[lang.id.value] = lang.toModel()
|
||||
}
|
||||
|
||||
for (dict in DictionaryDao.all()) {
|
||||
val cfg = ConfigFactory.load().getConfig("h2")
|
||||
val config = HikariConfig().apply {
|
||||
@@ -68,10 +84,14 @@ object DatabaseFactory {
|
||||
val db = HikariDataSource(config)
|
||||
val dbc = Database.connect(db)
|
||||
dbs[dict.id.value] = dbc
|
||||
dictById[dict.id.value] = dict.toModel()
|
||||
|
||||
hdb["${dict.lang1.shortName.lowercase()}${dict.lang2.shortName.lowercase()}"] = dbc
|
||||
}
|
||||
}
|
||||
return hMap
|
||||
println("DBS PRINT")
|
||||
println(dbs)
|
||||
|
||||
}
|
||||
|
||||
private fun runFlyway(datasource: DataSource) {
|
||||
|
||||
@@ -6,4 +6,7 @@ import models.Language
|
||||
interface LanguageService {
|
||||
fun getLanguage(id: Int): Language?
|
||||
fun getAllLanguages(): List<Language>
|
||||
|
||||
fun getAlphaCode4String(code: String): Language?
|
||||
fun getAlphaCode4Lang(lang: Language): Language?
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import dao.LanguageDao
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import service.DatabaseFactory.dbExecId
|
||||
import service.DatabaseFactory.getLanguageForCode
|
||||
import models.Language
|
||||
|
||||
class LanguageServiceImpl : LanguageService {
|
||||
@@ -14,4 +15,12 @@ class LanguageServiceImpl : LanguageService {
|
||||
LanguageDao.all().map { it.toModel() }
|
||||
}
|
||||
|
||||
override fun getAlphaCode4String(code: String): Language? {
|
||||
return getLanguageForCode(code)
|
||||
}
|
||||
|
||||
override fun getAlphaCode4Lang(lang: Language): Language? {
|
||||
return getLanguageForCode(lang.shortName)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class TermServiceImpl : TermService {
|
||||
if (type == SearchType.EXACT)
|
||||
TermDao.find { Terms.string2 eq s }.map { it.toFullModel() }
|
||||
else
|
||||
TermDao.find { Terms.string1 like s }.map { it.toFullModel() }
|
||||
TermDao.find { Terms.string2 like s }.map { it.toFullModel() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@ object Languages : IntIdTable() {
|
||||
|
||||
val name = varchar("name", 255)
|
||||
val shortName = varchar("short_name", 255)
|
||||
|
||||
val alphaCode = char("alpha_code",2).nullable()
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import tables.Suffixes
|
||||
object Terms : IntIdTable() {
|
||||
val string1 = varchar("string1", 255)
|
||||
val string2 = varchar("string2", 255)
|
||||
val string1 = varchar("string1", 255).index()
|
||||
val string2 = varchar("string2", 255).index()
|
||||
val suffix1 = reference("suffix1_id",Suffixes).nullable()
|
||||
val suffix2 = reference("suffix2_id",Suffixes).nullable()
|
||||
val type = reference("type_id",DictTypes).nullable()
|
||||
|
||||
Reference in New Issue
Block a user