refactor: switch from metro to koin
Honestly metro looks too overcomplicated and I still don't know how to use it properly. Switching to koin for now as I'm more comfortable with it.
This commit is contained in:
parent
53abaccd21
commit
2725342c3f
23 changed files with 165 additions and 199 deletions
|
|
@ -6,8 +6,8 @@ plugins {
|
||||||
alias(libs.plugins.composeHotReload) apply false
|
alias(libs.plugins.composeHotReload) apply false
|
||||||
alias(libs.plugins.composeMultiplatform) apply false
|
alias(libs.plugins.composeMultiplatform) apply false
|
||||||
alias(libs.plugins.composeCompiler) apply false
|
alias(libs.plugins.composeCompiler) apply false
|
||||||
|
alias(libs.plugins.koinCompiler) apply false
|
||||||
alias(libs.plugins.kotlinMultiplatform) apply false
|
alias(libs.plugins.kotlinMultiplatform) apply false
|
||||||
alias(libs.plugins.kotlinSerialization) apply false
|
alias(libs.plugins.kotlinSerialization) apply false
|
||||||
alias(libs.plugins.metro) apply false
|
|
||||||
alias(libs.plugins.sqldelight) apply false
|
alias(libs.plugins.sqldelight) apply false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.androidLibrary)
|
alias(libs.plugins.androidLibrary)
|
||||||
|
alias(libs.plugins.koinCompiler)
|
||||||
alias(libs.plugins.kotlinMultiplatform)
|
alias(libs.plugins.kotlinMultiplatform)
|
||||||
alias(libs.plugins.kotlinSerialization)
|
alias(libs.plugins.kotlinSerialization)
|
||||||
alias(libs.plugins.metro)
|
|
||||||
alias(libs.plugins.sqldelight)
|
alias(libs.plugins.sqldelight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,6 +23,9 @@ kotlin {
|
||||||
implementation(libs.ktor.client.websockets)
|
implementation(libs.ktor.client.websockets)
|
||||||
implementation(libs.ktor.serialization.kotlinx.json)
|
implementation(libs.ktor.serialization.kotlinx.json)
|
||||||
|
|
||||||
|
implementation(project.dependencies.platform(libs.koin.bom))
|
||||||
|
implementation(libs.koin.core)
|
||||||
|
|
||||||
implementation(libs.kermit)
|
implementation(libs.kermit)
|
||||||
implementation(libs.settings)
|
implementation(libs.settings)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,7 @@ package moe.lava.neon.core
|
||||||
|
|
||||||
import com.russhwolf.settings.Settings
|
import com.russhwolf.settings.Settings
|
||||||
import com.russhwolf.settings.nullableString
|
import com.russhwolf.settings.nullableString
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import dev.zacsweers.metro.SingleIn
|
|
||||||
|
|
||||||
@SingleIn(AppScope::class)
|
|
||||||
@Inject
|
|
||||||
class AppSettings {
|
class AppSettings {
|
||||||
private val settings = Settings()
|
private val settings = Settings()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
package moe.lava.neon.core.api
|
package moe.lava.neon.core.api
|
||||||
|
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import dev.zacsweers.metro.SingleIn
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
import io.ktor.client.plugins.HttpSend
|
import io.ktor.client.plugins.HttpSend
|
||||||
|
|
@ -23,8 +20,6 @@ import kotlinx.serialization.json.JsonNamingStrategy
|
||||||
import moe.lava.neon.core.api.captcha.CaptchaRequest
|
import moe.lava.neon.core.api.captcha.CaptchaRequest
|
||||||
import moe.lava.neon.core.api.captcha.CaptchaResponse
|
import moe.lava.neon.core.api.captcha.CaptchaResponse
|
||||||
|
|
||||||
@SingleIn(AppScope::class)
|
|
||||||
@Inject
|
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
private val logger = Logger.withTag("neon.core.api/client")
|
private val logger = Logger.withTag("neon.core.api/client")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,19 @@
|
||||||
package moe.lava.neon.core.api.gateway
|
package moe.lava.neon.core.api.gateway
|
||||||
|
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import moe.lava.neon.core.di.EventHandlerGraph
|
import moe.lava.neon.core.api.gateway.handlers.EventHandlers
|
||||||
import moe.lava.neon.core.repository.AuthRepository
|
import moe.lava.neon.core.repository.AuthRepository
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Inject
|
|
||||||
class GatewayHandler(
|
class GatewayHandler(
|
||||||
private val auth: AuthRepository,
|
private val auth: AuthRepository,
|
||||||
private val handlers: EventHandlerGraph,
|
private val eventHandlers: EventHandlers,
|
||||||
) {
|
) {
|
||||||
private val logger = Logger.withTag("neon.core.api.gateway/handler")
|
private val logger = Logger.withTag("neon.core.api.gateway/handler")
|
||||||
private val scope = CoroutineScope(Dispatchers.IO)
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
@ -35,7 +33,7 @@ class GatewayHandler(
|
||||||
|
|
||||||
session = GatewaySession.start(
|
session = GatewaySession.start(
|
||||||
token = token,
|
token = token,
|
||||||
eventHandlers = handlers,
|
eventHandlers = eventHandlers,
|
||||||
resumeProps = resumeProps,
|
resumeProps = resumeProps,
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
logger.d { "Successful session start" }
|
logger.d { "Successful session start" }
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import moe.lava.neon.core.api.ApiConstants
|
import moe.lava.neon.core.api.ApiConstants
|
||||||
import moe.lava.neon.core.api.ApiConstants.json
|
import moe.lava.neon.core.api.ApiConstants.json
|
||||||
import moe.lava.neon.core.di.EventHandlerGraph
|
import moe.lava.neon.core.api.gateway.handlers.EventHandlers
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ private val logger = Logger.withTag("neon.core.api.gateway/session")
|
||||||
class GatewaySession private constructor(
|
class GatewaySession private constructor(
|
||||||
private var ws: DefaultClientWebSocketSession,
|
private var ws: DefaultClientWebSocketSession,
|
||||||
private val token: String,
|
private val token: String,
|
||||||
private val handlers: EventHandlerGraph,
|
private val handlers: EventHandlers,
|
||||||
private val scope: CoroutineScope,
|
private val scope: CoroutineScope,
|
||||||
private var resumeProps: ResumeProperties?,
|
private var resumeProps: ResumeProperties?,
|
||||||
private val onDestroy: (GatewayCloseReason, ResumeProperties?) -> Unit,
|
private val onDestroy: (GatewayCloseReason, ResumeProperties?) -> Unit,
|
||||||
|
|
@ -46,7 +46,7 @@ class GatewaySession private constructor(
|
||||||
companion object {
|
companion object {
|
||||||
suspend fun start(
|
suspend fun start(
|
||||||
token: String,
|
token: String,
|
||||||
eventHandlers: EventHandlerGraph,
|
eventHandlers: EventHandlers,
|
||||||
client: HttpClient = HttpClient {
|
client: HttpClient = HttpClient {
|
||||||
install(HttpCookies)
|
install(HttpCookies)
|
||||||
install(WebSockets)
|
install(WebSockets)
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,7 @@ package moe.lava.neon.core.api.gateway.handlers
|
||||||
import moe.lava.neon.core.api.gateway.Event
|
import moe.lava.neon.core.api.gateway.Event
|
||||||
|
|
||||||
sealed interface Handler<T: Event.Incoming>
|
sealed interface Handler<T: Event.Incoming>
|
||||||
|
|
||||||
|
class EventHandlers(
|
||||||
|
val ready: ReadyHandler
|
||||||
|
)
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
package moe.lava.neon.core.api.gateway.handlers
|
package moe.lava.neon.core.api.gateway.handlers
|
||||||
|
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import moe.lava.neon.core.api.gateway.Event
|
import moe.lava.neon.core.api.gateway.Event
|
||||||
import moe.lava.neon.core.api.gateway.ResumeProperties
|
import moe.lava.neon.core.api.gateway.ResumeProperties
|
||||||
|
|
||||||
private val logger = Logger.withTag("neon.core.api.events/ready")
|
private val logger = Logger.withTag("neon.core.api.events/ready")
|
||||||
|
|
||||||
@Inject
|
|
||||||
class ReadyHandler : Handler<Event.Ready> {
|
class ReadyHandler : Handler<Event.Ready> {
|
||||||
fun handle(event: Event.Ready, updateResumeProps: (ResumeProperties) -> Unit) {
|
fun handle(event: Event.Ready, updateResumeProps: (ResumeProperties) -> Unit) {
|
||||||
logger.i { "Received payload $event" }
|
logger.i { "Received payload $event" }
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package moe.lava.neon.core.di
|
|
||||||
|
|
||||||
import dev.zacsweers.metro.GraphExtension
|
|
||||||
import moe.lava.neon.core.AppSettings
|
|
||||||
import moe.lava.neon.core.api.ApiClient
|
|
||||||
import moe.lava.neon.core.repository.AuthRepository
|
|
||||||
import moe.lava.neon.core.repository.UserRepository
|
|
||||||
|
|
||||||
@GraphExtension
|
|
||||||
interface AppGraph {
|
|
||||||
val api: ApiClient
|
|
||||||
val settings: AppSettings
|
|
||||||
|
|
||||||
val auth: AuthRepository
|
|
||||||
val users: UserRepository
|
|
||||||
|
|
||||||
val gatewayHandlers: EventHandlerGraph
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package moe.lava.neon.core.di
|
||||||
|
|
||||||
|
import moe.lava.neon.core.AppSettings
|
||||||
|
import moe.lava.neon.core.api.ApiClient
|
||||||
|
import moe.lava.neon.core.api.gateway.GatewayHandler
|
||||||
|
import moe.lava.neon.core.api.gateway.handlers.EventHandlers
|
||||||
|
import moe.lava.neon.core.api.gateway.handlers.ReadyHandler
|
||||||
|
import moe.lava.neon.core.repository.AuthRepository
|
||||||
|
import moe.lava.neon.core.repository.UserRepository
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import org.koin.plugin.module.dsl.single
|
||||||
|
|
||||||
|
val coreModule = module {
|
||||||
|
single<ApiClient>()
|
||||||
|
single<AppSettings>()
|
||||||
|
|
||||||
|
single<AuthRepository>()
|
||||||
|
single<UserRepository>()
|
||||||
|
|
||||||
|
single<GatewayHandler>()
|
||||||
|
|
||||||
|
single<ReadyHandler>()
|
||||||
|
single<EventHandlers>()
|
||||||
|
}
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package moe.lava.neon.core.di
|
|
||||||
|
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.ContributesTo
|
|
||||||
import dev.zacsweers.metro.GraphExtension
|
|
||||||
import moe.lava.neon.core.api.gateway.handlers.ReadyHandler
|
|
||||||
|
|
||||||
@GraphExtension
|
|
||||||
@ContributesTo(AppScope::class)
|
|
||||||
interface EventHandlerGraph {
|
|
||||||
val ready: ReadyHandler
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
package moe.lava.neon.core.repository
|
package moe.lava.neon.core.repository
|
||||||
|
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import dev.zacsweers.metro.SingleIn
|
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
import io.ktor.client.request.header
|
import io.ktor.client.request.header
|
||||||
|
|
@ -46,8 +43,6 @@ sealed class AuthResponse {
|
||||||
// data class MFARequested() : AuthResponse()
|
// data class MFARequested() : AuthResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
|
||||||
@SingleIn(AppScope::class)
|
|
||||||
class AuthRepository(
|
class AuthRepository(
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
private val api: ApiClient,
|
private val api: ApiClient,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
package moe.lava.neon.core.repository
|
package moe.lava.neon.core.repository
|
||||||
|
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import dev.zacsweers.metro.SingleIn
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@SingleIn(AppScope::class)
|
|
||||||
class UserRepository {
|
class UserRepository {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,13 @@ desugar = "2.1.5"
|
||||||
hcaptcha = "4.4.0"
|
hcaptcha = "4.4.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
kermit = "2.0.8"
|
kermit = "2.0.8"
|
||||||
|
koin-bom = "4.2.0-RC1"
|
||||||
|
koin-plugin = "0.3.0"
|
||||||
kotlin = "2.3.0"
|
kotlin = "2.3.0"
|
||||||
kotlinx-coroutines = "1.10.2"
|
kotlinx-coroutines = "1.10.2"
|
||||||
ktor = "3.4.0"
|
ktor = "3.4.0"
|
||||||
material3 = "1.11.0-alpha02"
|
material3 = "1.11.0-alpha02"
|
||||||
material3-adaptive = "1.3.0-alpha04"
|
material3-adaptive = "1.3.0-alpha04"
|
||||||
metro = "0.10.2"
|
|
||||||
settings = "1.3.0"
|
settings = "1.3.0"
|
||||||
sqldelight = "2.2.1"
|
sqldelight = "2.2.1"
|
||||||
|
|
||||||
|
|
@ -54,6 +55,12 @@ desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desuga
|
||||||
hcaptcha-compose = { module = "com.github.hCaptcha.hcaptcha-android-sdk:compose-sdk", version.ref = "hcaptcha" }
|
hcaptcha-compose = { module = "com.github.hCaptcha.hcaptcha-android-sdk:compose-sdk", version.ref = "hcaptcha" }
|
||||||
junit = { module = "junit:junit", version.ref = "junit" }
|
junit = { module = "junit:junit", version.ref = "junit" }
|
||||||
kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
|
kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
|
||||||
|
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" }
|
||||||
|
koin-compose = { module = "io.insert-koin:koin-compose" }
|
||||||
|
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel" }
|
||||||
|
koin-compose-navigation3 = { module = "io.insert-koin:koin-compose-navigation3" }
|
||||||
|
koin-core = { module = "io.insert-koin:koin-core" }
|
||||||
|
koin-test = { module = "io.insert-koin:koin-test" }
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", 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" }
|
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
||||||
kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
|
kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
|
||||||
|
|
@ -62,7 +69,6 @@ ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
||||||
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
|
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
|
||||||
ktor-client-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
|
ktor-client-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
|
||||||
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
|
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
|
||||||
metrox-viewmodel-compose = { module = "dev.zacsweers.metro:metrox-viewmodel-compose", version.ref = "metro" }
|
|
||||||
settings = { module = "com.russhwolf:multiplatform-settings-no-arg", version.ref = "settings" }
|
settings = { module = "com.russhwolf:multiplatform-settings-no-arg", version.ref = "settings" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
|
@ -71,7 +77,7 @@ androidLibrary = { id = "com.android.library", version.ref = "agp" }
|
||||||
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
composeHotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "compose-hot-reload" }
|
composeHotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "compose-hot-reload" }
|
||||||
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
||||||
|
koinCompiler = { id = "io.insert-koin.compiler.plugin", version.ref = "koin-plugin" }
|
||||||
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||||
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
metro = { id = "dev.zacsweers.metro", version.ref = "metro" }
|
|
||||||
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
|
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ plugins {
|
||||||
alias(libs.plugins.composeMultiplatform)
|
alias(libs.plugins.composeMultiplatform)
|
||||||
alias(libs.plugins.composeCompiler)
|
alias(libs.plugins.composeCompiler)
|
||||||
alias(libs.plugins.composeHotReload)
|
alias(libs.plugins.composeHotReload)
|
||||||
alias(libs.plugins.metro)
|
alias(libs.plugins.koinCompiler)
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
|
@ -54,8 +54,10 @@ kotlin {
|
||||||
|
|
||||||
implementation(libs.kermit)
|
implementation(libs.kermit)
|
||||||
|
|
||||||
implementation(libs.metrox.viewmodel.compose)
|
implementation(project.dependencies.platform(libs.koin.bom))
|
||||||
|
implementation(libs.koin.compose)
|
||||||
|
implementation(libs.koin.compose.viewmodel)
|
||||||
|
implementation(libs.koin.compose.navigation3)
|
||||||
}
|
}
|
||||||
commonTest.dependencies {
|
commonTest.dependencies {
|
||||||
implementation(libs.kotlin.test)
|
implementation(libs.kotlin.test)
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,23 @@ import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import moe.lava.neon.ui.App
|
import moe.lava.neon.ui.App
|
||||||
|
import moe.lava.neon.ui.di.initKoin
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
|
import org.koin.android.ext.koin.androidLogger
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
initKoin {
|
||||||
|
androidContext(this@MainActivity)
|
||||||
|
androidLogger()
|
||||||
|
}
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
App()
|
App()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun AppAndroidPreview() {
|
|
||||||
App()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import androidx.compose.material3.MaterialExpressiveTheme
|
||||||
import androidx.compose.material3.MotionScheme
|
import androidx.compose.material3.MotionScheme
|
||||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
|
||||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||||
import androidx.navigation3.runtime.NavKey
|
import androidx.navigation3.runtime.NavKey
|
||||||
import androidx.navigation3.runtime.entryProvider
|
import androidx.navigation3.runtime.entryProvider
|
||||||
|
|
@ -14,12 +13,10 @@ import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
||||||
import androidx.navigation3.ui.NavDisplay
|
import androidx.navigation3.ui.NavDisplay
|
||||||
import androidx.savedstate.serialization.SavedStateConfiguration
|
import androidx.savedstate.serialization.SavedStateConfiguration
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import dev.zacsweers.metro.createGraph
|
|
||||||
import dev.zacsweers.metrox.viewmodel.LocalMetroViewModelFactory
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
import kotlinx.serialization.modules.polymorphic
|
import kotlinx.serialization.modules.polymorphic
|
||||||
import moe.lava.neon.ui.di.AppUiGraph
|
import moe.lava.neon.core.repository.AuthRepository
|
||||||
import moe.lava.neon.ui.screens.Login
|
import moe.lava.neon.ui.screens.Login
|
||||||
import moe.lava.neon.ui.screens.Sample
|
import moe.lava.neon.ui.screens.Sample
|
||||||
import moe.lava.neon.ui.screens.chat.Chat
|
import moe.lava.neon.ui.screens.chat.Chat
|
||||||
|
|
@ -29,6 +26,7 @@ import moe.lava.neon.ui.screens.navigator.NavigatorModel
|
||||||
import moe.lava.neon.ui.screens.navigator.NavigatorPreviewProvider
|
import moe.lava.neon.ui.screens.navigator.NavigatorPreviewProvider
|
||||||
import moe.lava.neon.ui.util.ThreePaneSceneStrategy
|
import moe.lava.neon.ui.util.ThreePaneSceneStrategy
|
||||||
import moe.lava.neon.ui.util.rememberThreePaneSceneStrategy
|
import moe.lava.neon.ui.util.rememberThreePaneSceneStrategy
|
||||||
|
import org.koin.compose.koinInject
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
object Route {
|
object Route {
|
||||||
|
|
@ -68,81 +66,78 @@ fun App() {
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val uiGraph = createGraph<AppUiGraph>()
|
val auth: AuthRepository = koinInject()
|
||||||
val graph = uiGraph.core
|
CaptchaBinder(koinInject())
|
||||||
CaptchaBinder(graph.api)
|
MaterialExpressiveTheme(
|
||||||
CompositionLocalProvider(LocalMetroViewModelFactory provides uiGraph.metroViewModelFactory) {
|
colorScheme = getColorScheme(),
|
||||||
MaterialExpressiveTheme(
|
motionScheme = MotionScheme.expressive(),
|
||||||
colorScheme = getColorScheme(),
|
) {
|
||||||
motionScheme = MotionScheme.expressive(),
|
val init = if (auth.token != null) Route.Sample else Route.Login
|
||||||
) {
|
val backStack = rememberNavBackStack(config, Route.Sample)
|
||||||
val init = if (graph.auth.token != null) Route.Sample else Route.Login
|
val threePaneStrategy = rememberThreePaneSceneStrategy<NavKey>()
|
||||||
val backStack = rememberNavBackStack(config, Route.Sample)
|
NavDisplay(
|
||||||
val threePaneStrategy = rememberThreePaneSceneStrategy<NavKey>()
|
backStack = backStack,
|
||||||
NavDisplay(
|
entryDecorators = listOf(
|
||||||
backStack = backStack,
|
rememberSaveableStateHolderNavEntryDecorator(),
|
||||||
entryDecorators = listOf(
|
rememberViewModelStoreNavEntryDecorator(),
|
||||||
rememberSaveableStateHolderNavEntryDecorator(),
|
),
|
||||||
rememberViewModelStoreNavEntryDecorator(),
|
onBack = { backStack.removeLastOrNull() },
|
||||||
),
|
sceneStrategy = threePaneStrategy,
|
||||||
onBack = { backStack.removeLastOrNull() },
|
entryProvider = entryProvider {
|
||||||
sceneStrategy = threePaneStrategy,
|
entry<Route.Login> {
|
||||||
entryProvider = entryProvider {
|
Login(
|
||||||
entry<Route.Login> {
|
onSuccess = {
|
||||||
Login(
|
backStack.clear()
|
||||||
onSuccess = {
|
backStack.add(Route.Sample)
|
||||||
backStack.clear()
|
|
||||||
backStack.add(Route.Sample)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
entry<Route.Sample> {
|
|
||||||
Sample(
|
|
||||||
navTest = {
|
|
||||||
backStack.add(Route.Navigator(it))
|
|
||||||
backStack.add(Route.Chat)
|
|
||||||
backStack.add(Route.MembersList)
|
|
||||||
},
|
|
||||||
onRequestLogout = {
|
|
||||||
backStack.clear()
|
|
||||||
backStack.add(Route.Login)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<Route.Navigator>(
|
|
||||||
metadata = ThreePaneSceneStrategy.listPane()
|
|
||||||
) { key ->
|
|
||||||
if (key.left) {
|
|
||||||
Navigator(
|
|
||||||
NavigatorPreviewProvider.base2.copy(
|
|
||||||
guildNavPosition = NavigatorModel.GuildNavPosition.LeftSidebar
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Navigator(
|
|
||||||
NavigatorPreviewProvider.base2.copy(
|
|
||||||
guildNavPosition = NavigatorModel.GuildNavPosition.BottomSheet
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
entry<Route.Sample> {
|
||||||
|
Sample(
|
||||||
|
navTest = {
|
||||||
|
backStack.add(Route.Navigator(it))
|
||||||
|
backStack.add(Route.Chat)
|
||||||
|
backStack.add(Route.MembersList)
|
||||||
|
},
|
||||||
|
onRequestLogout = {
|
||||||
|
backStack.clear()
|
||||||
|
backStack.add(Route.Login)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
entry<Route.Chat>(
|
entry<Route.Navigator>(
|
||||||
metadata = ThreePaneSceneStrategy.detailPane()
|
metadata = ThreePaneSceneStrategy.listPane()
|
||||||
) {
|
) { key ->
|
||||||
Chat(
|
if (key.left) {
|
||||||
onOpenMembers = { backStack.add(Route.MembersList) }
|
Navigator(
|
||||||
|
NavigatorPreviewProvider.base2.copy(
|
||||||
|
guildNavPosition = NavigatorModel.GuildNavPosition.LeftSidebar
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Navigator(
|
||||||
|
NavigatorPreviewProvider.base2.copy(
|
||||||
|
guildNavPosition = NavigatorModel.GuildNavPosition.BottomSheet
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
entry<Route.MembersList>(
|
|
||||||
metadata = ThreePaneSceneStrategy.extraPane()
|
|
||||||
) {
|
|
||||||
MembersList()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
entry<Route.Chat>(
|
||||||
|
metadata = ThreePaneSceneStrategy.detailPane()
|
||||||
|
) {
|
||||||
|
Chat(
|
||||||
|
onOpenMembers = { backStack.add(Route.MembersList) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<Route.MembersList>(
|
||||||
|
metadata = ThreePaneSceneStrategy.extraPane()
|
||||||
|
) {
|
||||||
|
MembersList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package moe.lava.neon.ui.di
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
|
||||||
import dev.zacsweers.metro.DependencyGraph
|
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import dev.zacsweers.metro.Provider
|
|
||||||
import dev.zacsweers.metro.SingleIn
|
|
||||||
import dev.zacsweers.metrox.viewmodel.ManualViewModelAssistedFactory
|
|
||||||
import dev.zacsweers.metrox.viewmodel.MetroViewModelFactory
|
|
||||||
import dev.zacsweers.metrox.viewmodel.ViewModelAssistedFactory
|
|
||||||
import dev.zacsweers.metrox.viewmodel.ViewModelGraph
|
|
||||||
import moe.lava.neon.core.di.AppGraph
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
@DependencyGraph(AppScope::class)
|
|
||||||
interface AppUiGraph : ViewModelGraph {
|
|
||||||
val core: AppGraph
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@ContributesBinding(AppScope::class)
|
|
||||||
@SingleIn(AppScope::class)
|
|
||||||
class AppViewModelFactory(
|
|
||||||
override val viewModelProviders: Map<KClass<out ViewModel>, Provider<ViewModel>>,
|
|
||||||
override val assistedFactoryProviders: Map<KClass<out ViewModel>, Provider<ViewModelAssistedFactory>>,
|
|
||||||
override val manualAssistedFactoryProviders: Map<KClass<out ManualViewModelAssistedFactory>, Provider<ManualViewModelAssistedFactory>>,
|
|
||||||
) : MetroViewModelFactory()
|
|
||||||
13
ui/src/commonMain/kotlin/moe/lava/neon/ui/di/KoinInit.kt
Normal file
13
ui/src/commonMain/kotlin/moe/lava/neon/ui/di/KoinInit.kt
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
package moe.lava.neon.ui.di
|
||||||
|
|
||||||
|
import org.koin.core.KoinApplication
|
||||||
|
import org.koin.core.context.startKoin
|
||||||
|
import org.koin.dsl.KoinAppDeclaration
|
||||||
|
import org.koin.dsl.includes
|
||||||
|
|
||||||
|
fun initKoin(config: KoinAppDeclaration? = null): KoinApplication {
|
||||||
|
return startKoin {
|
||||||
|
includes(config)
|
||||||
|
modules(uiModule)
|
||||||
|
}
|
||||||
|
}
|
||||||
13
ui/src/commonMain/kotlin/moe/lava/neon/ui/di/UiModule.kt
Normal file
13
ui/src/commonMain/kotlin/moe/lava/neon/ui/di/UiModule.kt
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
package moe.lava.neon.ui.di
|
||||||
|
|
||||||
|
import moe.lava.neon.core.di.coreModule
|
||||||
|
import moe.lava.neon.ui.screens.LoginViewModel
|
||||||
|
import moe.lava.neon.ui.screens.SampleViewModel
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import org.koin.plugin.module.dsl.viewModel
|
||||||
|
|
||||||
|
val uiModule = module {
|
||||||
|
includes(coreModule)
|
||||||
|
viewModel<LoginViewModel>()
|
||||||
|
viewModel<SampleViewModel>()
|
||||||
|
}
|
||||||
|
|
@ -23,11 +23,6 @@ import androidx.compose.ui.text.input.VisualTransformation
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.ContributesIntoMap
|
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import dev.zacsweers.metrox.viewmodel.ViewModelKey
|
|
||||||
import dev.zacsweers.metrox.viewmodel.metroViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import moe.lava.neon.core.repository.AuthRepository
|
import moe.lava.neon.core.repository.AuthRepository
|
||||||
import moe.lava.neon.core.repository.AuthResponse
|
import moe.lava.neon.core.repository.AuthResponse
|
||||||
|
|
@ -35,12 +30,13 @@ import moe.lava.neon.resources.Res
|
||||||
import moe.lava.neon.resources.visibility
|
import moe.lava.neon.resources.visibility
|
||||||
import moe.lava.neon.resources.visibility_off
|
import moe.lava.neon.resources.visibility_off
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Login(
|
fun Login(
|
||||||
onSuccess: () -> Unit,
|
onSuccess: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val viewModel: LoginViewModel = metroViewModel()
|
val viewModel: LoginViewModel = koinViewModel()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -113,9 +109,6 @@ fun Login(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
|
||||||
@ViewModelKey(LoginViewModel::class)
|
|
||||||
@ContributesIntoMap(AppScope::class)
|
|
||||||
class LoginViewModel(
|
class LoginViewModel(
|
||||||
private val auth: AuthRepository
|
private val auth: AuthRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,6 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.ContributesIntoMap
|
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import dev.zacsweers.metrox.viewmodel.ViewModelKey
|
|
||||||
import dev.zacsweers.metrox.viewmodel.metroViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import moe.lava.neon.core.api.gateway.GatewayHandler
|
import moe.lava.neon.core.api.gateway.GatewayHandler
|
||||||
import moe.lava.neon.core.repository.AuthRepository
|
import moe.lava.neon.core.repository.AuthRepository
|
||||||
|
|
@ -32,13 +27,14 @@ import moe.lava.neon.resources.Res
|
||||||
import moe.lava.neon.resources.compose_multiplatform
|
import moe.lava.neon.resources.compose_multiplatform
|
||||||
import moe.lava.neon.ui.Greeting
|
import moe.lava.neon.ui.Greeting
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Sample(
|
fun Sample(
|
||||||
navTest: (Boolean) -> Unit,
|
navTest: (Boolean) -> Unit,
|
||||||
onRequestLogout: () -> Unit,
|
onRequestLogout: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val viewModel: SampleViewModel = metroViewModel()
|
val viewModel: SampleViewModel = koinViewModel()
|
||||||
var showContent by remember { mutableStateOf(false) }
|
var showContent by remember { mutableStateOf(false) }
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -86,9 +82,6 @@ fun Sample(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
|
||||||
@ViewModelKey(SampleViewModel::class)
|
|
||||||
@ContributesIntoMap(AppScope::class)
|
|
||||||
class SampleViewModel(
|
class SampleViewModel(
|
||||||
private val auth: AuthRepository,
|
private val auth: AuthRepository,
|
||||||
private val gateway: GatewayHandler,
|
private val gateway: GatewayHandler,
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,16 @@ import androidx.compose.ui.unit.Density
|
||||||
import androidx.compose.ui.window.Window
|
import androidx.compose.ui.window.Window
|
||||||
import androidx.compose.ui.window.application
|
import androidx.compose.ui.window.application
|
||||||
import moe.lava.neon.ui.App
|
import moe.lava.neon.ui.App
|
||||||
|
import moe.lava.neon.ui.di.initKoin
|
||||||
|
|
||||||
// The UI is designed with touchscreens in mind; on desktop elements may look gigantic
|
// The UI is designed with touchscreens in mind; on desktop elements may look gigantic
|
||||||
// So scale them down a bit
|
// So scale them down a bit
|
||||||
const val scaleFactor = 0.75f
|
const val scaleFactor = 0.75f
|
||||||
|
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
|
initKoin {
|
||||||
|
printLogger()
|
||||||
|
}
|
||||||
Window(
|
Window(
|
||||||
onCloseRequest = ::exitApplication,
|
onCloseRequest = ::exitApplication,
|
||||||
title = "Neon",
|
title = "Neon",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue