refactor: split up core into multiple modules

This commit is contained in:
Cilly Leang 2026-02-05 01:05:02 +11:00
parent 2725342c3f
commit 0d84411f14
Signed by: cilly
GPG key ID: 6500251E087653C9
38 changed files with 344 additions and 149 deletions

View file

@ -0,0 +1,65 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
}
kotlin {
jvm()
androidTarget {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
sourceSets {
commonMain.dependencies {
implementation(project(":api:shared"))
implementation(libs.kermit)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.client.websockets)
implementation(libs.ktor.serialization.kotlinx.json)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
jvmMain.dependencies {
implementation(libs.ktor.client.okhttp)
}
androidMain.dependencies {
implementation(libs.ktor.client.okhttp)
}
}
}
dependencies {
coreLibraryDesugaring(libs.desugar)
}
android {
namespace = "moe.lava.neon.api.gateway"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
}
}

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api.gateway
package moe.lava.neon.api.gateway
@Suppress("unused")
object Capability {

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api.gateway
package moe.lava.neon.api.gateway
import io.ktor.websocket.CloseReason

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api.gateway
package moe.lava.neon.api.gateway
import co.touchlab.kermit.Logger
import kotlinx.coroutines.CoroutineScope
@ -6,15 +6,15 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.serialization.ExperimentalSerializationApi
import moe.lava.neon.core.api.gateway.handlers.EventHandlers
import moe.lava.neon.core.repository.AuthRepository
import moe.lava.neon.api.gateway.handlers.Handler
import kotlin.math.pow
import kotlin.reflect.KClass
import kotlin.time.Duration.Companion.seconds
class GatewayHandler(
private val auth: AuthRepository,
private val eventHandlers: EventHandlers,
) {
typealias EventHandlers = Map<KClass<out Event.Dispatch>, MutableList<Handler<in Event.Dispatch>>>
class GatewayHandler {
private val eventHandlers: EventHandlers = mutableMapOf()
private val logger = Logger.withTag("neon.core.api.gateway/handler")
private val scope = CoroutineScope(Dispatchers.IO)
private var session: GatewaySession? = null
@ -23,13 +23,11 @@ class GatewayHandler(
private var retryAttempts: Int = 0
@OptIn(ExperimentalSerializationApi::class)
suspend fun connect() {
suspend fun connect(token: String) {
if (session != null) {
logger.w(Throwable()) { "Attempted to connect, but client already connected, ignoring..." }
return
}
val token = auth.token
?: throw IllegalStateException("Tried to connect to gateway with no token")
session = GatewaySession.start(
token = token,
@ -59,7 +57,7 @@ class GatewayHandler(
logger.d { "Reconnecting in ${dur.inWholeMilliseconds}ms" }
delay(dur)
retryAttempts += 1
res = runCatching { connect() }
res = runCatching { connect(token) }
res.exceptionOrNull()?.let {
logger.e(it) { "Reconnect failed" }
}

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api.gateway
package moe.lava.neon.api.gateway
import co.touchlab.kermit.Logger
import io.ktor.client.HttpClient
@ -22,15 +22,14 @@ import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import moe.lava.neon.core.api.ApiConstants
import moe.lava.neon.core.api.ApiConstants.json
import moe.lava.neon.core.api.gateway.handlers.EventHandlers
import moe.lava.neon.api.ApiConstants
import moe.lava.neon.api.ApiConstants.json
import kotlin.random.Random
import kotlin.time.Duration.Companion.milliseconds
private val logger = Logger.withTag("neon.core.api.gateway/session")
class GatewaySession private constructor(
internal class GatewaySession private constructor(
private var ws: DefaultClientWebSocketSession,
private val token: String,
private val handlers: EventHandlers,
@ -100,19 +99,28 @@ class GatewaySession private constructor(
private suspend fun handlePayload(payload: Payload.Incoming<*>) {
logger.d { payload.toString() }
when (val event = payload.d) {
val event = payload.d
when (event) {
is Event.Heartbeat -> handleHeartbeat()
is Event.Reconnect -> close(GatewayCloseReason.ServerReconnect)
is Event.InvalidSession -> close(GatewayCloseReason.InvalidSession(event.resumable))
is Event.Hello -> handleHello(event)
is Event.HeartbeatAck -> { missedHeartbeats -= 1 }
is Event.Ready -> handlers.ready.handle(event) {
resumeProps = it
is Event.Ready -> {
resumeProps = ResumeProperties(
sessionId = event.sessionId,
resumeGatewayUrl = event.resumeGatewayUrl,
lastSequence = 0,
)
onSuccess()
}
is Event.Resumed -> onSuccess()
}
if (event is Event.Dispatch) {
val eventHandlers = handlers[event::class] ?: return
eventHandlers.forEach { it.handle(event) }
}
}
private suspend fun handleUnknownPayload(payload: Payload.Unknown) {

View file

@ -1,9 +1,9 @@
package moe.lava.neon.core.api.gateway
package moe.lava.neon.api.gateway
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import moe.lava.neon.core.api.ApiConstants
import moe.lava.neon.core.api.structures.User
import moe.lava.neon.api.ApiConstants
import moe.lava.neon.api.objects.User
sealed interface Payload {
val op: Int

View file

@ -1,6 +1,6 @@
package moe.lava.neon.core.api.gateway
package moe.lava.neon.api.gateway
data class ResumeProperties(
internal data class ResumeProperties(
val sessionId: String,
val resumeGatewayUrl: String,
val lastSequence: Int,

View file

@ -1,12 +1,10 @@
package moe.lava.neon.core.api.gateway
package moe.lava.neon.api.gateway
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.decodeFromJsonElement
import moe.lava.neon.core.api.ApiConstants
import moe.lava.neon.api.ApiConstants.json
private val json = ApiConstants.json
fun <T : Event.Outgoing> T.pack(): Payload.Outgoing<T> {
internal fun <T : Event.Outgoing> T.pack(): Payload.Outgoing<T> {
val opcode: Int = when (this) {
is Event.Heartbeat -> 1
is Event.Identify -> 2
@ -16,7 +14,7 @@ fun <T : Event.Outgoing> T.pack(): Payload.Outgoing<T> {
return Payload.Outgoing(op = opcode, d = this)
}
fun Payload.Unknown.asIncoming() : Payload.WithSequence {
internal fun Payload.Unknown.asIncoming() : Payload.WithSequence {
return when (op) {
0 -> when (t) {
"READY" -> decode<Event.Ready>()

View file

@ -0,0 +1,7 @@
package moe.lava.neon.api.gateway.handlers
import moe.lava.neon.api.gateway.Event
sealed interface Handler<T: Event.Dispatch> {
suspend fun handle(event: T)
}

65
api/rest/build.gradle.kts Normal file
View file

@ -0,0 +1,65 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
}
kotlin {
jvm()
androidTarget {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
sourceSets {
commonMain.dependencies {
implementation(project(":api:shared"))
implementation(project(":common"))
implementation(libs.kermit)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
jvmMain.dependencies {
implementation(libs.ktor.client.okhttp)
}
androidMain.dependencies {
implementation(libs.ktor.client.okhttp)
}
}
}
dependencies {
coreLibraryDesugaring(libs.desugar)
}
android {
namespace = "moe.lava.neon.api.rest"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
}
}

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api
package moe.lava.neon.api
import co.touchlab.kermit.Logger
import io.ktor.client.HttpClient
@ -15,10 +15,8 @@ import io.ktor.http.userAgent
import io.ktor.serialization.kotlinx.json.json
import io.ktor.util.appendAll
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy
import moe.lava.neon.core.api.captcha.CaptchaRequest
import moe.lava.neon.core.api.captcha.CaptchaResponse
import moe.lava.neon.common.captcha.CaptchaRequest
import moe.lava.neon.common.captcha.CaptchaResponse
class ApiClient {
private val logger = Logger.withTag("neon.core.api/client")
@ -81,20 +79,3 @@ class ApiClient {
}
}
}
@OptIn(ExperimentalSerializationApi::class)
fun buildApiClient() = HttpClient {
expectSuccess = true
install(ContentNegotiation) {
json(Json {
namingStrategy = JsonNamingStrategy.SnakeCase
ignoreUnknownKeys = true
})
}
install(WebSockets)
install(HttpCookies)
defaultRequest {
url("https://discord.com/api/v9/")
headers.appendAll(ApiConstants.baseHeaders)
}
}

View file

@ -0,0 +1,50 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
}
kotlin {
jvm()
androidTarget {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
sourceSets {
commonMain.dependencies {
implementation(libs.kotlinx.serialization.json)
}
}
}
dependencies {
coreLibraryDesugaring(libs.desugar)
}
android {
namespace = "moe.lava.neon.api"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
}
}

View file

@ -1,12 +1,14 @@
package moe.lava.neon.core.api
package moe.lava.neon.api
import android.annotation.SuppressLint
import android.os.Build
import java.util.Locale
@Suppress("ConstantLocale")
@SuppressLint("ConstantLocale")
internal actual val platformSuperProps = PlatformProps(
device = android.os.Build.DEVICE,
device = Build.DEVICE,
// TODO: this only outputs language but not country (e.g. en instead of en-AU)
// .toLanguageTag() is close, but returns too much junk (e.g. en-AU-u-fw-mon)
systemLocale = Locale.getDefault().language,
osVersion = "${android.os.Build.VERSION.SDK_INT}",
osVersion = "${Build.VERSION.SDK_INT}",
)

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api
package moe.lava.neon.api
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api.structures
package moe.lava.neon.api.objects
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.LongAsStringSerializer

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api.structures
package moe.lava.neon.api.objects
import kotlinx.serialization.Serializable

View file

@ -1,7 +1,8 @@
package moe.lava.neon.core.api
package moe.lava.neon.api
import java.util.Locale
// TODO
@Suppress("ConstantLocale")
internal actual val platformSuperProps = PlatformProps(
device = "",

14
common/build.gradle.kts Normal file
View file

@ -0,0 +1,14 @@
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
}
kotlin {
jvm()
sourceSets {
commonMain.dependencies {
implementation(libs.kotlinx.serialization.core)
}
}
}

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api.captcha
package moe.lava.neon.common.captcha
import kotlinx.serialization.Serializable

View file

@ -1,4 +1,4 @@
package moe.lava.neon.core.api.captcha
package moe.lava.neon.common.captcha
sealed class CaptchaResponse {
data class Success(val token: String) : CaptchaResponse()

View file

@ -18,9 +18,10 @@ kotlin {
sourceSets {
commonMain.dependencies {
implementation(project(":api:gateway"))
implementation(project(":api:rest"))
implementation(project(":common"))
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.client.websockets)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(project.dependencies.platform(libs.koin.bom))
@ -32,12 +33,6 @@ kotlin {
commonTest.dependencies {
implementation(libs.kotlin.test)
}
jvmMain.dependencies {
implementation(libs.ktor.client.okhttp)
}
androidMain.dependencies {
implementation(libs.ktor.client.okhttp)
}
}
}

View file

@ -3,7 +3,7 @@ package moe.lava.neon.core
import com.russhwolf.settings.Settings
import com.russhwolf.settings.nullableString
class AppSettings {
internal class AppSettings {
private val settings = Settings()
var fingerprint by settings.nullableString()

View file

@ -1,9 +0,0 @@
package moe.lava.neon.core.api.gateway.handlers
import moe.lava.neon.core.api.gateway.Event
sealed interface Handler<T: Event.Incoming>
class EventHandlers(
val ready: ReadyHandler
)

View file

@ -1,18 +0,0 @@
package moe.lava.neon.core.api.gateway.handlers
import co.touchlab.kermit.Logger
import moe.lava.neon.core.api.gateway.Event
import moe.lava.neon.core.api.gateway.ResumeProperties
private val logger = Logger.withTag("neon.core.api.events/ready")
class ReadyHandler : Handler<Event.Ready> {
fun handle(event: Event.Ready, updateResumeProps: (ResumeProperties) -> Unit) {
logger.i { "Received payload $event" }
updateResumeProps(ResumeProperties(
sessionId = event.sessionId,
resumeGatewayUrl = event.resumeGatewayUrl,
lastSequence = 0,
))
}
}

View file

@ -1,11 +1,11 @@
package moe.lava.neon.core.di
import moe.lava.neon.api.ApiClient
import moe.lava.neon.api.gateway.GatewayHandler
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.CaptchaRepository
import moe.lava.neon.core.repository.GatewayRepository
import moe.lava.neon.core.repository.UserRepository
import org.koin.dsl.module
import org.koin.plugin.module.dsl.single
@ -15,10 +15,9 @@ val coreModule = module {
single<AppSettings>()
single<AuthRepository>()
single<CaptchaRepository>()
single<GatewayRepository>()
single<UserRepository>()
single<GatewayHandler>()
single<ReadyHandler>()
single<EventHandlers>()
}

View file

@ -10,8 +10,8 @@ import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.contentType
import kotlinx.serialization.Serializable
import moe.lava.neon.api.ApiClient
import moe.lava.neon.core.AppSettings
import moe.lava.neon.core.api.ApiClient
@Serializable
private data class ExperimentResponse(
@ -43,16 +43,15 @@ sealed class AuthResponse {
// data class MFARequested() : AuthResponse()
}
class AuthRepository(
class AuthRepository internal constructor(
private val settings: AppSettings,
private val api: ApiClient,
) {
private val logger = Logger.withTag("neon.core.repo/auth")
var token by settings::token
private set
private var token by settings::token
private var fingerprint by settings::fingerprint
var fingerprint by settings::fingerprint
private set
val loggedIn get() = token != null
suspend fun login(
email: String,

View file

@ -0,0 +1,11 @@
package moe.lava.neon.core.repository
import moe.lava.neon.api.ApiClient
import moe.lava.neon.common.captcha.CaptchaRequest
import moe.lava.neon.common.captcha.CaptchaResponse
class CaptchaRepository(
private val api: ApiClient,
) {
fun setHandler(handler: suspend (CaptchaRequest) -> CaptchaResponse) = api.setCaptchaHandler(handler)
}

View file

@ -0,0 +1,18 @@
package moe.lava.neon.core.repository
import moe.lava.neon.api.gateway.GatewayHandler
import moe.lava.neon.core.AppSettings
class GatewayRepository internal constructor(
private val gateway: GatewayHandler,
private val settings: AppSettings,
) {
suspend fun start(): Result<Unit> = runCatching {
val token = settings.token
?: throw IllegalArgumentException("Tried to start gateway with no token")
gateway.connect(token)
}
suspend fun pause() = runCatching { gateway.disconnect() }
}

View file

@ -23,6 +23,7 @@ koin-bom = "4.2.0-RC1"
koin-plugin = "0.3.0"
kotlin = "2.3.0"
kotlinx-coroutines = "1.10.2"
kotlinx-serialization = "1.10.0"
ktor = "3.4.0"
material3 = "1.11.0-alpha02"
material3-adaptive = "1.3.0-alpha04"
@ -64,6 +65,8 @@ koin-test = { module = "io.insert-koin:koin-test" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", 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-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }

View file

@ -33,5 +33,9 @@ plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
include(":api:gateway")
include(":api:rest")
include(":api:shared")
include(":common")
include(":core")
include(":ui")

View file

@ -31,6 +31,7 @@ kotlin {
implementation(libs.ktor.client.okhttp)
}
commonMain.dependencies {
implementation(project(":common"))
implementation(project(":core"))
implementation(libs.compose.components.resources)
implementation(libs.compose.foundation)

View file

@ -19,8 +19,8 @@ import com.hcaptcha.sdk.HCaptchaVerifyParams
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import moe.lava.neon.core.api.ApiClient
import moe.lava.neon.core.api.captcha.CaptchaResponse
import moe.lava.neon.common.captcha.CaptchaRequest
import moe.lava.neon.common.captcha.CaptchaResponse
private val logger = Logger.withTag("neon.ui.app/captcha")
@ -31,7 +31,7 @@ private const val EXTRA_RESULT_TOKEN = "extra_result_token"
private const val EXTRA_RESULT_ERROR = "extra_result_error"
@Composable
actual fun CaptchaBinder(api: ApiClient) {
actual fun getCaptchaHandler(): suspend (CaptchaRequest) -> CaptchaResponse {
val context = LocalContext.current
val queue = MutableSharedFlow<Pair<String, CaptchaResponse>>()
val scope = rememberCoroutineScope()
@ -67,7 +67,7 @@ actual fun CaptchaBinder(api: ApiClient) {
}
}
api.setCaptchaHandler { captcha ->
return { captcha ->
val intent = Intent(context, HCaptchaActivity::class.java).apply {
putExtra(EXTRA_SITE_KEY, captcha.captchaSitekey)
putExtra(EXTRA_RQ_DATA, captcha.captchaRqdata)

View file

@ -17,6 +17,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import moe.lava.neon.core.repository.AuthRepository
import moe.lava.neon.core.repository.CaptchaRepository
import moe.lava.neon.ui.screens.Login
import moe.lava.neon.ui.screens.Sample
import moe.lava.neon.ui.screens.chat.Chat
@ -67,12 +68,15 @@ fun App() {
}
val auth: AuthRepository = koinInject()
CaptchaBinder(koinInject())
val captcha: CaptchaRepository = koinInject()
captcha.setHandler(getCaptchaHandler())
MaterialExpressiveTheme(
colorScheme = getColorScheme(),
motionScheme = MotionScheme.expressive(),
) {
val init = if (auth.token != null) Route.Sample else Route.Login
val init = if (auth.loggedIn) Route.Sample else Route.Login
// val backStack = rememberNavBackStack(config, init)
val backStack = rememberNavBackStack(config, Route.Sample)
val threePaneStrategy = rememberThreePaneSceneStrategy<NavKey>()
NavDisplay(

View file

@ -1,7 +0,0 @@
package moe.lava.neon.ui
import androidx.compose.runtime.Composable
import moe.lava.neon.core.api.ApiClient
@Composable
expect fun CaptchaBinder(api: ApiClient)

View file

@ -0,0 +1,8 @@
package moe.lava.neon.ui
import androidx.compose.runtime.Composable
import moe.lava.neon.common.captcha.CaptchaRequest
import moe.lava.neon.common.captcha.CaptchaResponse
@Composable
expect fun getCaptchaHandler(): suspend (CaptchaRequest) -> CaptchaResponse

View file

@ -21,8 +21,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import co.touchlab.kermit.Logger
import kotlinx.coroutines.launch
import moe.lava.neon.core.api.gateway.GatewayHandler
import moe.lava.neon.core.repository.AuthRepository
import moe.lava.neon.core.repository.GatewayRepository
import moe.lava.neon.resources.Res
import moe.lava.neon.resources.compose_multiplatform
import moe.lava.neon.ui.Greeting
@ -84,26 +84,24 @@ fun Sample(
class SampleViewModel(
private val auth: AuthRepository,
private val gateway: GatewayHandler,
private val gateway: GatewayRepository,
) : ViewModel() {
private val logger = Logger.withTag("neon.ui.screens/Sample")
val token get() = auth.token
fun connect() {
viewModelScope.launch {
try {
gateway.connect()
} catch(e: Throwable) {
logger.e(e) { "Failed to connect to gateway: ${e.stackTraceToString()}" }
val exception = gateway.start().exceptionOrNull()
if (exception != null) {
logger.e(exception) { "Failed to connect to gateway: ${exception.stackTraceToString()}" }
}
}
}
fun disconnect() {
viewModelScope.launch {
try {
gateway.disconnect()
} catch(e: Throwable) {
logger.e(e) { "Failed to connect to gateway: ${e.stackTraceToString()}" }
val exception = gateway.pause().exceptionOrNull()
if (exception != null) {
logger.e(exception) { "Failed to disconnect from gateway: ${exception.stackTraceToString()}" }
}
}
}

View file

@ -1,13 +0,0 @@
package moe.lava.neon.ui
import androidx.compose.runtime.Composable
import moe.lava.neon.core.api.ApiClient
import moe.lava.neon.core.api.captcha.CaptchaResponse
@Composable
// TODO
actual fun CaptchaBinder(api: ApiClient) {
api.setCaptchaHandler {
CaptchaResponse.Failed(NotImplementedError())
}
}

View file

@ -0,0 +1,13 @@
package moe.lava.neon.ui
import androidx.compose.runtime.Composable
import moe.lava.neon.common.captcha.CaptchaRequest
import moe.lava.neon.common.captcha.CaptchaResponse
@Composable
// TODO
actual fun getCaptchaHandler(): suspend (CaptchaRequest) -> CaptchaResponse {
return {
CaptchaResponse.Failed(NotImplementedError())
}
}