First commit

This commit is contained in:
2025-01-19 11:07:46 +01:00
commit 8902b17d66
112 changed files with 3476 additions and 0 deletions

34
.fleet/receipt.json Normal file
View File

@@ -0,0 +1,34 @@
// Project generated by Kotlin Multiplatform Wizard
{
"spec": {
"template_id": "kmt",
"targets": {
"android": {
"ui": [
"compose"
]
},
"ios": {
"ui": [
"compose"
]
},
"desktop": {
"ui": [
"compose"
]
},
"web": {
"ui": [
"compose"
]
},
"server": {
"engine": [
"ktor"
]
}
}
},
"timestamp": "2025-01-14T15:21:30.946900300Z"
}

18
.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
*.iml
.kotlin
.gradle
**/build/
xcuserdata
!src/**/build/
local.properties
.idea
.DS_Store
captures
.externalNativeBuild
.cxx
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
**/xcshareddata/WorkspaceSettings.xcsettings

26
README.md Normal file
View File

@@ -0,0 +1,26 @@
This is a Kotlin Multiplatform project targeting Android, iOS, Web, Desktop, Server.
* `/composeApp` is for code that will be shared across your Compose Multiplatform applications.
It contains several subfolders:
- `commonMain` is for code thats common for all targets.
- Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name.
For example, if you want to use Apples CoreCrypto for the iOS part of your Kotlin app,
`iosMain` would be the right folder for such calls.
* `/iosApp` contains iOS applications. Even if youre sharing your UI with Compose Multiplatform,
you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.
* `/server` is for the Ktor server application.
* `/shared` is for the code that will be shared between all targets in the project.
The most important subfolder is `commonMain`. If preferred, you can add code to the platform-specific folders here too.
Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html),
[Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/#compose-multiplatform),
[Kotlin/Wasm](https://kotl.in/wasm/)…
We would appreciate your feedback on Compose/Web and Kotlin/Wasm in the public Slack channel [#compose-web](https://slack-chats.kotlinlang.org/c/compose-web).
If you face any issues, please report them on [GitHub](https://github.com/JetBrains/compose-multiplatform/issues).
You can open the web application by running the `:composeApp:wasmJsBrowserDevelopmentRun` Gradle task.

13
build.gradle.kts Normal file
View File

@@ -0,0 +1,13 @@
plugins {
// this is necessary to avoid the plugins to be loaded multiple times
// in each subproject's classloader
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.androidLibrary) apply false
alias(libs.plugins.composeMultiplatform) apply false
alias(libs.plugins.composeCompiler) apply false
alias(libs.plugins.kotlinJvm) apply false
alias(libs.plugins.kotlinMultiplatform) apply false
kotlin("plugin.serialization") version "1.9.22" apply false
}

122
composeApp/build.gradle.kts Normal file
View File

@@ -0,0 +1,122 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
}
kotlin {
androidTarget {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "ComposeApp"
isStatic = true
}
}
jvm("desktop")
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = "composeApp"
browser {
val rootDirPath = project.rootDir.path
val projectDirPath = project.projectDir.path
commonWebpackConfig {
outputFileName = "composeApp.js"
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
static = (static ?: mutableListOf()).apply {
// Serve sources to debug inside browser
add(rootDirPath)
add(projectDirPath)
}
}
}
}
binaries.executable()
}
sourceSets {
val desktopMain by getting
androidMain.dependencies {
implementation(compose.preview)
implementation(libs.androidx.activity.compose)
}
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(projects.shared)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
implementation(libs.kotlinx.coroutines.swing)
}
}
}
android {
namespace = "org.neutrino.ktans"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
applicationId = "org.neutrino.ktans"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
debugImplementation(compose.uiTooling)
}
compose.desktop {
application {
mainClass = "org.neutrino.ktans.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "org.neutrino.ktans"
packageVersion = "1.0.0"
}
}
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<activity
android:exported="true"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|mnc|colorMode|density|fontScale|fontWeightAdjustment|keyboard|layoutDirection|locale|mcc|navigation|smallestScreenSize|touchscreen|uiMode"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,23 @@
package org.neutrino.ktans
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App()
}
}
}
@Preview
@Composable
fun AppAndroidPreview() {
App()
}

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">KTrans</string>
</resources>

View File

@@ -0,0 +1,36 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="600dp"
android:height="600dp"
android:viewportWidth="600"
android:viewportHeight="600">
<path
android:pathData="M301.21,418.53C300.97,418.54 300.73,418.56 300.49,418.56C297.09,418.59 293.74,417.72 290.79,416.05L222.6,377.54C220.63,376.43 219,374.82 217.85,372.88C216.7,370.94 216.09,368.73 216.07,366.47L216.07,288.16C216.06,287.32 216.09,286.49 216.17,285.67C216.38,283.54 216.91,281.5 217.71,279.6L199.29,268.27L177.74,256.19C175.72,260.43 174.73,265.23 174.78,270.22L174.79,387.05C174.85,393.89 178.57,400.2 184.53,403.56L286.26,461.02C290.67,463.51 295.66,464.8 300.73,464.76C300.91,464.76 301.09,464.74 301.27,464.74C301.24,449.84 301.22,439.23 301.22,439.23L301.21,418.53Z"
android:fillColor="#041619"
android:fillType="nonZero"/>
<path
android:pathData="M409.45,242.91L312.64,188.23C303.64,183.15 292.58,183.26 283.68,188.51L187.92,245C183.31,247.73 179.93,251.62 177.75,256.17L177.74,256.19L199.29,268.27L217.71,279.6C217.83,279.32 217.92,279.02 218.05,278.74C218.24,278.36 218.43,277.98 218.64,277.62C219.06,276.88 219.52,276.18 220.04,275.51C221.37,273.8 223.01,272.35 224.87,271.25L289.06,233.39C290.42,232.59 291.87,231.96 293.39,231.51C295.53,230.87 297.77,230.6 300,230.72C302.98,230.88 305.88,231.73 308.47,233.2L373.37,269.85C375.54,271.08 377.49,272.68 379.13,274.57C379.68,275.19 380.18,275.85 380.65,276.53C380.86,276.84 381.05,277.15 381.24,277.47L397.79,266.39L420.34,252.93L420.31,252.88C417.55,248.8 413.77,245.35 409.45,242.91Z"
android:fillColor="#37BF6E"
android:fillType="nonZero"/>
<path
android:pathData="M381.24,277.47C381.51,277.92 381.77,278.38 382.01,278.84C382.21,279.24 382.39,279.65 382.57,280.06C382.91,280.88 383.19,281.73 383.41,282.59C383.74,283.88 383.92,285.21 383.93,286.57L383.93,361.1C383.96,363.95 383.35,366.77 382.16,369.36C381.93,369.86 381.69,370.35 381.42,370.83C379.75,373.79 377.32,376.27 374.39,378L310.2,415.87C307.47,417.48 304.38,418.39 301.21,418.53L301.22,439.23C301.22,439.23 301.24,449.84 301.27,464.74C306.1,464.61 310.91,463.3 315.21,460.75L410.98,404.25C419.88,399 425.31,389.37 425.22,379.03L425.22,267.85C425.17,262.48 423.34,257.34 420.34,252.93L397.79,266.39L381.24,277.47Z"
android:fillColor="#3870B2"
android:fillType="nonZero"/>
<path
android:pathData="M177.75,256.17C179.93,251.62 183.31,247.73 187.92,245L283.68,188.51C292.58,183.26 303.64,183.15 312.64,188.23L409.45,242.91C413.77,245.35 417.55,248.8 420.31,252.88L420.34,252.93L498.59,206.19C494.03,199.46 487.79,193.78 480.67,189.75L320.86,99.49C306.01,91.1 287.75,91.27 273.07,99.95L114.99,193.2C107.39,197.69 101.81,204.11 98.21,211.63L177.74,256.19L177.75,256.17ZM301.27,464.74C301.09,464.74 300.91,464.76 300.73,464.76C295.66,464.8 290.67,463.51 286.26,461.02L184.53,403.56C178.57,400.2 174.85,393.89 174.79,387.05L174.78,270.22C174.73,265.23 175.72,260.43 177.74,256.19L98.21,211.63C94.86,218.63 93.23,226.58 93.31,234.82L93.31,427.67C93.42,438.97 99.54,449.37 109.4,454.92L277.31,549.77C284.6,553.88 292.84,556.01 301.2,555.94L301.2,555.8C301.39,543.78 301.33,495.26 301.27,464.74Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
<path
android:pathData="M498.59,206.19L420.34,252.93C423.34,257.34 425.17,262.48 425.22,267.85L425.22,379.03C425.31,389.37 419.88,399 410.98,404.25L315.21,460.75C310.91,463.3 306.1,464.61 301.27,464.74C301.33,495.26 301.39,543.78 301.2,555.8L301.2,555.94C309.48,555.87 317.74,553.68 325.11,549.32L483.18,456.06C497.87,447.39 506.85,431.49 506.69,414.43L506.69,230.91C506.6,222.02 503.57,213.5 498.59,206.19Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
<path
android:pathData="M301.2,555.94C292.84,556.01 284.6,553.88 277.31,549.76L109.4,454.92C99.54,449.37 93.42,438.97 93.31,427.67L93.31,234.82C93.23,226.58 94.86,218.63 98.21,211.63C101.81,204.11 107.39,197.69 114.99,193.2L273.07,99.95C287.75,91.27 306.01,91.1 320.86,99.49L480.67,189.75C487.79,193.78 494.03,199.46 498.59,206.19C503.57,213.5 506.6,222.02 506.69,230.91L506.69,414.43C506.85,431.49 497.87,447.39 483.18,456.06L325.11,549.32C317.74,553.68 309.48,555.87 301.2,555.94Z"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#083042"
android:fillType="nonZero"/>
</vector>

View File

@@ -0,0 +1,104 @@
package org.neutrino.ktans
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
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 db.migration.Translation
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.ui.tooling.preview.Preview
import ktrans.composeapp.generated.resources.Res
import ktrans.composeapp.generated.resources.compose_multiplatform
import models.TermFull
import service.DatabaseFactory.getTranslationForLanguages
import service.TermService
import service.TermServiceImpl
import service.SearchType
import service.DatabaseFactory
@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")
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
package org.neutrino.ktans
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
fun main() = application {
Window(
onCloseRequest = ::exitApplication,
title = "KTrans",
) {
App()
}
}

View File

@@ -0,0 +1,6 @@
h2 {
driver = org.h2.Driver
url = "jdbc:h2:file:./data/%s;DB_CLOSE_DELAY=-1;"
user = "sa"
password = ""
}

View File

@@ -0,0 +1,5 @@
package org.neutrino.ktans
import androidx.compose.ui.window.ComposeUIViewController
fun MainViewController() = ComposeUIViewController { App() }

View File

@@ -0,0 +1,12 @@
package org.neutrino.ktans
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.window.ComposeViewport
import kotlinx.browser.document
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
ComposeViewport(document.body!!) {
App()
}
}

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KTrans</title>
<link type="text/css" rel="stylesheet" href="styles.css">
<script type="application/javascript" src="composeApp.js"></script>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,7 @@
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}

13
gradle.properties Normal file
View File

@@ -0,0 +1,13 @@
#Kotlin
kotlin.code.style=official
kotlin.daemon.jvmargs=-Xmx2048M
#Gradle
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
#Android
android.nonTransitiveRClass=true
android.useAndroidX=true
#Ktor
io.ktor.development=true

131
gradle/libs.versions.toml Normal file
View File

@@ -0,0 +1,131 @@
[versions]
agp = "8.5.2"
android-compileSdk = "34"
android-minSdk = "24"
android-targetSdk = "34"
androidx-activityCompose = "1.9.3"
androidx-appcompat = "1.7.0"
androidx-constraintlayout = "2.2.0"
androidx-core-ktx = "1.15.0"
androidx-espresso-core = "3.6.1"
androidx-lifecycle = "2.8.4"
androidx-material = "1.12.0"
androidx-test-junit = "1.2.1"
assertjVersion = "3.26.3"
compose-multiplatform = "1.7.0"
exposedVersion = "0.55.0"
flywayVersion = "10.20.1"
h2 = "2.3.232"
hikariCpVersion = "5.1.0"
junit = "4.13.2"
junitVersion = "5.11.3"
kformat = "0.11"
kotlin = "2.1.0"
kotlinSerializationCompilerPluginEmbeddable = "1.9.22"
kotlinx-coroutines = "1.9.0"
kotlinxSerializationCore = "1.8.0"
kotlinxSerializationJson = "1.8.0"
kotlinxSerializationJsonJvm = "1.5.0"
kotlinxSerializationJsonVersion = "1.6.0"
kotlinxSerializationRuntime = "0.14.0"
kotliquery = "1.9.0"
ktor = "3.0.3"
ktorClientCio = "3.0.3"
ktorClientContentNegotiation = "3.0.3"
ktorClientContentNegotiationVersion = "3.0.3"
ktorClientCore = "3.0.3"
ktorClientLogging = "3.0.3"
ktorSerializationKotlinxCbor = "3.0.3"
ktorSerializationKotlinxJson = "3.0.3"
ktorSerializationKotlinxJsonVersion = "3.0.2"
ktorSerializationKotlinxProtobuf = "3.0.3"
ktorSerializationKotlinxXml = "3.0.3"
ktorServerContentNegotiation = "3.0.2"
ktorServerCore = "3.0.2"
ktorServerCors = "3.0.2"
ktorServerNetty = "3.0.2"
ktorServerRequestValidation = "3.0.2"
logback = "1.5.12"
material = "1.7.6"
psqlVersion = "42.7.4"
restAssuredVersion = "5.5.0"
[libraries]
assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertjVersion" }
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposedVersion" }
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposedVersion" }
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposedVersion" }
exposed-json = { module = "org.jetbrains.exposed:exposed-json", version.ref = "exposedVersion" }
flyway-core = { module = "org.flywaydb:flyway-core", version.ref = "flywayVersion" }
h2 = { module = "com.h2database:h2", version.ref = "h2" }
hikaricp = { module = "com.zaxxer:HikariCP", version.ref = "hikariCpVersion" }
io-ktor-ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
io-ktor-ktor-server-netty = { module = "io.ktor:ktor-server-netty" }
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junitVersion" }
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junitVersion" }
kformat = { module = "de.m3y.kformat:kformat", version.ref = "kformat" }
kotlin-serialization-compiler-plugin-embeddable = { module = "org.jetbrains.kotlin:kotlin-serialization-compiler-plugin-embeddable", version.ref = "kotlinSerializationCompilerPluginEmbeddable" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" }
androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerializationCore" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
kotlinx-serialization-json-jvm = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-jvm", version.ref = "kotlinxSerializationJsonJvm" }
kotlinx-serialization-json-v160 = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJsonVersion" }
kotlinx-serialization-runtime = { module = "org.jetbrains.kotlinx:kotlinx-serialization-runtime", version.ref = "kotlinxSerializationRuntime" }
kotliquery = { module = "com.github.seratch:kotliquery", version.ref = "kotliquery" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
ktor-client-cio-v180 = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientCio" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktorClientContentNegotiation" }
ktor-client-content-negotiation-v180 = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktorClientContentNegotiationVersion" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientCore" }
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktorClientLogging" }
ktor-ktor-server-core = { module = "io.ktor:ktor-server-core" }
ktor-ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
ktor-serialization = { module = "io.ktor:ktor-serialization", version.ref = "ktor" }
ktor-serialization-kotlinx-cbor = { module = "io.ktor:ktor-serialization-kotlinx-cbor", version.ref = "ktorSerializationKotlinxCbor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktor-serialization-kotlinx-json-v180 = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktorSerializationKotlinxJson" }
ktor-serialization-kotlinx-json-v302 = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktorSerializationKotlinxJsonVersion" }
ktor-serialization-kotlinx-protobuf = { module = "io.ktor:ktor-serialization-kotlinx-protobuf", version.ref = "ktorSerializationKotlinxProtobuf" }
ktor-serialization-kotlinx-xml = { module = "io.ktor:ktor-serialization-kotlinx-xml", version.ref = "ktorSerializationKotlinxXml" }
ktor-server-call-logging = { module = "io.ktor:ktor-server-call-logging", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
ktor-server-content-negotiation-v302 = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktorServerContentNegotiation" }
ktor-server-core-jvm = { module = "io.ktor:ktor-server-core-jvm" }
ktor-server-core-v302 = { module = "io.ktor:ktor-server-core", version.ref = "ktorServerCore" }
ktor-server-cors = { module = "io.ktor:ktor-server-cors", version.ref = "ktorServerCors" }
ktor-server-default-headers = { module = "io.ktor:ktor-server-default-headers", version.ref = "ktor" }
ktor-server-netty-jvm = { module = "io.ktor:ktor-server-netty-jvm" }
ktor-server-netty-v302 = { module = "io.ktor:ktor-server-netty", version.ref = "ktorServerNetty" }
ktor-server-request-validation = { module = "io.ktor:ktor-server-request-validation", version.ref = "ktorServerRequestValidation" }
ktor-server-test-host-jvm = { module = "io.ktor:ktor-server-test-host-jvm" }
ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.ref = "ktor" }
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" }
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" }
postgresql = { module = "org.postgresql:postgresql", version.ref = "psqlVersion" }
rest-assured = { module = "io.rest-assured:rest-assured", version.ref = "restAssuredVersion" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

252
gradlew vendored Executable file
View File

@@ -0,0 +1,252 @@
#!/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.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# 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/HEAD/platforms/jvm/plugins-application/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
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# 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
if ! command -v java >/dev/null 2>&1
then
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
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
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
# 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"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# 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" "$@"

94
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,94 @@
@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
@rem SPDX-License-Identifier: Apache-2.0
@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=.
@rem This is normally unused
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% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
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% equ 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!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,3 @@
TEAM_ID=
BUNDLE_ID=org.neutrino.ktans.KTrans
APP_NAME=KTrans

View File

@@ -0,0 +1,393 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
7555FF7B242A565900829871 /* KTrans.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KTrans.app; sourceTree = BUILT_PRODUCTS_DIR; };
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B92378962B6B1156000C7307 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
058557D7273AAEEB004C7B11 /* Preview Content */ = {
isa = PBXGroup;
children = (
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
42799AB246E5F90AF97AA0EF /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
7555FF72242A565900829871 = {
isa = PBXGroup;
children = (
AB1DB47929225F7C00F7AF9C /* Configuration */,
7555FF7D242A565900829871 /* iosApp */,
7555FF7C242A565900829871 /* Products */,
42799AB246E5F90AF97AA0EF /* Frameworks */,
);
sourceTree = "<group>";
};
7555FF7C242A565900829871 /* Products */ = {
isa = PBXGroup;
children = (
7555FF7B242A565900829871 /* KTrans.app */,
);
name = Products;
sourceTree = "<group>";
};
7555FF7D242A565900829871 /* iosApp */ = {
isa = PBXGroup;
children = (
058557BA273AAA24004C7B11 /* Assets.xcassets */,
7555FF82242A565900829871 /* ContentView.swift */,
7555FF8C242A565B00829871 /* Info.plist */,
2152FB032600AC8F00CF470E /* iOSApp.swift */,
058557D7273AAEEB004C7B11 /* Preview Content */,
);
path = iosApp;
sourceTree = "<group>";
};
AB1DB47929225F7C00F7AF9C /* Configuration */ = {
isa = PBXGroup;
children = (
AB3632DC29227652001CCB65 /* Config.xcconfig */,
);
path = Configuration;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
7555FF7A242A565900829871 /* iosApp */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
buildPhases = (
F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */,
7555FF77242A565900829871 /* Sources */,
B92378962B6B1156000C7307 /* Frameworks */,
7555FF79242A565900829871 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = iosApp;
packageProductDependencies = (
);
productName = iosApp;
productReference = 7555FF7B242A565900829871 /* KTrans.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
7555FF73242A565900829871 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1130;
LastUpgradeCheck = 1540;
ORGANIZATIONNAME = orgName;
TargetAttributes = {
7555FF7A242A565900829871 = {
CreatedOnToolsVersion = 11.3.1;
};
};
};
buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 7555FF72242A565900829871;
packageReferences = (
);
productRefGroup = 7555FF7C242A565900829871 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
7555FF7A242A565900829871 /* iosApp */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
7555FF79242A565900829871 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Compile Kotlin Framework";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
7555FF77242A565900829871 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
7555FF83242A565900829871 /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
7555FFA3242A565B00829871 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
7555FFA4242A565B00829871 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
7555FFA6242A565B00829871 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}";
ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
);
INFOPLIST_FILE = iosApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
7555FFA7242A565B00829871 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}";
ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
);
INFOPLIST_FILE = iosApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7555FFA3242A565B00829871 /* Debug */,
7555FFA4242A565B00829871 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7555FFA6242A565B00829871 /* Debug */,
7555FFA7242A565B00829871 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 7555FF73242A565900829871 /* Project object */;
}

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "app-icon-1024.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,21 @@
import UIKit
import SwiftUI
import ComposeApp
struct ComposeView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
MainViewControllerKt.MainViewController()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
struct ContentView: View {
var body: some View {
ComposeView()
.ignoresSafeArea(.keyboard) // Compose has own keyboard handler
}
}

50
iosApp/iosApp/Info.plist Normal file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
</dict>
<key>UILaunchScreen</key>
<dict/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,10 @@
import SwiftUI
@main
struct iOSApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

51
server/build.gradle.kts Normal file
View File

@@ -0,0 +1,51 @@
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin.Companion.kotlinNodeJsEnvSpec
plugins {
alias(libs.plugins.kotlinJvm)
alias(libs.plugins.ktor)
application
}
group = "org.neutrino.ktans"
version = "1.0.0"
application {
mainClass.set("org.neutrino.ktans.ApplicationKt")
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}")
}
dependencies {
implementation(libs.ktor.server.core.jvm)
implementation(libs.ktor.server.netty.jvm)
implementation(libs.ktor.server.netty.jvm)
implementation(libs.kotlinx.serialization.json.jvm)
implementation(libs.kotlinx.serialization.core)
implementation(libs.ktor.server.cors)
implementation(libs.ktor.server.netty)
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.request.validation)
implementation(libs.ktor.server.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(projects.shared)
implementation(libs.ktor.server.call.logging)
implementation(libs.ktor.server.default.headers)
implementation(libs.ktor.server.websockets)
implementation(libs.kotlin.stdlib)
implementation(libs.kotliquery)
implementation(libs.h2)
implementation(libs.postgresql)
implementation(libs.exposed.core)
implementation(libs.exposed.dao)
implementation(libs.exposed.jdbc)
implementation(libs.exposed.json)
implementation(libs.hikaricp)
implementation(libs.flyway.core)
implementation(libs.logback)
implementation(libs.kformat)
testImplementation(libs.ktor.server.tests)
testImplementation(libs.kotlin.test.junit)
}

View File

@@ -0,0 +1,69 @@
package org.neutrino.ktans
import dao.DictionaryDao
import dao.PronunciationDao
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.calllogging.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.defaultheaders.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory
import util.JsonMapper
import web.index
import service.DatabaseFactory.dbExecId
import service.DatabaseFactory.dbExecName
import service.DictTypeService
import service.DictTypeServiceImpl
import service.DictionaryServiceImpl
import service.PronunciationServiceImpl
import service.LanguageServiceImpl
import service.TranslationServiceImpl
import service.TermServiceImpl
import web.dictType
import web.dictionary
import web.pronunciation
import web.language
import web.translation
import web.term
fun Application.module() {
install(DefaultHeaders)
install(CallLogging)
install(ContentNegotiation) {
json(JsonMapper.defaultMapper)
}
DatabaseFactory.connectAndMigrate()
DatabaseFactory.connectAll()
val dictTypeService = DictTypeServiceImpl()
val dictionaryService = DictionaryServiceImpl()
val pronunciationService = PronunciationServiceImpl()
val languageService = LanguageServiceImpl()
val translationService = TranslationServiceImpl()
val termService = TermServiceImpl()
routing {
index()
dictType(dictTypeService)
dictionary(dictionaryService)
pronunciation(pronunciationService)
language(languageService)
translation(translationService,termService)
term(termService)
}
}
fun main(args: Array<String>) {
EngineMain.main(args)
}

View File

@@ -0,0 +1,17 @@
ktor {
deployment {
port = 8080
watch = [ build ]
}
application {
modules = [ org.neutrino.ktans.ApplicationKt.module ]
}
}
h2 {
driver = org.h2.Driver
url = "jdbc:h2:file:./data/%s;DB_CLOSE_DELAY=-1;"
user = "sa"
password = ""
}

View File

@@ -0,0 +1,53 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Kotlin/Ktor/Exposed Starter - It's Working!</title>
<meta name="author" content="Ryan Harrison">
</head>
<body>
<h1 id="it-s-working-">It&#39;s Working!</h1>
<p>This starter project creates a new in-memory H2 database with one table for <code>Widget</code> instances. A simple RESTful interface is provided
to perform CRUD operations on <code>Widgets</code> alongside a websocket to be notified in real-time of any changes.</p>
<h2 id="routes-">Routes:</h2>
<p><code>GET /widgets</code> --&gt; get all widgets in the database</p>
<p><code>GET /widgets/{id}</code> --&gt; get one widget instance by id (integer)</p>
<p><code>POST /widgets</code> --&gt; add a new widget to the database by providing a JSON object (converted to a NewWidget instance).
e.g - </p>
<pre><code>{
<span class="hljs-attr">"name"</span>: <span class="hljs-string">"new widget"</span>,
<span class="hljs-attr">"quantity"</span>: <span class="hljs-number">64</span>
}
</code></pre>
<p>returns</p>
<pre><code>{
<span class="hljs-attr">"id"</span>: <span class="hljs-number">4</span>,
<span class="hljs-attr">"name"</span>: <span class="hljs-string">"new widget"</span>,
<span class="hljs-attr">"quantity"</span>: <span class="hljs-number">64</span>,
<span class="hljs-attr">"dateCreated"</span>: <span class="hljs-number">1519926898</span>
}
</code></pre>
<p><code>PUT /widgets</code> --&gt; update an existing widgets name or quantity. Pass in the id in the JSON request to determine which record to update
</p>
<p><code>DELETE /widgets/{id}</code> --&gt; delete the widget with the specified id</p>
<h2 id="notifications-websocket-">Notifications (WebSocket)</h2>
<p>All updates (creates, updates and deletes) to <code>Widget</code> instances are served as notifications through a WebSocket endpoint:</p>
<p><code>WS /updates</code> --&gt; returns <code>Notification</code> instances containing the change type, id and entity (if applicable) e.g:</p>
<pre><code class="lang-json">{
<span class="hljs-attr">"type"</span>: <span class="hljs-string">"CREATE"</span>,
<span class="hljs-attr">"id"</span>: <span class="hljs-number">12</span>,
<span class="hljs-attr">"entity"</span>: {
<span class="hljs-attr">"id"</span>: <span class="hljs-number">12</span>,
<span class="hljs-attr">"name"</span>: <span class="hljs-string">"widget1"</span>,
<span class="hljs-attr">"quantity"</span>: <span class="hljs-number">5</span>,
<span class="hljs-attr">"dateUpdated"</span>: <span class="hljs-number">1533583858169</span>
}
}
</code></pre>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>

33
settings.gradle.kts Normal file
View File

@@ -0,0 +1,33 @@
rootProject.name = "KTrans"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement {
repositories {
google {
mavenContent {
includeGroupAndSubgroups("androidx")
includeGroupAndSubgroups("com.android")
includeGroupAndSubgroups("com.google")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
google {
mavenContent {
includeGroupAndSubgroups("androidx")
includeGroupAndSubgroups("com.android")
includeGroupAndSubgroups("com.google")
}
}
mavenCentral()
}
}
include(":composeApp")
include(":server")
include(":shared")

114
shared/build.gradle.kts Normal file
View File

@@ -0,0 +1,114 @@
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
val ktorVersion = "3.0.2"
val exposedVersion = "0.55.0"
val h2Version = "2.3.232"
val hikariCpVersion = "5.1.0"
val flywayVersion = "10.20.1"
val logbackVersion = "1.5.12"
val assertjVersion = "3.26.3"
val restAssuredVersion = "5.5.0"
val junitVersion = "5.11.3"
val psqlVersion = "42.7.4"
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
kotlin("plugin.serialization")
}
repositories {
mavenCentral()
}
dependencies {
}
kotlin {
androidTarget {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
iosX64()
iosArm64()
iosSimulatorArm64()
jvm()
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser {
val rootDirPath = project.rootDir.path
val projectDirPath = project.projectDir.path
commonWebpackConfig {
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
static = (static ?: mutableListOf()).apply {
// Serve sources to debug inside browser
add(rootDirPath)
add(projectDirPath)
}
}
}
}
}
sourceSets {
commonMain.dependencies {
// put your Multiplatform dependencies here
implementation(libs.kotlinx.serialization.json.jvm)
implementation(libs.kotlinx.serialization.core)
implementation(libs.io.ktor.ktor.server.core)
implementation(libs.ktor.serialization)
implementation(libs.ktor.ktor.server.netty)
implementation(libs.ktor.server.call.logging)
implementation(libs.ktor.server.default.headers)
implementation(libs.ktor.server.websockets)
implementation(libs.ktor.server.content.negotiation)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.ktor.serialization)
implementation(libs.kotlinx.serialization.core)
implementation(libs.kotlin.stdlib)
implementation(libs.kotliquery)
implementation(libs.h2)
implementation(libs.postgresql)
implementation(libs.exposed.core)
implementation(libs.exposed.dao)
implementation(libs.exposed.jdbc)
implementation(libs.exposed.json)
implementation(libs.hikaricp)
implementation(libs.flyway.core)
implementation(libs.logback)
implementation(libs.kformat)
}
}
}
android {
namespace = "org.neutrino.ktans.shared"
compileSdk = libs.versions.android.compileSdk.get().toInt()
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
}

View File

@@ -0,0 +1,9 @@
package org.neutrino.ktans
import android.os.Build
class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"
}
actual fun getPlatform(): Platform = AndroidPlatform()

View File

@@ -0,0 +1,3 @@
package org.neutrino.ktans
const val SERVER_PORT = 8080

View File

@@ -0,0 +1,9 @@
package org.neutrino.ktans
class Greeting {
private val platform = getPlatform()
fun greet(): String {
return "Hello, ${platform.name}!"
}
}

View File

@@ -0,0 +1,7 @@
package org.neutrino.ktans
interface Platform {
val name: String
}
expect fun getPlatform(): Platform

View File

@@ -0,0 +1,18 @@
package dao
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.DictTypes
import models.DictType
class DictTypeDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<DictTypeDao>(DictTypes)
var shortName by DictTypes.shortName
var fullName by DictTypes.fullName
fun toModel() = DictType(
id = id.value,
shortName = shortName,
fullName = fullName
)
}

View File

@@ -0,0 +1,25 @@
package dao
import dao.SuffixDao.Companion.referrersOn
import models.Language
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.*
import models.Dictionary
class DictionaryDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<DictionaryDao>(Dictionaries)
var name by Dictionaries.name
var fullName by Dictionaries.fullName
var lang1 by LanguageDao referencedOn Dictionaries.lang1
var lang2 by LanguageDao referencedOn Dictionaries.lang2
fun toModel(): Dictionary = Dictionary(
id = id.value,
name = name,
fullName = fullName,
lang1Id = lang1.id.value,
lang2Id = lang2.id.value
)
}

View File

@@ -0,0 +1,18 @@
package dao
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.*
import models.Language
class LanguageDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<LanguageDao>(Languages)
var name by Languages.name
var shortName by Languages.shortName
fun toModel() : Language = Language(
id = id.value,
name = name,
shortName = shortName
)
}

View File

@@ -0,0 +1,22 @@
package dao
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.Pronunciations
import models.Pronunciation
class PronunciationDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<PronunciationDao>(Pronunciations)
var ipa by Pronunciations.ipa
var filename by Pronunciations.filename
var type by PronunciationTypeDao referencedOn Pronunciations.typeId
fun toModel() : Pronunciation = Pronunciation(
id = id.value,
typeId = type.id.value,
ipa = ipa,
filename = filename
)
}

View File

@@ -0,0 +1,12 @@
package dao
import models.Translation
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.PronunciationTypes
class PronunciationTypeDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<PronunciationTypeDao>(PronunciationTypes)
var name by PronunciationTypes.name
}

View File

@@ -0,0 +1,16 @@
package dao
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.Suffixes
import models.Suffix
class SuffixDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<SuffixDao>(Suffixes)
var text by Suffixes.text
fun toModel(): Suffix = Suffix(
id = id.value,
text = text
)
}

View File

@@ -0,0 +1,47 @@
package dao
import dao.SuffixDao.Companion.referrersOn
import models.Language
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.*
import models.Term
import models.TermFull
class TermDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<TermDao>(Terms)
var string1 by Terms.string1
var string2 by Terms.string2
var suffix1 by SuffixDao optionalReferencedOn Terms.suffix1
var suffix2 by SuffixDao optionalReferencedOn Terms.suffix2
var type by DictTypeDao optionalReferencedOn Terms.type
var member by Terms.member
var order2 by Terms.order2
//var flags by Terms.flags
var pronunciations by PronunciationDao via TermsPronunciations
fun toModel(): Term = Term(
id = id.value,
string1 = string1,
string2 = string2,
typeId = type?.id?.value,
suffix1Id = suffix1?.id?.value,
suffix2Id = suffix2?.id?.value,
member = member,
order2 = order2
//flags = flags
)
fun toFullModel(): TermFull = TermFull(
id = id.value,
string1 = string1,
string2 = string2,
type = type?.toModel(),
suffix1 = suffix1?.toModel(),
suffix2 = suffix2?.toModel(),
member = member,
order2 = order2,
//flags = flags,
pronunciations = pronunciations.map { it.toModel() }
)
}

View File

@@ -0,0 +1,27 @@
package dao
import models.Translation
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.Translations
class TranslationDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<TranslationDao>(Translations)
var langName1 by Translations.langName1
var langName2 by Translations.langName2
var direction by Translations.direction
var dictionary by DictionaryDao referencedOn Translations.dictionaryId
var lang1 by LanguageDao referencedOn Translations.lang1Id
var lang2 by LanguageDao referencedOn Translations.lang2Id
fun toModel() : Translation = Translation(
id = id.value,
dictionaryId = dictionary.id.value,
lang1Id = lang1.id.value,
lang2Id = lang2.id.value,
lang1Name = langName1,
lang2Name = langName2,
direction = direction
)
}

View File

@@ -0,0 +1,197 @@
package db.migration
import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.transactions.transaction
import kotlin.collections.*
import kotliquery.*
import de.m3y.kformat.*
import de.m3y.kformat.Table.Hints
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.Json
val format = Json { encodeDefaults = true }
data class Dictionary(
val id: Int,
val lang1Id: Int,
val lang2Id: Int,
val name: String,
val fullName: String)
data class DictType(
val id: Int,
val shortName: String,
val fullName: String
)
data class Language(
val id: Int,
val name: String,
val shortName: String
)
data class Translation(
val id: Int,
val dictionaryId: Int,
val lang1Id: Int,
val lang2Id: Int,
val lang1Name: String,
val lang2Name: String,
val direction: Int
)
data class Pronunciation(
val id: Int,
val typeId: Int,
val ipa: String?,
val filename: String?
)
data class PronunciationType(
val id: Int,
val name: String?
)
data class TermsPronunciation(
val termId: Int,
val pronunciationId: Int,
)
data class Suffix(
val id: Int,
val text: String
)
data class Term(
val id: Int,
val dictionaryId: Int,
val string1: String,
val string2: String,
val suffix1Id: Int?,
val suffix2Id: Int?,
val typeId: Int?,
val member: String?,
val order2: Int?,
val flags: String?
)
val toDictionary: (Row) -> Dictionary = { row ->
Dictionary(
row.int("id"),
row.int("lang1_id"),
row.int("lang2_id"),
row.string("name"),
row.string("full_name")
)
}
val toDictType: (Row) -> DictType = { row ->
DictType(
row.int("id"),
row.string("short_name"),
row.string("full_name")
)
}
val toLanguage: (Row) -> Language = { row ->
Language(
row.int("id"),
row.string("name"),
row.string("short_name")
)
}
val toTranslation: (Row) -> Translation = { row ->
Translation(
row.int("id"),
row.int("dictionary_id"),
row.int("lang1_id"),
row.int("lang2_id"),
row.string("lang_name1"),
row.string("lang_name2"),
row.int("direction"),
)
}
val toPronunciation: (Row) -> Pronunciation = { row ->
Pronunciation(
row.int("id"),
row.int("type_id"),
row.stringOrNull("ipa"),
row.stringOrNull("filename")
)
}
val toTermsPronunciation: (Row) -> TermsPronunciation = { row ->
TermsPronunciation(
row.int("term_id"),
row.int("pronunciation_id"),
)
}
val toSuffix: (Row) -> Suffix = { row ->
Suffix(
row.int("id"),
row.string("text")
)
}
val toTerm: (Row) -> Term = { row ->
Term(
row.int("id"),
row.int("dictionary_id"),
row.string("string1"),
row.string("string2"),
row.intOrNull("suffix1_id"),
row.intOrNull("suffix2_id"),
row.intOrNull("type_id"),
row.stringOrNull("member"),
row.intOrNull("order2"),
row.stringOrNull("flags")
)
}
object SharedData {
var hashDict: Map<Int,Dictionary> = mapOf()
var hashDictType: Map<Int,DictType> = mapOf()
var hashLang: Map<Int,Language> = mapOf()
var hashTransMap: Map<Int,Translation> = mapOf()
var hashPron: Map<Int,Pronunciation> = mapOf()
var hashSuffix: Map<Int,Suffix> = mapOf()
var hashTermPron : MutableMap<Int,Array<Int>> = mutableMapOf()
var listTerm : List<Term> = listOf()
var getListofTerms : (Int) -> List<Term> = {
listOf()
}
}
class V1__test_connection: BaseJavaMigration() {
override fun migrate(context: Context?) {
val session = sessionOf("jdbc:postgresql://nuc.lan:5432/dict", "dict_user", "PW4dbdict")
val allNameQuery = queryOf("select * from dictionary").map(toDictionary).asList
val allDict = session.run(allNameQuery)
SharedData.hashDict = allDict.associateBy { it.id }
val allDictTypeQuery = queryOf("select * from dict_type").map(toDictType).asList
val allDictType = session.run(allDictTypeQuery)
SharedData.hashDictType = allDictType.associateBy { it.id }
val allTranslationQuery = queryOf("select * from translation").map(toTranslation).asList
val allTranslation = session.run(allTranslationQuery)
SharedData.hashTransMap = allTranslation.associateBy { it.id }
val allLangQuery = queryOf("select * from language").map(toLanguage).asList
val allLanguages = session.run(allLangQuery)
SharedData.hashLang = allLanguages.associateBy { it.id }
val allSuffixQuery = queryOf("select * from suffix").map(toSuffix).asList
val allSuffix = session.run(allSuffixQuery)
SharedData.hashSuffix = allSuffix.associateBy { it.id }
val allPronunciationQuery = queryOf("select * from pronunciation").map(toPronunciation).asList
val allPronunciation = session.run(allPronunciationQuery)
SharedData.hashPron = allPronunciation.associateBy { it.id }
val allTermsPronunciationQuery = queryOf("select * from terms_pronunciations").map(toTermsPronunciation).asList
val allTermsPronunciation = session.run(allTermsPronunciationQuery)
allTermsPronunciation.forEach {
val p = SharedData.hashTermPron.getOrDefault(it.termId, arrayOf())
SharedData.hashTermPron[it.termId] = p + it.pronunciationId
}
SharedData.getListofTerms = {
println("DB ID = ${it}")
session.run(queryOf("select * from term WHERE dictionary_id=${it}").map(toTerm).asList)
}
}
}

View File

@@ -0,0 +1,53 @@
package db.migration
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.SchemaUtils
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.upsert
class V2__create_settingsdb: BaseJavaMigration() {
override fun migrate(context: Context?) {
transaction {
SchemaUtils.create(Dictionaries, Languages, Translations, Settings)
for ((di, lang) in SharedData.hashLang) {
Languages.insert {
it[id] = lang.id
it[name] = lang.name
it[shortName] = lang.shortName
}
}
for ((di, dict) in SharedData.hashDict) {
Dictionaries.insert {
it[id] = dict.id
it[lang1] = dict.lang1Id
it[lang2] = dict.lang2Id
it[name] = dict.name
it[fullName] = dict.fullName
}
}
for ((di, trans) in SharedData.hashTransMap) {
Translations.insert {
it[id] = trans.id
it[lang1Id] = trans.lang1Id
it[lang2Id] = trans.lang2Id
it[dictionaryId] = trans.dictionaryId
it[langName1] = trans.lang1Name
it[langName2] = trans.lang2Name
it[direction] = trans.direction
}
}
}
}
}

View File

@@ -0,0 +1,145 @@
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 V3__create_dictionaries: BaseJavaMigration() {
override fun migrate(context: Context?) {
println(SharedData.hashDict)
//exitProcess(0)
for ((dbId,dict) in SharedData.hashDict) {
var dictTerms = SharedData.getListofTerms(dbId)
val dbName = "jdbc:h2:file:/Users/jaro/data/dict_${SharedData.hashLang[dict.lang1Id]?.shortName?.lowercase()}${SharedData.hashLang[dict.lang2Id]?.shortName?.lowercase()}"
println(dbName)
val db = Database.connect(dbName)
transaction (db) {
SchemaUtils.create(Dictionaries, Languages, Translations, TermsPronunciations, Pronunciations, PronunciationTypes, DictTypes, Suffixes, Terms)
for ((di, lang) in SharedData.hashLang) {
Languages.insert {
it[id] = lang.id
it[name] = lang.name
it[shortName] = lang.shortName
}
}
for ((di, d) in SharedData.hashDict) {
Dictionaries.insert {
it[id] = d.id
it[lang1] = d.lang1Id
it[lang2] = d.lang2Id
it[name] = d.name
it[fullName] = d.fullName
}
}
for ((di, trans) in SharedData.hashTransMap) {
Translations.insert {
it[id] = trans.id
it[lang1Id] = trans.lang1Id
it[lang2Id] = trans.lang2Id
it[dictionaryId] = trans.dictionaryId
it[langName1] = trans.lang1Name
it[langName2] = trans.lang2Name
it[direction] = trans.direction
}
}
PronunciationTypes.insert {
it[id] = 1
it[name] = "English"
}
PronunciationTypes.insert {
it[id] = 2
it[name] = "American english"
}
PronunciationTypes.insert {
it[id] = 3
it[name] = "Business english"
}
for ((di, dt) in SharedData.hashDictType) {
DictTypes.insert {
it[id] = dt.id
it[shortName] = dt.shortName
it[fullName] = dt.fullName
}
}
var suffixMap : MutableMap<Int,Boolean> = mutableMapOf()
for (t in dictTerms) {
if (t.suffix1Id != null && !suffixMap.containsKey(t.suffix1Id)) {
val ss = SharedData.hashSuffix.getValue(t.suffix1Id)
Suffixes.insert {
it[id] = ss.id
it[text] = ss.text
}
suffixMap[ss.id] = true
}
if (t.suffix2Id != null && !suffixMap.containsKey(t.suffix2Id)) {
val ss = SharedData.hashSuffix.getValue(t.suffix2Id)
Suffixes.insert {
it[id] = ss.id
it[text] = ss.text
}
suffixMap[ss.id] = true
}
Terms.insert {
it[id] = t.id
it[string1] = t.string1
it[string2] = t.string2
it[suffix1] = t.suffix1Id
it[suffix2] = t.suffix2Id
it[type] = t.typeId
it[member] = t.member
it[order2] = t.order2
//it[flags] = format.decodeFromString<IntArray?>(t.flags?.toString() ?:"[]")
}
if (SharedData.hashTermPron.containsKey(t.id)) {
for (pronId in SharedData.hashTermPron.getValue(t.id)){
val pron = SharedData.hashPron.getValue(pronId)
Pronunciations.upsert {
it[id] = pron.id
it[typeId] = pron.typeId
it[ipa] = pron.ipa
it[filename] = pron.filename
}
}
}
}
for (t in dictTerms) {
if (SharedData.hashTermPron.containsKey(t.id)) {
for (pronId in SharedData.hashTermPron.getValue(t.id)){
TermsPronunciations.insert {
it[term] = t.id
it[pronunciation] = pronId
}
}
}
}
dictTerms = listOf()
suffixMap = mutableMapOf()
}
}
}
}

View File

@@ -0,0 +1,14 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class DictType(
val id: Int,
val shortName: String,
val fullName: String
)

View File

@@ -0,0 +1,17 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Dictionary(
val id: Int,
val lang1Id: Int,
val lang2Id: Int,
val name: String?,
val fullName: String?,
val translations: List<Translation> = emptyList()
)

View File

@@ -0,0 +1,15 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Language(
val id: Int,
val name: String,
val shortName: String
)

View File

@@ -0,0 +1,12 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Pronunciation(
val id: Int,
val typeId: Int,
val ipa: String?,
val filename: String?
)

View File

@@ -0,0 +1,10 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class PronunciationType(
val id: Int,
val name: String?
)

View File

@@ -0,0 +1,12 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Settings(
val id: Int,
val dictionary: Int?,
val lastSearch: String?,
)

View File

@@ -0,0 +1,11 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Suffix(
val id: Int,
val text: String
)

View File

@@ -0,0 +1,33 @@
package models
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import org.jetbrains.exposed.sql.Table
@Serializable
data class Term(
val id: Int,
val string1: String,
val string2: String,
val typeId: Int?,
val suffix1Id: Int?,
val suffix2Id: Int?,
val member: String?,
val order2: Int?,
// val flags: IntArray?
)
@Serializable
data class TermFull(
val id: Int,
val string1: String,
val string2: String,
val type: DictType?,
val suffix1: Suffix?,
val suffix2: Suffix?,
val member: String?,
val order2: Int?,
//val flags: IntArray?,
val pronunciations: List<Pronunciation>?
)

View File

@@ -0,0 +1,11 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class TermsPronunciation(
val termsId: Int,
val pronunciationId: Int
)

View File

@@ -0,0 +1,17 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Translation(
val id: Int,
val dictionaryId: Int,
val lang1Id: Int,
val lang2Id: Int,
val lang1Name: String?,
val lang2Name: String?,
val direction: Int
)

View File

@@ -0,0 +1,107 @@
package service
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.flywaydb.core.Flyway
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.LoggerFactory
import javax.sql.DataSource
import tables.*
import dao.*
import com.typesafe.config.ConfigFactory
import models.Translation
object DatabaseFactory {
private val log = LoggerFactory.getLogger(this::class.java)
private val dbs : MutableMap<Int,Database> = mutableMapOf()
private val hdb : MutableMap<String,Database> = mutableMapOf()
private val htrans: MutableMap<String,Translation> = mutableMapOf()
fun connectAndMigrate() {
log.info("Initialising database")
val pool = hikari()
dbs[0] = Database.connect(pool)
runFlyway(pool)
}
private fun hikari(): HikariDataSource {
val cfg = ConfigFactory.load().getConfig("h2")
val config = HikariConfig().apply {
driverClassName = cfg.getString("driver")
jdbcUrl = String.format(cfg.getString("url"),"dict_settings")
maximumPoolSize = 3
isAutoCommit = false
transactionIsolation = "TRANSACTION_REPEATABLE_READ"
validate()
}
return HikariDataSource(config)
}
fun getTranslationForLanguages(l1: String, l2: String): Translation? = htrans["${l1}${l2}"]?: null
fun connectAll(): Map<Int,Database> {
val hMap : MutableMap<Int, Database> = mutableMapOf()
transaction {
for (trans in TranslationDao.all()) {
val l1 = trans.lang1.shortName.lowercase()
val l2 = trans.lang2.shortName.lowercase()
htrans["${l1}${l2}"] = trans.toModel()
}
for (dict in DictionaryDao.all()) {
val cfg = ConfigFactory.load().getConfig("h2")
val config = HikariConfig().apply {
driverClassName = cfg.getString("driver")
jdbcUrl = String.format(cfg.getString("url"),"dict_${dict.lang1.shortName.lowercase()}${dict.lang2.shortName.lowercase()}")
maximumPoolSize = 3
isAutoCommit = false
transactionIsolation = "TRANSACTION_REPEATABLE_READ"
validate()
}
val db = HikariDataSource(config)
val dbc = Database.connect(db)
dbs[dict.id.value] = dbc
hdb["${dict.lang1.shortName.lowercase()}${dict.lang2.shortName.lowercase()}"] = dbc
}
}
return hMap
}
private fun runFlyway(datasource: DataSource) {
val flyway = Flyway.configure().dataSource(datasource).load()
try {
flyway.info()
flyway.migrate()
} catch (e: Exception) {
log.error("Exception running flyway migration", e)
throw e
}
log.info("Flyway migration has finished")
}
suspend fun <T> dbExec(
block: () -> T
): T = withContext(Dispatchers.IO) {
transaction { block() }
}
fun <T> dbExecId(
dbId: Int, block: () -> T
) : T = transaction(dbs[dbId]) {
block()
}
fun <T> dbExecName(
d1: String, d2: String, block: () -> T
) : T = transaction(hdb["${d1.lowercase()}${d2.lowercase()}"]) {
block()
}
}

View File

@@ -0,0 +1,9 @@
package service
import dao.DictTypeDao
import models.DictType
interface DictTypeService {
fun getDictType(id: Int): DictType?
fun getAllDictTypes(): List<DictType>
}

View File

@@ -0,0 +1,17 @@
package service
import dao.DictTypeDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import models.DictType
class DictTypeServiceImpl : DictTypeService {
override fun getDictType(id: Int): DictType? = dbExecId(1) {
DictTypeDao.findById(id)?.toModel()
}
override fun getAllDictTypes(): List<DictType> = dbExecId(1) {
DictTypeDao.all().map { it.toModel() }
}
}

View File

@@ -0,0 +1,9 @@
package service
import dao.DictionaryDao
import models.Dictionary
interface DictionaryService {
fun getDictionary(id: Int): Dictionary?
fun getAllDictionary(): List<Dictionary>
}

View File

@@ -0,0 +1,16 @@
package service
import dao.DictionaryDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import models.Dictionary
class DictionaryServiceImpl : DictionaryService {
override fun getDictionary(id: Int): Dictionary? = dbExecId(1) {
DictionaryDao.findById(id)?.toModel()
}
override fun getAllDictionary(): List<Dictionary> = dbExecId(1) {
DictionaryDao.all().map { it.toModel() }
}
}

View File

@@ -0,0 +1,9 @@
package service
import dao.LanguageDao
import models.Language
interface LanguageService {
fun getLanguage(id: Int): Language?
fun getAllLanguages(): List<Language>
}

View File

@@ -0,0 +1,17 @@
package service
import dao.LanguageDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import models.Language
class LanguageServiceImpl : LanguageService {
override fun getLanguage(id: Int): Language? = dbExecId(1) {
LanguageDao.findById(id)?.toModel()
}
override fun getAllLanguages(): List<Language> = dbExecId(1) {
LanguageDao.all().map { it.toModel() }
}
}

View File

@@ -0,0 +1,9 @@
package service
import dao.PronunciationDao
import models.Pronunciation
interface PronunciationService {
fun getPronunciation(id: Int): Pronunciation?
fun getAllPronunciations(): List<Pronunciation>
}

View File

@@ -0,0 +1,15 @@
package service
import dao.PronunciationDao
import models.Pronunciation
import service.DatabaseFactory.dbExecId
class PronunciationServiceImpl: PronunciationService {
override fun getPronunciation(id: Int): Pronunciation? = dbExecId(1) {
PronunciationDao.findById(id)?.toModel()
}
override fun getAllPronunciations(): List<Pronunciation> = dbExecId(1) {
PronunciationDao.all().map { it.toModel() }
}
}

View File

@@ -0,0 +1,24 @@
package service
import dao.TermDao
import models.Term
import models.TermFull
import models.Pronunciation
import models.Translation
enum class SearchType {
EXACT,
START,
END,
FULL
}
interface TermService {
fun getTerm(id: Int): Term?
fun getFullTerm(id: Int): TermFull?
fun getAllTerm(): List<Term>
fun getPronunciationsForTerm(id: Int): List<Pronunciation>?
fun getTranslationForTerm(term: String, trans: Translation, type: SearchType = SearchType.EXACT): List<TermFull>?
fun getTranslationForTerm(term: String, fromLang: String, toLang: String): List<TermFull>?
}

View File

@@ -0,0 +1,59 @@
package service
import dao.TermDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import service.DatabaseFactory.dbExecName
import models.Term
import models.TermFull
import models.Pronunciation
import models.Translation
import tables.*
import service.SearchType
class TermServiceImpl : TermService {
override fun getTerm(id: Int): Term? = dbExecId(1) {
TermDao.findById(id)?.toModel()
}
override fun getAllTerm(): List<Term> = dbExecId(1) {
TermDao.all().map { it.toModel() }
}
override fun getFullTerm(id: Int): TermFull? = dbExecId(1) {
TermDao.findById(id)?.toFullModel()
}
override fun getPronunciationsForTerm(id: Int): List<Pronunciation>? = dbExecId(1) {
TermDao.findById(id)?.toFullModel()?.pronunciations
}
override fun getTranslationForTerm(term: String,trans: Translation, type: SearchType): List<TermFull>? {
val s : String
when (type) {
SearchType.START -> s = term + "%"
SearchType.END -> s = "%" + term
SearchType.FULL -> s = "%" + term + "%"
SearchType.EXACT -> s = term
}
return dbExecId(trans.dictionaryId) {
if (trans.direction == 1)
if (type == SearchType.EXACT)
TermDao.find { Terms.string1 eq s }.map { it.toFullModel() }
else
TermDao.find { Terms.string1 like s }.map { it.toFullModel() }
else
if (type == SearchType.EXACT)
TermDao.find { Terms.string2 eq s }.map { it.toFullModel() }
else
TermDao.find { Terms.string1 like s }.map { it.toFullModel() }
}
}
override fun getTranslationForTerm(term: String, fromLang: String, toLang: String): List<TermFull>? = dbExecName(fromLang,toLang) {
TermDao.find { Terms.string1 eq term }.map { it.toFullModel() }
}
}

View File

@@ -0,0 +1,10 @@
package service
import dao.TranslationDao
import models.Translation
interface TranslationService {
fun getTranslation(id: Int): Translation?
fun getTranslationFromTo(from: String, to: String): Translation?
fun getAllTranslations(): List<Translation>
}

View File

@@ -0,0 +1,24 @@
package service
import dao.TranslationDao
import dao.LanguageDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import service.DatabaseFactory.getTranslationForLanguages
import models.Translation
import tables.Translations
import tables.Languages
class TranslationServiceImpl : TranslationService {
override fun getTranslation(id: Int): Translation? = dbExecId(0) {
TranslationDao.findById(id)?.toModel()
}
override fun getAllTranslations(): List<Translation> = dbExecId(0) {
TranslationDao.all().map { it.toModel() }
}
override fun getTranslationFromTo(from: String, to: String): Translation? = dbExecId(0) {
getTranslationForLanguages(from,to)
}
}

View File

@@ -0,0 +1,8 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object DictTypes : IntIdTable() {
val shortName = varchar("short_name", 255)
val fullName = varchar("full_name", 255)
}

View File

@@ -0,0 +1,10 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Dictionaries : IntIdTable() {
val lang1 = reference("lang1_id", Languages)
val lang2 = reference("lang2_id", Languages)
val name = varchar("name", 255)
val fullName = varchar("full_name", 255)
}

View File

@@ -0,0 +1,12 @@
package tables
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.Column
object Languages : IntIdTable() {
val name = varchar("name", 255)
val shortName = varchar("short_name", 255)
}

View File

@@ -0,0 +1,10 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Pronunciations : IntIdTable() {
val typeId = reference("type_id",PronunciationTypes)
val ipa = varchar("ipa", 255).nullable()
val filename = varchar("filename", 255).nullable()
}

View File

@@ -0,0 +1,7 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object PronunciationTypes : IntIdTable() {
val name = varchar("name", 255)
}

View File

@@ -0,0 +1,8 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Settings : IntIdTable() {
val dictionary = reference("dictionary_id",Dictionaries)
val lastSearch = varchar("last_search", 255).nullable()
}

View File

@@ -0,0 +1,7 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Suffixes : IntIdTable() {
val text = varchar("text", 255)
}

View File

@@ -0,0 +1,17 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.json.json
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 suffix1 = reference("suffix1_id",Suffixes).nullable()
val suffix2 = reference("suffix2_id",Suffixes).nullable()
val type = reference("type_id",DictTypes).nullable()
val member = varchar("member", 255).nullable()
val order2 = integer("order2").nullable()
//val flags = json<IntArray>("flags", Json.Default).nullable()
}

View File

@@ -0,0 +1,10 @@
package tables
import org.jetbrains.exposed.sql.Table
object TermsPronunciations : Table() {
val term = reference("term", Terms)
val pronunciation = reference("pronunciation", Pronunciations)
override val primaryKey = PrimaryKey(term, pronunciation, name = "PK_Term_Pronunciation")
}

Some files were not shown because too many files have changed in this diff Show More