diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 7f6d0e4..9030209 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -14,6 +14,14 @@ plugins { } kotlin { + // export correct artifact to use all classes of library directly from Swift + targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget::class.java).all { + binaries.withType(org.jetbrains.kotlin.gradle.plugin.mpp.Framework::class.java).all { + export("dev.icerock.moko:mvvm-core:0.16.1") + export("dev.icerock.moko:mvvm-state:0.16.1") + } + } + androidTarget { compilations.all { compileTaskProvider { @@ -58,6 +66,9 @@ kotlin { implementation(libs.multiplatformSettings) implementation(libs.koin.core) implementation(libs.koin.compose) + + api("dev.icerock.moko:mvvm-compose:0.16.1") + api("dev.icerock.moko:mvvm-flow-compose:0.16.1") } commonTest.dependencies { @@ -89,11 +100,11 @@ kotlin { android { namespace = "dev.meloda.overseerr" - compileSdk = 35 + compileSdk = 34 defaultConfig { minSdk = 26 - targetSdk = 35 + targetSdk = 34 applicationId = "dev.meloda.overseerr.androidApp" versionCode = 1 diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index da8f676..a5d1d7a 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -2,9 +2,11 @@ + - - + + - \ No newline at end of file + diff --git a/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/common/AppGlobal.kt b/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/common/AppGlobal.kt new file mode 100644 index 0000000..9d8093b --- /dev/null +++ b/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/common/AppGlobal.kt @@ -0,0 +1,16 @@ +package dev.meloda.overseerr.common + +import android.app.Application +import dev.meloda.overseerr.di.appModule +import org.koin.core.context.startKoin + +class AppGlobal : Application() { + + override fun onCreate() { + super.onCreate() + + startKoin { + modules(appModule) + } + } +} diff --git a/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/model/Platform.android.kt b/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/model/Platform.android.kt new file mode 100644 index 0000000..14fae78 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/model/Platform.android.kt @@ -0,0 +1,6 @@ +package dev.meloda.overseerr.model + +actual class Platform actual constructor() { + actual val name: String + get() = "Android" +} diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/App.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/App.kt index e987a48..5133b2f 100644 --- a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/App.kt +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/App.kt @@ -1,100 +1,78 @@ package dev.meloda.overseerr -import androidx.compose.animation.core.* -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.rotate -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp -import overseerr.composeapp.generated.resources.* +import dev.meloda.overseerr.model.Platform import dev.meloda.overseerr.theme.AppTheme -import dev.meloda.overseerr.theme.LocalThemeIsDark -import kotlinx.coroutines.isActive -import org.jetbrains.compose.resources.Font -import org.jetbrains.compose.resources.stringResource -import org.jetbrains.compose.resources.vectorResource +import org.koin.compose.KoinContext +import org.koin.compose.koinInject +@OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun App() = AppTheme { - Column( - modifier = Modifier - .fillMaxSize() - .windowInsetsPadding(WindowInsets.safeDrawing) - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = stringResource(Res.string.cyclone), - fontFamily = FontFamily(Font(Res.font.IndieFlower_Regular)), - style = MaterialTheme.typography.displayLarge - ) +internal fun App() = KoinContext { + val platform: Platform = koinInject() - var isRotating by remember { mutableStateOf(false) } + AppTheme { + var loginValue by rememberSaveable { + mutableStateOf("") + } + var passwordValue by rememberSaveable { + mutableStateOf("") + } - val rotate = remember { Animatable(0f) } - val target = 360f - if (isRotating) { - LaunchedEffect(Unit) { - while (isActive) { - val remaining = (target - rotate.value) / target - rotate.animateTo(target, animationSpec = tween((1_000 * remaining).toInt(), easing = LinearEasing)) - rotate.snapTo(0f) + Scaffold( + topBar = { + TopAppBar( + title = { + Text(text = "Log in") + } + ) + + Text( + text = "Current platform: ${platform.name}", + style = MaterialTheme.typography.displayLarge + ) + } + ) { padding -> + Column( + modifier = Modifier.fillMaxSize() + .padding(padding), + horizontalAlignment = Alignment.CenterHorizontally + ) { + TextField( + modifier = Modifier.fillMaxWidth(0.9f), + value = loginValue, + onValueChange = { newText -> loginValue = newText }, + placeholder = { Text(text = "Login") } + ) + + Spacer(modifier = Modifier.height(16.dp)) + + TextField( + modifier = Modifier.fillMaxWidth(0.9f), + value = passwordValue, + onValueChange = { newText -> passwordValue = newText }, + placeholder = { Text(text = "Password") } + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = { + + } + ) { + Text(text = "Authorize") } } } - - Image( - modifier = Modifier - .size(250.dp) - .padding(16.dp) - .run { rotate(rotate.value) }, - imageVector = vectorResource(Res.drawable.ic_cyclone), - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface), - contentDescription = null - ) - - ElevatedButton( - modifier = Modifier - .padding(horizontal = 8.dp, vertical = 4.dp) - .widthIn(min = 200.dp), - onClick = { isRotating = !isRotating }, - content = { - Icon(vectorResource(Res.drawable.ic_rotate_right), contentDescription = null) - Spacer(Modifier.size(ButtonDefaults.IconSpacing)) - Text( - stringResource(if (isRotating) Res.string.stop else Res.string.run) - ) - } - ) - - var isDark by LocalThemeIsDark.current - val icon = remember(isDark) { - if (isDark) Res.drawable.ic_light_mode - else Res.drawable.ic_dark_mode - } - - ElevatedButton( - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp).widthIn(min = 200.dp), - onClick = { isDark = !isDark }, - content = { - Icon(vectorResource(icon), contentDescription = null) - Spacer(Modifier.size(ButtonDefaults.IconSpacing)) - Text(stringResource(Res.string.theme)) - } - ) - - val uriHandler = LocalUriHandler.current - TextButton( - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp).widthIn(min = 200.dp), - onClick = { uriHandler.openUri("https://github.com/terrakok") }, - ) { - Text(stringResource(Res.string.open_github)) - } } } diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/di/MainModule.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/di/MainModule.kt new file mode 100644 index 0000000..a356837 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/di/MainModule.kt @@ -0,0 +1,9 @@ +package dev.meloda.overseerr.di + +import dev.meloda.overseerr.model.Platform +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +val appModule = module { + singleOf(::Platform) +} diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/LoginViewModel.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/LoginViewModel.kt new file mode 100644 index 0000000..7431af2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/LoginViewModel.kt @@ -0,0 +1,9 @@ +package dev.meloda.overseerr.login + +import dev.icerock.moko.mvvm.viewmodel.ViewModel + +class LoginViewModel : ViewModel() { + + + +} diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/model/LoginScreenState.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/model/LoginScreenState.kt new file mode 100644 index 0000000..571ea3b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/model/LoginScreenState.kt @@ -0,0 +1,4 @@ +package dev.meloda.overseerr.login.model + +class LoginScreenState { +} diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/presentation/LoginScreen.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/presentation/LoginScreen.kt new file mode 100644 index 0000000..67094b1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/login/presentation/LoginScreen.kt @@ -0,0 +1,10 @@ +package dev.meloda.overseerr.login.presentation + +import androidx.compose.runtime.Composable +import dev.icerock.moko.mvvm.compose.getViewModel +import dev.meloda.overseerr.login.LoginViewModel + +@Composable +fun LoginScreen(viewModel: LoginViewModel) { + +} diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/model/Platform.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/model/Platform.kt new file mode 100644 index 0000000..598bcb5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/model/Platform.kt @@ -0,0 +1,5 @@ +package dev.meloda.overseerr.model + +expect class Platform() { + val name: String +} diff --git a/composeApp/src/iosMain/kotlin/dev/meloda/overseerr/model/Platform.ios.kt b/composeApp/src/iosMain/kotlin/dev/meloda/overseerr/model/Platform.ios.kt new file mode 100644 index 0000000..e3b14e2 --- /dev/null +++ b/composeApp/src/iosMain/kotlin/dev/meloda/overseerr/model/Platform.ios.kt @@ -0,0 +1,6 @@ +package dev.meloda.overseerr.model + +actual class Platform actual constructor() { + actual val name: String + get() = "iOS" +} diff --git a/composeApp/src/iosMain/kotlin/main.kt b/composeApp/src/iosMain/kotlin/main.kt index 8bb405c..a0b9790 100644 --- a/composeApp/src/iosMain/kotlin/main.kt +++ b/composeApp/src/iosMain/kotlin/main.kt @@ -1,5 +1,12 @@ import androidx.compose.ui.window.ComposeUIViewController import dev.meloda.overseerr.App +import dev.meloda.overseerr.di.appModule +import org.koin.core.context.startKoin import platform.UIKit.UIViewController -fun MainViewController(): UIViewController = ComposeUIViewController { App() } +fun MainViewController(): UIViewController = ComposeUIViewController { + startKoin { + modules(appModule) + } + App() +} diff --git a/composeApp/src/jvmMain/kotlin/dev/meloda/overseerr/model/Platform.jvm.kt b/composeApp/src/jvmMain/kotlin/dev/meloda/overseerr/model/Platform.jvm.kt new file mode 100644 index 0000000..e3520fd --- /dev/null +++ b/composeApp/src/jvmMain/kotlin/dev/meloda/overseerr/model/Platform.jvm.kt @@ -0,0 +1,6 @@ +package dev.meloda.overseerr.model + +actual class Platform actual constructor() { + actual val name: String + get() = "JVM" +} diff --git a/composeApp/src/jvmMain/kotlin/main.kt b/composeApp/src/jvmMain/kotlin/main.kt index f85c2ed..c46f9b6 100644 --- a/composeApp/src/jvmMain/kotlin/main.kt +++ b/composeApp/src/jvmMain/kotlin/main.kt @@ -2,10 +2,16 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState -import java.awt.Dimension import dev.meloda.overseerr.App +import dev.meloda.overseerr.di.appModule +import org.koin.core.context.startKoin +import java.awt.Dimension fun main() = application { + startKoin { + modules(appModule) + } + Window( title = "Overseerr", state = rememberWindowState(width = 800.dp, height = 600.dp), @@ -14,4 +20,4 @@ fun main() = application { window.minimumSize = Dimension(350, 600) App() } -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index 75a6406..c5b001b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,7 @@ org.gradle.parallel=true kotlin.code.style=official kotlin.js.compiler=ir kotlin.daemon.jvmargs=-Xmx4G +kotlin.native.ignoreDisabledTargets=true #Android android.useAndroidX=true diff --git a/settings.gradle.kts b/settings.gradle.kts index 1a27689..8e05f06 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,9 @@ pluginManagement { mavenCentral() } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" +} dependencyResolutionManagement { repositories { @@ -31,4 +34,3 @@ dependencyResolutionManagement { } } include(":composeApp") -