diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a8ea5167..53d5aaa2 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -54,12 +54,14 @@ android {
}
dependencies {
- implementation("androidx.core:core-splashscreen:1.0.0-beta01")
- implementation("androidx.activity:activity-compose:${Versions.Androidx.activityCompose}")
+ implementation("androidx.core:core-splashscreen:1.0.0-beta02")
implementation(projects.entry)
implementation(projects.common)
implementation(projects.common.gecko)
+ // Koin
+ implementation("io.insert-koin:koin-android:${Versions.koin}")
+
if (enableFirebase) {
implementation("com.google.firebase:firebase-analytics-ktx:${Versions.Firebase.analytics}")
implementation(platform("com.google.firebase:firebase-bom:${Versions.Firebase.bom}"))
diff --git a/app/src/main/java/com/dimension/maskbook/ComposeActivity.kt b/app/src/main/java/com/dimension/maskbook/ComposeActivity.kt
index a7aa364d..26a4b821 100644
--- a/app/src/main/java/com/dimension/maskbook/ComposeActivity.kt
+++ b/app/src/main/java/com/dimension/maskbook/ComposeActivity.kt
@@ -23,12 +23,10 @@ package com.dimension.maskbook
import android.content.Intent
import android.os.Bundle
import android.view.WindowManager
-import androidx.activity.compose.setContent
import androidx.compose.runtime.CompositionLocalProvider
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
-import androidx.fragment.app.FragmentActivity
import coil.compose.LocalImageLoader
import com.dimension.maskbook.common.gecko.PromptFeatureDelegate
import com.dimension.maskbook.common.gecko.WebContentController
@@ -36,10 +34,12 @@ import com.dimension.maskbook.common.manager.ImageLoaderManager
import com.dimension.maskbook.common.ui.widget.LocalWindowInsetsController
import com.dimension.maskbook.entry.ui.App
import com.google.accompanist.insets.ProvideWindowInsets
+import moe.tlaster.precompose.lifecycle.PreComposeActivity
+import moe.tlaster.precompose.lifecycle.setContent
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
-class ComposeActivity : FragmentActivity() {
+class ComposeActivity : PreComposeActivity() {
private lateinit var promptFeature: PromptFeatureDelegate
private val imageLoaderManager: ImageLoaderManager by inject()
diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt
index 9d29b109..08389e06 100644
--- a/buildSrc/src/main/kotlin/Config.kt
+++ b/buildSrc/src/main/kotlin/Config.kt
@@ -35,16 +35,6 @@ fun com.android.build.api.dsl.LibraryExtension.setupLibrary() {
sourceSets["debug"].java.srcDir("build/generated/ksp/android/androidDebug/kotlin")
}
-
-fun com.android.build.gradle.LibraryExtension.withCompose() {
- buildFeatures {
- compose = true
- }
- composeOptions {
- kotlinCompilerExtensionVersion = Versions.compose
- }
-}
-
fun Project.kspAndroid(dependencyNotation: Any) {
project.dependencies.add("kspAndroid", dependencyNotation)
}
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index ffca0021..b30f84f8 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -22,8 +22,7 @@ object Versions {
const val ksp = "${Kotlin.lang}-1.0.4"
const val spotless = "6.3.0"
const val ktlint = "0.43.2"
- const val compose_jb = "1.1.0"
- const val compose = "1.1.0"
+ const val compose_jb = "1.1.1"
const val accompanist = "0.23.0"
const val navigation = "2.4.1"
const val lifecycle = "2.4.1"
@@ -61,7 +60,6 @@ object Versions {
const val paging = "3.1.0"
const val pagingCompose = "1.0.0-alpha14"
const val annotation = "1.3.0"
- const val activityCompose = "1.4.0"
const val biometric = "1.2.0-alpha04"
const val activity = "1.4.0"
const val fragment = "1.3.6"
diff --git a/common/build.gradle.kts b/common/build.gradle.kts
index 122ec84d..6d77028c 100644
--- a/common/build.gradle.kts
+++ b/common/build.gradle.kts
@@ -10,29 +10,18 @@ kotlin {
android()
sourceSets {
val commonMain by getting {
- dependencies {
- implementation(projects.common.routeProcessor.annotations)
- kspAndroid(projects.common.routeProcessor)
- api(projects.common.bigDecimal)
- }
- }
- val commonTest by getting {
- dependencies {
- implementation(kotlin("test"))
- }
- }
- val androidMain by getting {
+ kotlin.srcDir("src/commonMain/route")
dependencies {
api(projects.wallet.export)
api(projects.labs.export)
api(projects.persona.export)
api(projects.setting.export)
api(projects.extension.export)
- api(projects.localization)
- api(projects.common.retrofit)
- api(projects.common.okhttp)
api(projects.common.bigDecimal)
+ implementation(projects.common.routeProcessor.annotations)
+ kspAndroid(projects.common.routeProcessor)
+
// Compose
api("org.jetbrains.compose.ui:ui:${Versions.compose_jb}")
api("org.jetbrains.compose.ui:ui-util:${Versions.compose_jb}")
@@ -43,16 +32,32 @@ kotlin {
api("org.jetbrains.compose.ui:ui-tooling:${Versions.compose_jb}")
// Koin
- api("io.insert-koin:koin-android:${Versions.koin}")
- api("io.insert-koin:koin-androidx-compose:${Versions.koin}")
+ api("io.insert-koin:koin-core:${Versions.koin}")
- // Lifecycle
- api("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}")
- api("androidx.lifecycle:lifecycle-livedata-ktx:${Versions.lifecycle}")
- api("androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.lifecycle}")
- api("androidx.lifecycle:lifecycle-viewmodel-savedstate:${Versions.lifecycle}")
- api("androidx.lifecycle:lifecycle-viewmodel-compose:${Versions.lifecycle}")
- api("androidx.lifecycle:lifecycle-common-java8:${Versions.lifecycle}")
+ // coroutines
+ api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
+
+ // serialization
+ api("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
+
+ // okhttp
+ api("com.squareup.okhttp3:okhttp:${Versions.okhttp}")
+ implementation("com.squareup.okhttp3:logging-interceptor:${Versions.okhttp}")
+
+ // retrofit
+ api("com.squareup.retrofit2:retrofit:${Versions.retrofit}")
+ api("com.squareup.retrofit2:converter-scalars:${Versions.retrofit}")
+ api("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:${Versions.retrofitSerialization}")
+ }
+ }
+ val commonTest by getting {
+ dependencies {
+ implementation(kotlin("test"))
+ }
+ }
+ val androidMain by getting {
+ dependencies {
+ api(projects.localization)
// Coil
api("io.coil-kt:coil-compose:${Versions.coil}")
@@ -62,28 +67,21 @@ kotlin {
api("com.google.accompanist:accompanist-pager:${Versions.accompanist}")
api("com.google.accompanist:accompanist-pager-indicators:${Versions.accompanist}")
api("com.google.accompanist:accompanist-swiperefresh:${Versions.accompanist}")
- api("com.google.accompanist:accompanist-navigation-animation:${Versions.accompanist}")
- api("com.google.accompanist:accompanist-navigation-material:${Versions.accompanist}")
api("com.google.accompanist:accompanist-permissions:${Versions.accompanist}")
api("com.google.accompanist:accompanist-insets:${Versions.accompanist}")
// coroutines
- api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
api("org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.Kotlin.coroutines}")
// Androidx
api("androidx.core:core-ktx:${Versions.Androidx.core}")
api("androidx.appcompat:appcompat:${Versions.Androidx.appcompat}")
api("androidx.activity:activity-ktx:${Versions.Androidx.activity}")
+ api("androidx.activity:activity-compose:${Versions.Androidx.activity}")
api("androidx.fragment:fragment-ktx:${Versions.Androidx.fragment}")
api("androidx.datastore:datastore-preferences:${Versions.datastore}")
- api("androidx.navigation:navigation-ui-ktx:${Versions.navigation}")
- api("androidx.navigation:navigation-compose:${Versions.navigation}")
implementation("androidx.biometric:biometric-ktx:${Versions.Androidx.biometric}")
- // serialization
- api("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
-
// sqlite
api("androidx.room:room-runtime:${Versions.Androidx.room}")
api("androidx.room:room-ktx:${Versions.Androidx.room}")
diff --git a/common/gecko/sample/build.gradle.kts b/common/gecko/sample/build.gradle.kts
index 47ec85d7..5d41fd81 100644
--- a/common/gecko/sample/build.gradle.kts
+++ b/common/gecko/sample/build.gradle.kts
@@ -32,7 +32,7 @@ android {
}
dependencies {
- implementation("androidx.activity:activity-compose:${Versions.Androidx.activityCompose}")
+ implementation("androidx.activity:activity-compose:${Versions.Androidx.activity}")
implementation("org.jetbrains.compose.ui:ui:${Versions.compose_jb}")
implementation("org.jetbrains.compose.ui:ui-util:${Versions.compose_jb}")
implementation("org.jetbrains.compose.foundation:foundation:${Versions.compose_jb}")
diff --git a/common/okhttp/build.gradle.kts b/common/okhttp/build.gradle.kts
deleted file mode 100644
index ba145bbe..00000000
--- a/common/okhttp/build.gradle.kts
+++ /dev/null
@@ -1,25 +0,0 @@
-plugins {
- kotlin("multiplatform")
- id("com.android.library")
-}
-
-kotlin {
- android()
- sourceSets {
- val androidMain by getting {
- dependencies {
- api("com.squareup.okhttp3:okhttp:${Versions.okhttp}")
- api("com.squareup.okhttp3:logging-interceptor:${Versions.okhttp}")
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.Kotlin.coroutines}")
- }
- }
- val androidTest by getting {
- dependencies {
- }
- }
- }
-}
-
-android {
- setupLibrary()
-}
diff --git a/common/okhttp/consumer-rules.pro b/common/okhttp/consumer-rules.pro
deleted file mode 100644
index e69de29b..00000000
diff --git a/common/okhttp/proguard-rules.pro b/common/okhttp/proguard-rules.pro
deleted file mode 100644
index 481bb434..00000000
--- a/common/okhttp/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/common/okhttp/src/androidMain/AndroidManifest.xml b/common/okhttp/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 2b9f24e6..00000000
--- a/common/okhttp/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/common/retrofit/build.gradle.kts b/common/retrofit/build.gradle.kts
deleted file mode 100644
index 4c4f0a3c..00000000
--- a/common/retrofit/build.gradle.kts
+++ /dev/null
@@ -1,28 +0,0 @@
-plugins {
- kotlin("multiplatform")
- id("com.android.library")
- kotlin("plugin.serialization")
-}
-
-kotlin {
- android()
- sourceSets {
- val androidMain by getting {
- dependencies {
- api(projects.common.okhttp)
- api("com.squareup.retrofit2:retrofit:${Versions.retrofit}")
- api("com.squareup.retrofit2:converter-scalars:${Versions.retrofit}")
- api("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:${Versions.retrofitSerialization}")
- api("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
- }
- }
- val androidTest by getting {
- dependencies {
- }
- }
- }
-}
-
-android {
- setupLibrary()
-}
diff --git a/common/retrofit/consumer-rules.pro b/common/retrofit/consumer-rules.pro
deleted file mode 100644
index e69de29b..00000000
diff --git a/common/retrofit/proguard-rules.pro b/common/retrofit/proguard-rules.pro
deleted file mode 100644
index 481bb434..00000000
--- a/common/retrofit/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/common/retrofit/src/androidMain/AndroidManifest.xml b/common/retrofit/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 0dafc6e7..00000000
--- a/common/retrofit/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/common/routeProcessor/src/main/java/com/dimension/maskbook/common/routeProcessor/RouteDefinition.kt b/common/routeProcessor/src/main/java/com/dimension/maskbook/common/routeProcessor/RouteDefinition.kt
index 5269fed7..40d14b96 100644
--- a/common/routeProcessor/src/main/java/com/dimension/maskbook/common/routeProcessor/RouteDefinition.kt
+++ b/common/routeProcessor/src/main/java/com/dimension/maskbook/common/routeProcessor/RouteDefinition.kt
@@ -85,19 +85,16 @@ internal data class ParameterRouteDefinition(
childRoute.forEach {
if (it is FunctionRouteDefinition) {
val pathParams = it.parameters.filter { !it.parameter.type.resolve().isMarkedNullable }
- val queryParams = it.parameters.filter { it.parameter.type.resolve().isMarkedNullable }
addProperty(
PropertySpec.builder("path", String::class)
.addModifiers(KModifier.CONST)
.initializer(
- "%S + %S + %S + %S + %S + %S + %S",
+ "%S + %S + %S + %S + %S",
parentPath,
RouteDivider,
name,
if (pathParams.any()) RouteDivider else "",
pathParams.joinToString(RouteDivider) { "{${it.name}}" },
- if (queryParams.any()) "?" else "",
- queryParams.joinToString("&") { "${it.name}={${it.name}}" }
)
.build()
)
diff --git a/common/routeProcessor/src/main/java/com/dimension/maskbook/common/routeProcessor/RouteGraphProcessor.kt b/common/routeProcessor/src/main/java/com/dimension/maskbook/common/routeProcessor/RouteGraphProcessor.kt
index 07ff9630..a4ed39d9 100644
--- a/common/routeProcessor/src/main/java/com/dimension/maskbook/common/routeProcessor/RouteGraphProcessor.kt
+++ b/common/routeProcessor/src/main/java/com/dimension/maskbook/common/routeProcessor/RouteGraphProcessor.kt
@@ -42,13 +42,12 @@ import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.buildCodeBlock
import com.squareup.kotlinpoet.ksp.KotlinPoetKspPreview
-import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.toTypeName
import com.squareup.kotlinpoet.ksp.writeTo
import com.squareup.kotlinpoet.withIndent
-private val navControllerType = ClassName("androidx.navigation", "NavController")
-private val navBackStackEntryType = ClassName("androidx.navigation", "NavBackStackEntry")
+private val navControllerType = ClassName("moe.tlaster.precompose.navigation", "NavController")
+private val navBackStackEntryType = ClassName("moe.tlaster.precompose.navigation", "BackStackEntry")
private const val navControllerName = "controller"
@OptIn(KotlinPoetKspPreview::class, KspExperimental::class)
@@ -93,14 +92,11 @@ internal class RouteGraphProcessor(
)
val packageName = data.first().packageName
FileSpec.builder(packageName.asString(), "RouteGraph")
- .addImport("androidx.navigation", "NavType")
- .addImport("androidx.navigation", "navDeepLink")
- .addImport("androidx.navigation", "navArgument")
.also { fileBuilder ->
fileBuilder.addFunction(
FunSpec.builder(generatedFunctionName)
.addModifiers(KModifier.INTERNAL)
- .receiver(ClassName("androidx.navigation", "NavGraphBuilder"))
+ .receiver(ClassName("moe.tlaster.precompose.navigation", "RouteBuilder"))
.addParameter(
navControllerName,
navControllerType,
@@ -133,42 +129,11 @@ internal class RouteGraphProcessor(
"route = %S,",
annotation.route,
)
- val parameters = ksFunctionDeclaration.parameters.filter {
- it.isAnnotationPresent(
- Query::class
- ) || it.isAnnotationPresent(Path::class)
- }
- if (parameters.isNotEmpty()) {
- addStatement("arguments = listOf(")
- withIndent {
- parameters.forEach {
- val type = it.type.resolve()
- val typeName = type.toClassName()
-
- val argumentName = when {
- it.isAnnotationPresent(Path::class) -> it.getAnnotationsByType(Path::class).first().name
- it.isAnnotationPresent(Query::class) -> it.getAnnotationsByType(Query::class).first().name
- else -> it.name?.asString().orEmpty()
- }
-
- addStatement(
- "navArgument(%S) { type = NavType.%NType; nullable = %L },",
- argumentName,
- if (typeName.isBoolean) "Bool" else type.declaration.simpleName.asString(),
- it.isAnnotationPresent(Query::class) && !typeName.isLong
- )
- }
- }
- addStatement("),")
- }
if (annotation.deeplink.isNotEmpty()) {
addStatement("deepLinks = listOf(")
withIndent {
annotation.deeplink.forEach {
- addStatement(
- "navDeepLink { uriPattern = %S }",
- it
- )
+ add("%S", it)
}
}
addStatement("),")
@@ -187,17 +152,17 @@ internal class RouteGraphProcessor(
if (it.isAnnotationPresent(Path::class)) {
val path = it.getAnnotationsByType(Path::class).first()
builder.addStatement(
- "val ${it.name?.asString()} = it.arguments!!.get(%S) as %T",
+ "val ${it.name?.asString()}: %T = it.path(%S)!!",
+ it.type.toTypeName(),
path.name,
- it.type.toTypeName()
)
} else if (it.isAnnotationPresent(Query::class)) {
val query =
it.getAnnotationsByType(Query::class).first()
builder.addStatement(
- "val ${it.name?.asString()} = it.arguments?.get(%S) as? %T",
+ "val ${it.name?.asString()}: %T = it.query(%S)",
+ it.type.toTypeName(),
query.name,
- it.type.toTypeName()
)
}
}
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/CommonSetup.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/CommonSetup.kt
index 20c0ca25..06d42ad0 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/CommonSetup.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/CommonSetup.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.common
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
import com.dimension.maskbook.common.manager.ImageLoaderManager
import com.dimension.maskbook.common.manager.KeyStoreManager
import com.dimension.maskbook.common.util.BiometricAuthenticator
@@ -32,12 +30,14 @@ import com.dimension.maskbook.common.viewmodel.SetUpPaymentPasswordViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
-import org.koin.androidx.viewmodel.dsl.viewModel
+import moe.tlaster.koin.viewModel
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.RouteBuilder
import org.koin.core.qualifier.named
import org.koin.dsl.module
object CommonSetup : ModuleSetup {
- override fun NavGraphBuilder.route(navController: NavController) {
+ override fun RouteBuilder.route(navController: NavController) {
}
override fun dependencyInject() = module {
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/IsDebug.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/IsDebug.kt
new file mode 100644
index 00000000..a7583cbc
--- /dev/null
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/IsDebug.kt
@@ -0,0 +1,23 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package com.dimension.maskbook.common
+
+actual val isDebug: Boolean = BuildConfig.DEBUG
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ModuleSetup.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ModuleSetup.kt
index 68e07959..6363435a 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ModuleSetup.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ModuleSetup.kt
@@ -20,15 +20,15 @@
*/
package com.dimension.maskbook.common
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.RouteBuilder
import org.koin.core.module.Module
interface ModuleSetup {
- fun NavGraphBuilder.route(navController: NavController)
+ fun RouteBuilder.route(navController: NavController)
fun dependencyInject(): Module
fun onExtensionReady() {}
}
-fun ModuleSetup.route(builder: NavGraphBuilder, navController: NavController) =
+fun ModuleSetup.route(builder: RouteBuilder, navController: NavController) =
builder.route(navController)
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/FlowExt.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/FlowExt.kt
index 98f46007..974055aa 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/FlowExt.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/FlowExt.kt
@@ -20,33 +20,12 @@
*/
package com.dimension.maskbook.common.ext
-import android.annotation.SuppressLint
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalLifecycleOwner
-import androidx.lifecycle.flowWithLifecycle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
-@Composable
-fun Flow.observeAsState(initial: T): State {
- val lifecycleOwner = LocalLifecycleOwner.current
- return remember(this, lifecycleOwner) {
- flowWithLifecycle(lifecycleOwner.lifecycle)
- }.collectAsState(initial = initial)
-}
-
-@SuppressLint("StateFlowValueCalledInComposition")
-@Composable
-fun StateFlow.observeAsState(): State {
- return observeAsState(initial = value)
-}
-
fun Flow.asStateIn(scope: CoroutineScope, initial: T) =
this.stateIn(
scope,
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/Json.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/Json.kt
index dc1de17c..fbb92aaf 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/Json.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/Json.kt
@@ -35,7 +35,7 @@ import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.long
-val JSON by lazy {
+val JSON: Json by lazy {
Json {
ignoreUnknownKeys = true
isLenient = true
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/KoinExt.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/KoinExt.kt
index e194bc3d..eede1bbf 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/KoinExt.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/KoinExt.kt
@@ -22,10 +22,9 @@ package com.dimension.maskbook.common.ext
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
-import androidx.lifecycle.ViewModel
-import androidx.navigation.NavController
-import org.koin.androidx.viewmodel.ViewModelOwner
-import org.koin.androidx.viewmodel.scope.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.viewmodel.ViewModel
import org.koin.core.annotation.KoinInternalApi
import org.koin.core.context.GlobalContext
import org.koin.core.parameter.ParametersDefinition
@@ -48,9 +47,9 @@ inline fun NavController.getNestedNavigationViewModel(
scope: Scope = GlobalContext.get().scopeRegistry.rootScope,
noinline parameters: ParametersDefinition? = null,
): T {
- return remember(route, qualifier, parameters) {
- val backStackEntry = getBackStackEntry(route)
- val owner = ViewModelOwner.from(backStackEntry, backStackEntry)
- scope.getViewModel(qualifier, { owner }, parameters)
+ return remember(route, qualifier, scope, parameters) {
+ val routeStack = getRouteStack(route)
+ ?: throw RuntimeException("not find routeStack with route: $route")
+ routeStack.getViewModel(qualifier, scope, parameters)
}
}
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/NavControllerExt.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/NavControllerExt.kt
index 21429d6d..749e8cde 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/NavControllerExt.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/NavControllerExt.kt
@@ -21,38 +21,76 @@
package com.dimension.maskbook.common.ext
import android.net.Uri
-import androidx.navigation.NavController
-import androidx.navigation.NavOptionsBuilder
-import androidx.navigation.navOptions
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.NavOptions
+import moe.tlaster.precompose.navigation.PopUpTo
-fun NavController.navigate(uri: Uri, builder: NavOptionsBuilder.() -> Unit) {
- navigate(uri, navOptions(builder))
+class NavOptionsBuilder internal constructor() {
+
+ var launchSingleTop = false
+
+ private var popUpTo: PopUpTo? = null
+
+ fun popUpTo(route: String, popUpBuilder: PopUpToBuilder.() -> Unit) {
+ popUpTo = PopUpToBuilder().apply(popUpBuilder).build(route)
+ }
+
+ fun build(): NavOptions {
+ return NavOptions(
+ launchSingleTop = launchSingleTop,
+ popUpTo = popUpTo,
+ )
+ }
+}
+
+class PopUpToBuilder internal constructor() {
+
+ var inclusive = false
+
+ fun build(route: String): PopUpTo {
+ return PopUpTo(
+ route = route,
+ inclusive = inclusive
+ )
+ }
+}
+
+fun navOptions(builder: NavOptionsBuilder.() -> Unit): NavOptions {
+ return NavOptionsBuilder().apply(builder).build()
+}
+
+fun NavController.navigate(route: String, builder: NavOptionsBuilder.() -> Unit) {
+ navigate(route, navOptions(builder))
+}
+
+fun NavController.navigateUri(uri: Uri, builder: NavOptionsBuilder.() -> Unit = {}) {
+ navigate(uri.toString(), navOptions(builder))
}
-fun NavController.navigateUri(uri: String, builder: NavOptionsBuilder.() -> Unit = {}) {
- navigate(Uri.parse(uri), navOptions(builder))
+fun NavController.navigateUri(uri: Uri, navOptions: NavOptions) {
+ navigate(uri.toString(), navOptions)
}
fun NavController.navigateWithPopSelf(route: String) {
navigate(route) {
- currentDestination?.id?.let { popId ->
- popUpTo(popId) { inclusive = true }
+ currentBackStackEntry?.route?.let { popRoute ->
+ popUpTo(popRoute.route) { inclusive = true }
}
}
}
-fun NavController.navigateUriWithPopSelf(uri: String) {
- navigate(Uri.parse(uri)) {
- currentDestination?.id?.let { popId ->
- popUpTo(popId) { inclusive = true }
+fun NavController.navigateUriWithPopSelf(uri: Uri) {
+ navigateUri(uri) {
+ currentBackStackEntry?.route?.let { popRoute ->
+ popUpTo(popRoute.route) { inclusive = true }
}
}
}
fun NavController.navigateToExtension(site: String? = null) {
- navigateUri(Deeplinks.WebContent(site)) {
+ navigate(Deeplinks.WebContent(site)) {
launchSingleTop = true
popUpTo(CommonRoute.Main.Home.path) {
inclusive = true
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/NavControllerResult.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/NavControllerResult.kt
index 015f724b..f6fb973b 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/NavControllerResult.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ext/NavControllerResult.kt
@@ -20,11 +20,6 @@
*/
package com.dimension.maskbook.common.ext
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelStoreOwner
-import androidx.lifecycle.viewModelScope
-import androidx.navigation.NavController
import com.dimension.maskbook.common.model.ResultEvent
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@@ -34,6 +29,11 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner
+import moe.tlaster.precompose.viewmodel.getViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
/**
* use for navigate result, use like:
@@ -78,15 +78,4 @@ private class EventResultViewModel : ViewModel() {
}
private val ViewModelStoreOwner.eventResultViewModel: EventResultViewModel
- get() {
- return ViewModelProvider(
- this, EventResultViewModelFactory
- )[EventResultViewModel::class.java]
- }
-
-private object EventResultViewModelFactory : ViewModelProvider.Factory {
- override fun create(modelClass: Class): T {
- @Suppress("UNCHECKED_CAST")
- return EventResultViewModel() as T
- }
-}
+ get() = viewModelStore.getViewModel { EventResultViewModel() }
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/route/Consts.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/route/Consts.kt
index e4151d84..ca425919 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/route/Consts.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/route/Consts.kt
@@ -20,21 +20,7 @@
*/
package com.dimension.maskbook.common.route
-import androidx.compose.animation.AnimatedVisibilityScope
-import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.compose.animation.slideInVertically
-import androidx.compose.animation.slideOutVertically
-import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.runtime.Composable
-import androidx.navigation.NamedNavArgument
-import androidx.navigation.NavBackStackEntry
-import androidx.navigation.NavDeepLink
-import androidx.navigation.NavGraphBuilder
-import com.google.accompanist.navigation.animation.composable
-import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
-import com.google.accompanist.navigation.material.bottomSheet
-
-const val navigationComposeDialogPackage = "androidx.navigation.compose"
+const val navigationComposeDialogPackage = "com.dimension.maskbook.common.route"
const val navigationComposeDialog = "dialog"
const val navigationComposeAnimComposablePackage = "com.dimension.maskbook.common.route"
@@ -45,56 +31,3 @@ const val navigationComposeModalComposable = "modalComposable"
const val navigationComposeBottomSheetPackage = "com.dimension.maskbook.common.route"
const val navigationComposeBottomSheet = "bottomSheet"
-
-@OptIn(ExperimentalAnimationApi::class)
-fun NavGraphBuilder.composable(
- route: String,
- arguments: List = emptyList(),
- deepLinks: List = emptyList(),
- content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
-) {
- composable(
- route = route,
- arguments = arguments,
- deepLinks = deepLinks,
- content = content,
- )
-}
-
-@OptIn(ExperimentalAnimationApi::class)
-fun NavGraphBuilder.modalComposable(
- route: String,
- arguments: List = emptyList(),
- deepLinks: List = emptyList(),
- content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
-) {
- composable(
- route = route,
- arguments = arguments,
- deepLinks = deepLinks,
- content = content,
- enterTransition = {
- slideInVertically { it }
- },
- exitTransition = null,
- popEnterTransition = null,
- popExitTransition = {
- slideOutVertically { it }
- },
- )
-}
-
-@OptIn(ExperimentalMaterialNavigationApi::class)
-fun NavGraphBuilder.bottomSheet(
- route: String,
- arguments: List = emptyList(),
- deepLinks: List = emptyList(),
- content: @Composable ColumnScope.(backstackEntry: NavBackStackEntry) -> Unit
-) {
- bottomSheet(
- route = route,
- arguments = arguments,
- deepLinks = deepLinks,
- content = content
- )
-}
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/route/RouteBuilder.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/route/RouteBuilder.kt
new file mode 100644
index 00000000..224b4aed
--- /dev/null
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/route/RouteBuilder.kt
@@ -0,0 +1,100 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package com.dimension.maskbook.common.route
+
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
+import androidx.compose.runtime.Composable
+import moe.tlaster.precompose.navigation.BackStackEntry
+import moe.tlaster.precompose.navigation.RouteBuilder
+import moe.tlaster.precompose.navigation.NavTransition
+
+fun RouteBuilder.composable(
+ route: String,
+ deepLinks: List = emptyList(),
+ content: @Composable (BackStackEntry) -> Unit
+) {
+ scene(
+ route = route,
+ deepLinks = deepLinks,
+ content = content,
+ )
+}
+
+@OptIn(ExperimentalAnimationApi::class)
+fun RouteBuilder.modalComposable(
+ route: String,
+ deepLinks: List = emptyList(),
+ content: @Composable (BackStackEntry) -> Unit
+) {
+ scene(
+ route = route,
+ deepLinks = deepLinks,
+ content = content,
+ navTransition = NavTransition(
+ enterTransition = {
+ slideInVertically { it }
+ },
+ exitTransition = NavTransition.NoneExit,
+ popEnterTransition = NavTransition.NoneEnter,
+ popExitTransition = {
+ slideOutVertically { it }
+ },
+ )
+ )
+}
+
+fun RouteBuilder.bottomSheet(
+ route: String,
+ deepLinks: List = emptyList(),
+ content: @Composable (BackStackEntry) -> Unit
+) {
+ bottomSheet(
+ route = route,
+ deepLinks = deepLinks,
+ content = content
+ )
+}
+
+fun RouteBuilder.dialog(
+ route: String,
+ deepLinks: List = emptyList(),
+ content: @Composable (BackStackEntry) -> Unit
+) {
+ dialog(
+ route = route,
+ deepLinks = deepLinks,
+ content = content,
+ )
+}
+
+fun RouteBuilder.navigation(
+ route: String,
+ startDestination: String,
+ builder: RouteBuilder.() -> Unit
+) {
+ navigation(
+ route = route,
+ initialRoute = startDestination,
+ builder = builder,
+ )
+}
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/notification/InAppNotification.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/notification/InAppNotification.kt
index 0227f619..144692f9 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/notification/InAppNotification.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/notification/InAppNotification.kt
@@ -22,8 +22,8 @@ package com.dimension.maskbook.common.ui.notification
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.ui.res.stringResource
-import com.dimension.maskbook.common.ext.observeAsState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
@@ -103,6 +103,6 @@ class InAppNotification {
}
@Composable
- fun observeAsState(initial: Event? = null) =
- source.observeAsState(initial = initial)
+ fun collectAsState(initial: Event? = null) =
+ source.collectAsState(initial = initial)
}
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/scene/SetupPaymentPassword.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/scene/SetupPaymentPassword.kt
index 3679dea4..b83f159d 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/scene/SetupPaymentPassword.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/scene/SetupPaymentPassword.kt
@@ -29,27 +29,27 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import com.dimension.maskbook.common.R
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.MaskModal
import com.dimension.maskbook.common.ui.widget.MaskPasswordInputField
import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.common.viewmodel.SetUpPaymentPasswordViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@Composable
fun SetUpPaymentPassword(
onNext: () -> Unit,
) {
val viewModel: SetUpPaymentPasswordViewModel = getViewModel()
- val newPassword by viewModel.newPassword.observeAsState(initial = "")
- val newPasswordConfirm by viewModel.newPasswordConfirm.observeAsState(initial = "")
- val canConfirm by viewModel.canConfirm.observeAsState(initial = false)
+ val newPassword by viewModel.newPassword.collectAsState(initial = "")
+ val newPasswordConfirm by viewModel.newPasswordConfirm.collectAsState(initial = "")
+ val canConfirm by viewModel.canConfirm.collectAsState(initial = false)
MaskModal(
title = {
Text(
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/scene/VerifyMnemonicWordsScene.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/scene/VerifyMnemonicWordsScene.kt
index 2f30adc9..8f82d5fa 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/scene/VerifyMnemonicWordsScene.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/scene/VerifyMnemonicWordsScene.kt
@@ -20,7 +20,6 @@
*/
package com.dimension.maskbook.common.ui.scene
-import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -72,6 +71,7 @@ import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.calculateCurrentOffsetForPage
import com.google.accompanist.pager.rememberPagerState
+import moe.tlaster.precompose.ui.BackHandler
import kotlin.math.absoluteValue
@OptIn(ExperimentalPagerApi::class, ExperimentalMaterialApi::class)
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/tab/TabScreen.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/tab/TabScreen.kt
index 131ac529..a73d1008 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/tab/TabScreen.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/tab/TabScreen.kt
@@ -21,7 +21,7 @@
package com.dimension.maskbook.common.ui.tab
import androidx.compose.runtime.Composable
-import androidx.navigation.NavController
+import moe.tlaster.precompose.navigation.NavController
interface TabScreen {
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/theme/IsDarkTheme.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/theme/IsDarkTheme.kt
index f13c3161..aaa8124c 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/theme/IsDarkTheme.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/theme/IsDarkTheme.kt
@@ -24,12 +24,12 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.staticCompositionLocalOf
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.setting.export.SettingServices
import com.dimension.maskbook.setting.export.model.Appearance
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
internal val LocalIsDarkTheme = staticCompositionLocalOf { false }
@@ -41,7 +41,7 @@ val MaterialTheme.isDarkTheme: Boolean
@Composable
internal fun isDarkTheme(): Boolean {
val repo = get()
- val appearance by repo.appearance.observeAsState(initial = Appearance.default)
+ val appearance by repo.appearance.collectAsState(initial = Appearance.default)
return when (appearance) {
Appearance.default -> isSystemInDarkTheme()
Appearance.light -> false
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/theme/MoreColors.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/theme/MoreColors.kt
index 6376a4bc..628bd169 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/theme/MoreColors.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/theme/MoreColors.kt
@@ -36,9 +36,9 @@ class MoreColors(
caption: Color,
onCaption: Color,
) {
- var caption by mutableStateOf(caption, structuralEqualityPolicy())
+ var caption: Color by mutableStateOf(caption, structuralEqualityPolicy())
internal set
- var onCaption by mutableStateOf(onCaption, structuralEqualityPolicy())
+ var onCaption: Color by mutableStateOf(onCaption, structuralEqualityPolicy())
internal set
}
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/MaskBottomSheetNavigator.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/MaskBottomSheetNavigator.kt
index 5f25f379..9ffdbd82 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/MaskBottomSheetNavigator.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/MaskBottomSheetNavigator.kt
@@ -22,22 +22,20 @@ package com.dimension.maskbook.common.ui.widget
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.SwipeableDefaults
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
-import com.google.accompanist.navigation.material.BottomSheetNavigator
-import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
-@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterialNavigationApi::class,)
+@OptIn(ExperimentalMaterialApi::class)
@Composable
fun rememberMaskBottomSheetNavigator(
animationSpec: AnimationSpec = SwipeableDefaults.AnimationSpec,
skipHalfExpanded: Boolean = true,
-): BottomSheetNavigator {
+): ModalBottomSheetState {
val sheetState = rememberModalBottomSheetState(
ModalBottomSheetValue.Hidden,
animationSpec
@@ -59,7 +57,5 @@ fun rememberMaskBottomSheetNavigator(
}
}
- return remember(sheetState) {
- BottomSheetNavigator(sheetState = sheetState)
- }
+ return sheetState
}
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/MaskInAppNotification.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/MaskInAppNotification.kt
index 42110ca4..206a7a30 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/MaskInAppNotification.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/MaskInAppNotification.kt
@@ -41,7 +41,7 @@ fun MaskInAppNotification(
) {
val inAppNotification = LocalInAppNotification.current
Log.d("MaskInAppNotification", "inAppNotification: $inAppNotification")
- val notification by inAppNotification.observeAsState(null)
+ val notification by inAppNotification.collectAsState(null)
val event = notification?.getContentIfNotHandled()
val message = event?.getMessage()
val actionMessage = event?.let {
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/RouteHost.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/RouteHost.kt
index e4abd950..b201d419 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/RouteHost.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/RouteHost.kt
@@ -24,32 +24,39 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
+import androidx.compose.material.ModalBottomSheetLayout
+import androidx.compose.material.ModalBottomSheetState
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavGraphBuilder
-import androidx.navigation.NavHostController
import com.dimension.maskbook.common.ui.theme.modalScrimColor
-import com.google.accompanist.navigation.animation.AnimatedNavHost
-import com.google.accompanist.navigation.animation.rememberAnimatedNavController
-import com.google.accompanist.navigation.material.BottomSheetNavigator
-import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
-import com.google.accompanist.navigation.material.ModalBottomSheetLayout
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.NavHost
+import moe.tlaster.precompose.navigation.NavTransition
+import moe.tlaster.precompose.navigation.RouteBuilder
+import moe.tlaster.precompose.navigation.rememberNavController
private const val navHostAnimationDurationMillis = 320
@ExperimentalAnimationApi
-@ExperimentalMaterialNavigationApi
+@ExperimentalMaterialApi
@Composable
fun RouteHost(
- bottomSheetNavigator: BottomSheetNavigator = rememberMaskBottomSheetNavigator(),
- navController: NavHostController = rememberAnimatedNavController(bottomSheetNavigator),
+ navController: NavController = rememberNavController(),
+ bottomSheetState: ModalBottomSheetState = rememberMaskBottomSheetNavigator(),
startDestination: String,
- builder: NavGraphBuilder.() -> Unit
+ builder: RouteBuilder.() -> Unit
) {
ModalBottomSheetLayout(
- bottomSheetNavigator,
+ sheetContent = navController.sheetContent ?: {
+ Box(Modifier.height(1.dp))
+ },
+ sheetState = bottomSheetState,
sheetBackgroundColor = MaterialTheme.colors.background,
sheetShape = MaterialTheme.shapes.large.copy(
bottomStart = CornerSize(0.dp),
@@ -57,41 +64,44 @@ fun RouteHost(
),
scrimColor = MaterialTheme.colors.modalScrimColor,
) {
- AnimatedNavHost(
+ NavHost(
navController = navController,
- startDestination = startDestination,
- enterTransition = {
- slideInHorizontally(
- initialOffsetX = { it },
- animationSpec = tween(
- navHostAnimationDurationMillis
+ bottomSheetState = bottomSheetState,
+ initialRoute = startDestination,
+ navTransition = NavTransition(
+ enterTransition = {
+ slideInHorizontally(
+ initialOffsetX = { it },
+ animationSpec = tween(
+ navHostAnimationDurationMillis
+ )
)
- )
- },
- exitTransition = {
- slideOutHorizontally(
- targetOffsetX = { -it },
- animationSpec = tween(
- navHostAnimationDurationMillis
+ },
+ exitTransition = {
+ slideOutHorizontally(
+ targetOffsetX = { -it },
+ animationSpec = tween(
+ navHostAnimationDurationMillis
+ )
)
- )
- },
- popEnterTransition = {
- slideInHorizontally(
- initialOffsetX = { -it },
- animationSpec = tween(
- navHostAnimationDurationMillis
+ },
+ popEnterTransition = {
+ slideInHorizontally(
+ initialOffsetX = { -it },
+ animationSpec = tween(
+ navHostAnimationDurationMillis
+ )
)
- )
- },
- popExitTransition = {
- slideOutHorizontally(
- targetOffsetX = { it },
- animationSpec = tween(
- navHostAnimationDurationMillis
+ },
+ popExitTransition = {
+ slideOutHorizontally(
+ targetOffsetX = { it },
+ animationSpec = tween(
+ navHostAnimationDurationMillis
+ )
)
- )
- },
+ },
+ ),
builder = builder,
)
}
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/WalletTokenImage.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/WalletTokenImage.kt
index 98f1ed9e..b7041ab4 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/WalletTokenImage.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/ui/widget/WalletTokenImage.kt
@@ -29,13 +29,13 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp
-import coil.compose.ImagePainter
@Composable
fun WalletTokenImage(
- painter: ImagePainter,
- chainPainter: ImagePainter,
+ painter: Painter,
+ chainPainter: Painter,
) {
Box {
Image(
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BaseMnemonicPhraseViewModel.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BaseMnemonicPhraseViewModel.kt
index 5dcef857..c1767ac0 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BaseMnemonicPhraseViewModel.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BaseMnemonicPhraseViewModel.kt
@@ -20,14 +20,14 @@
*/
package com.dimension.maskbook.common.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.BuildConfig
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.model.MnemonicWord
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
abstract class BaseMnemonicPhraseViewModel : ViewModel() {
protected val _words = MutableStateFlow(emptyList())
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BiometricEnableViewModel.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BiometricEnableViewModel.kt
index 287c8989..8342597f 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BiometricEnableViewModel.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BiometricEnableViewModel.kt
@@ -21,9 +21,9 @@
package com.dimension.maskbook.common.viewmodel
import android.content.Context
-import androidx.lifecycle.ViewModel
import com.dimension.maskbook.common.util.BiometricAuthenticator
import com.dimension.maskbook.setting.export.SettingServices
+import moe.tlaster.precompose.viewmodel.ViewModel
class BiometricEnableViewModel(
private val biometricAuthenticator: BiometricAuthenticator,
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BiometricViewModel.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BiometricViewModel.kt
index 93956e26..5acb2f90 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BiometricViewModel.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/BiometricViewModel.kt
@@ -21,8 +21,6 @@
package com.dimension.maskbook.common.viewmodel
import android.content.Context
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.R
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.util.BiometricAuthenticator
@@ -31,6 +29,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
open class BiometricViewModel(
private val biometricAuthenticator: BiometricAuthenticator,
diff --git a/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/SetUpPaymentPasswordViewModel.kt b/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/SetUpPaymentPasswordViewModel.kt
index c74b3c16..59d2af14 100644
--- a/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/SetUpPaymentPasswordViewModel.kt
+++ b/common/src/androidMain/kotlin/com/dimension/maskbook/common/viewmodel/SetUpPaymentPasswordViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.common.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.export.SettingServices
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class SetUpPaymentPasswordViewModel(
private val repository: SettingServices,
diff --git a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt
new file mode 100644
index 00000000..6abba122
--- /dev/null
+++ b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt
@@ -0,0 +1,142 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.lifecycle
+
+import android.view.ViewGroup
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionContext
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.ViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistryOwner
+import androidx.savedstate.ViewTreeSavedStateRegistryOwner
+import moe.tlaster.precompose.ui.BackDispatcher
+import moe.tlaster.precompose.ui.BackDispatcherOwner
+import moe.tlaster.precompose.ui.LocalBackDispatcherOwner
+import moe.tlaster.precompose.ui.LocalLifecycleOwner
+import moe.tlaster.precompose.ui.LocalViewModelStoreOwner
+import moe.tlaster.precompose.viewmodel.ViewModelStore
+import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner
+
+open class PreComposeActivity :
+ FragmentActivity(),
+ LifecycleOwner,
+ ViewModelStoreOwner,
+ androidx.lifecycle.LifecycleOwner,
+ SavedStateRegistryOwner,
+ BackDispatcherOwner,
+ androidx.lifecycle.LifecycleObserver {
+ override val lifecycle by lazy {
+ LifecycleRegistry()
+ }
+
+ override val viewModelStore by lazy {
+ ViewModelStore()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ lifecycle.currentState = Lifecycle.State.Active
+ }
+
+ override fun onPause() {
+ super.onPause()
+ lifecycle.currentState = Lifecycle.State.InActive
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ lifecycle.currentState = Lifecycle.State.Destroyed
+ }
+
+ override val backDispatcher by lazy {
+ BackDispatcher()
+ }
+
+ override fun onBackPressed() {
+ if (!backDispatcher.onBackPress()) {
+ super.onBackPressed()
+ }
+ }
+}
+
+fun PreComposeActivity.setContent(
+ parent: CompositionContext? = null,
+ content: @Composable () -> Unit
+) {
+ val existingComposeView = window.decorView
+ .findViewById(android.R.id.content)
+ .getChildAt(0) as? ComposeView
+
+ if (existingComposeView != null) with(existingComposeView) {
+ setParentCompositionContext(parent)
+ setContent {
+ ContentInternal(content)
+ }
+ } else ComposeView(this).apply {
+ // Set content and parent **before** setContentView
+ // to have ComposeView create the composition on attach
+ setParentCompositionContext(parent)
+ setContent {
+ ContentInternal(content)
+ }
+ // Set the view tree owners before setting the content view so that the inflation process
+ // and attach listeners will see them already present
+ setOwners()
+ setContentView(this, DefaultActivityContentLayoutParams)
+ }
+}
+
+private fun PreComposeActivity.setOwners() {
+ val decorView = window.decorView
+ if (ViewTreeLifecycleOwner.get(decorView) == null) {
+ ViewTreeLifecycleOwner.set(decorView, this)
+ }
+ if (ViewTreeSavedStateRegistryOwner.get(decorView) == null) {
+ ViewTreeSavedStateRegistryOwner.set(decorView, this)
+ }
+}
+
+@Composable
+private fun PreComposeActivity.ContentInternal(content: @Composable () -> Unit) {
+ ProvideAndroidCompositionLocals {
+ content.invoke()
+ }
+}
+
+@Composable
+private fun PreComposeActivity.ProvideAndroidCompositionLocals(
+ content: @Composable () -> Unit,
+) {
+ CompositionLocalProvider(
+ LocalLifecycleOwner provides this,
+ LocalViewModelStoreOwner provides this,
+ LocalBackDispatcherOwner provides this,
+ ) {
+ content.invoke()
+ }
+}
+
+private val DefaultActivityContentLayoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+)
diff --git a/common/src/commonMain/kotlin/com/dimension/maskbook/common/IsDebug.kt b/common/src/commonMain/kotlin/com/dimension/maskbook/common/IsDebug.kt
new file mode 100644
index 00000000..95b56823
--- /dev/null
+++ b/common/src/commonMain/kotlin/com/dimension/maskbook/common/IsDebug.kt
@@ -0,0 +1,23 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package com.dimension.maskbook.common
+
+expect val isDebug: Boolean
diff --git a/common/okhttp/src/androidMain/kotlin/com/dimension/maskbook/common/okhttp/Okhttp.kt b/common/src/commonMain/kotlin/com/dimension/maskbook/common/okhttp/Okhttp.kt
similarity index 92%
rename from common/okhttp/src/androidMain/kotlin/com/dimension/maskbook/common/okhttp/Okhttp.kt
rename to common/src/commonMain/kotlin/com/dimension/maskbook/common/okhttp/Okhttp.kt
index b55cc50e..601600f6 100644
--- a/common/okhttp/src/androidMain/kotlin/com/dimension/maskbook/common/okhttp/Okhttp.kt
+++ b/common/src/commonMain/kotlin/com/dimension/maskbook/common/okhttp/Okhttp.kt
@@ -20,7 +20,7 @@
*/
package com.dimension.maskbook.common.okhttp
-import android.util.Log
+import com.dimension.maskbook.common.isDebug
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
@@ -31,10 +31,10 @@ import java.io.IOException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
-val okHttpClient by lazy {
+val okHttpClient: OkHttpClient by lazy {
OkHttpClient.Builder()
.apply {
- if (BuildConfig.DEBUG) {
+ if (isDebug) {
addInterceptor(
HttpLoggingInterceptor(HttpLogger()).apply {
setLevel(HttpLoggingInterceptor.Level.BODY)
@@ -47,7 +47,8 @@ val okHttpClient by lazy {
class HttpLogger : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
- Log.i("HttpLogger", message)
+ // TODO
+ // Log.i("HttpLogger", message)
}
}
diff --git a/common/retrofit/src/androidMain/kotlin/com/dimension/maskbook/common/retrofit/Retrofit.kt b/common/src/commonMain/kotlin/com/dimension/maskbook/common/retrofit/Retrofit.kt
similarity index 100%
rename from common/retrofit/src/androidMain/kotlin/com/dimension/maskbook/common/retrofit/Retrofit.kt
rename to common/src/commonMain/kotlin/com/dimension/maskbook/common/retrofit/Retrofit.kt
diff --git a/common/src/commonMain/kotlin/moe/tlaster/koin/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/koin/ViewModel.kt
new file mode 100644
index 00000000..6267febc
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/koin/ViewModel.kt
@@ -0,0 +1,43 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.koin
+
+import moe.tlaster.precompose.viewmodel.ViewModel
+import org.koin.core.annotation.KoinReflectAPI
+import org.koin.core.definition.Definition
+import org.koin.core.instance.InstanceFactory
+import org.koin.core.instance.newInstance
+import org.koin.core.module.Module
+import org.koin.core.qualifier.Qualifier
+
+inline fun Module.viewModel(
+ qualifier: Qualifier? = null,
+ noinline definition: Definition
+): Pair> {
+ return factory(qualifier, definition)
+}
+
+@KoinReflectAPI
+inline fun Module.viewModel(
+ qualifier: Qualifier? = null
+): Pair> {
+ return factory(qualifier) { newInstance(it) }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/koin/compose/ComposeExt.kt b/common/src/commonMain/kotlin/moe/tlaster/koin/compose/ComposeExt.kt
new file mode 100644
index 00000000..4da83723
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/koin/compose/ComposeExt.kt
@@ -0,0 +1,48 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.koin.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import org.koin.core.Koin
+import org.koin.core.parameter.ParametersDefinition
+import org.koin.core.qualifier.Qualifier
+import org.koin.mp.KoinPlatformTools
+
+@Composable
+inline fun getRemember(
+ qualifier: Qualifier? = null,
+ noinline parameters: ParametersDefinition? = null,
+): T = remember(qualifier, parameters) {
+ get(qualifier, parameters)
+}
+
+@Composable
+fun getKoinRemember(): Koin = remember {
+ getKoin()
+}
+
+inline fun get(
+ qualifier: Qualifier? = null,
+ noinline parameters: ParametersDefinition? = null,
+): T = KoinPlatformTools.defaultContext().get().get(qualifier, parameters)
+
+fun getKoin(): Koin = KoinPlatformTools.defaultContext().get()
diff --git a/common/src/commonMain/kotlin/moe/tlaster/koin/compose/ViewModelExt.kt b/common/src/commonMain/kotlin/moe/tlaster/koin/compose/ViewModelExt.kt
new file mode 100644
index 00000000..a0d9c3ad
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/koin/compose/ViewModelExt.kt
@@ -0,0 +1,90 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.koin.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import moe.tlaster.precompose.ui.LocalViewModelStoreOwner
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner
+import moe.tlaster.precompose.viewmodel.getViewModel
+import org.koin.core.annotation.KoinInternalApi
+import org.koin.core.context.GlobalContext
+import org.koin.core.parameter.ParametersDefinition
+import org.koin.core.qualifier.Qualifier
+import org.koin.core.scope.Scope
+import kotlin.reflect.KClass
+
+@OptIn(KoinInternalApi::class)
+@Composable
+inline fun getViewModel(
+ qualifier: Qualifier? = null,
+ owner: ViewModelStoreOwner = LocalViewModelStoreOwner.current,
+ scope: Scope = GlobalContext.get().scopeRegistry.rootScope,
+ noinline parameters: ParametersDefinition? = null,
+): T {
+ return remember(qualifier, scope, parameters) {
+ owner.getViewModel(qualifier, scope, parameters)
+ }
+}
+
+// inline fun ViewModelStoreOwner.viewModel(
+// qualifier: Qualifier? = null,
+// mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED,
+// noinline parameters: ParametersDefinition? = null,
+// ): Lazy {
+// return lazy(mode) {
+// getViewModel(qualifier, parameters)
+// }
+// }
+//
+// fun ViewModelStoreOwner.viewModel(
+// qualifier: Qualifier? = null,
+// clazz: KClass,
+// mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED,
+// parameters: ParametersDefinition? = null,
+// ): Lazy {
+// return lazy(mode) { getViewModel(qualifier, clazz, parameters) }
+// }
+
+@OptIn(KoinInternalApi::class)
+inline fun ViewModelStoreOwner.getViewModel(
+ qualifier: Qualifier? = null,
+ scope: Scope = GlobalContext.get().scopeRegistry.rootScope,
+ noinline parameters: ParametersDefinition? = null,
+): T {
+ return getViewModel(qualifier, T::class, scope, parameters)
+}
+
+@OptIn(KoinInternalApi::class)
+fun ViewModelStoreOwner.getViewModel(
+ qualifier: Qualifier? = null,
+ clazz: KClass,
+ scope: Scope = GlobalContext.get().scopeRegistry.rootScope,
+ parameters: ParametersDefinition? = null,
+): T {
+ return this.viewModelStore.getViewModel(
+ key = qualifier?.value ?: (clazz.toString() + parameters?.invoke()),
+ clazz = clazz
+ ) {
+ scope.get(clazz, qualifier, parameters)
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt
new file mode 100644
index 00000000..f76381a7
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt
@@ -0,0 +1,35 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.lifecycle
+
+interface Lifecycle {
+ enum class State {
+ Initialized,
+ Active,
+ InActive,
+ Destroyed,
+ }
+
+ val currentState: State
+ fun removeObserver(observer: LifecycleObserver)
+ fun addObserver(observer: LifecycleObserver)
+ fun hasObserver(): Boolean
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt
new file mode 100644
index 00000000..aee3ce24
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt
@@ -0,0 +1,25 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.lifecycle
+
+interface LifecycleObserver {
+ fun onStateChanged(state: Lifecycle.State)
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt
new file mode 100644
index 00000000..542604a1
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt
@@ -0,0 +1,25 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.lifecycle
+
+interface LifecycleOwner {
+ val lifecycle: Lifecycle
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt
new file mode 100644
index 00000000..69a4fd53
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt
@@ -0,0 +1,52 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.lifecycle
+
+class LifecycleRegistry : Lifecycle {
+ private val observers = arrayListOf()
+ override var currentState: Lifecycle.State = Lifecycle.State.Initialized
+ set(value) {
+ if (field == Lifecycle.State.Destroyed || value == Lifecycle.State.Initialized) {
+ return
+ }
+ field = value
+ dispatchState(value)
+ }
+
+ private fun dispatchState(value: Lifecycle.State) {
+ observers.toMutableList().forEach {
+ it.onStateChanged(value)
+ }
+ }
+
+ override fun removeObserver(observer: LifecycleObserver) {
+ observers.remove(observer)
+ }
+
+ override fun addObserver(observer: LifecycleObserver) {
+ observers.add(observer)
+ observer.onStateChanged(currentState)
+ }
+
+ override fun hasObserver(): Boolean {
+ return observers.isNotEmpty()
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt
new file mode 100644
index 00000000..4ef9fb1e
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt
@@ -0,0 +1,162 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.lifecycle
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
+import kotlin.coroutines.resume
+
+/**
+ * Runs the given [block] in a new coroutine when `this` [Lifecycle] is at least at [state] and
+ * suspends the execution until `this` [Lifecycle] is [Lifecycle.State.Destroyed].
+ *
+ * The [block] will cancel and re-launch as the lifecycle moves in and out of the target state.
+ *
+ * ```
+ * class MyActivity : AppCompatActivity() {
+ * override fun onCreate(savedInstanceState: Bundle?) {
+ * /* ... */
+ * // Runs the block of code in a coroutine when the lifecycle is at least STARTED.
+ * // The coroutine will be cancelled when the ON_STOP event happens and will
+ * // restart executing if the lifecycle receives the ON_START event again.
+ * lifecycleScope.launch {
+ * lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ * uiStateFlow.collect { uiState ->
+ * updateUi(uiState)
+ * }
+ * }
+ * }
+ * }
+ * }
+ * ```
+ *
+ * The best practice is to call this function when the lifecycle is initialized. For
+ * example, `onCreate` in an Activity, or `onViewCreated` in a Fragment. Otherwise, multiple
+ * repeating coroutines doing the same could be created and be executed at the same time.
+ *
+ * Repeated invocations of `block` will run serially, that is they will always wait for the
+ * previous invocation to fully finish before re-starting execution as the state moves in and out
+ * of the required state.
+ *
+ * Warning: [Lifecycle.State.Initialized] is not allowed in this API. Passing it as a
+ * parameter will throw an [IllegalArgumentException].
+ *
+ * @param state [Lifecycle.State] in which `block` runs in a new coroutine. That coroutine
+ * will cancel if the lifecycle falls below that state, and will restart if it's in that state
+ * again.
+ */
+suspend fun Lifecycle.repeatOnLifecycle(
+ block: suspend CoroutineScope.() -> Unit
+) {
+ if (currentState === Lifecycle.State.Destroyed) {
+ return
+ }
+
+ // This scope is required to preserve context before we move to Dispatchers.Main
+ coroutineScope {
+ withContext(Dispatchers.Main.immediate) {
+ // Check the current state of the lifecycle as the previous check is not guaranteed
+ // to be done on the main thread.
+ if (currentState === Lifecycle.State.Destroyed) return@withContext
+
+ // Instance of the running repeating coroutine
+ var launchedJob: Job? = null
+
+ // Registered observer
+ var observer: LifecycleObserver? = null
+ try {
+ // Suspend the coroutine until the lifecycle is destroyed or
+ // the coroutine is cancelled
+ suspendCancellableCoroutine { cont ->
+ // Lifecycle observers that executes `block` when the lifecycle reaches certain state, and
+ // cancels when it falls below that state.
+ val mutex = Mutex()
+ observer = object : LifecycleObserver {
+ override fun onStateChanged(state: Lifecycle.State) {
+ when (state) {
+ Lifecycle.State.Initialized -> Unit
+ Lifecycle.State.Active -> {
+ launchedJob = this@coroutineScope.launch {
+ // Mutex makes invocations run serially,
+ // coroutineScope ensures all child coroutines finish
+ mutex.withLock {
+ coroutineScope {
+ block()
+ }
+ }
+ }
+ }
+ Lifecycle.State.InActive -> {
+ launchedJob?.cancel()
+ launchedJob = null
+ }
+ Lifecycle.State.Destroyed -> {
+ cont.resume(Unit)
+ }
+ }
+ }
+ }
+ this@repeatOnLifecycle.addObserver(observer as LifecycleObserver)
+ }
+ } finally {
+ launchedJob?.cancel()
+ observer?.let {
+ this@repeatOnLifecycle.removeObserver(it)
+ }
+ }
+ }
+ }
+}
+
+/**
+ * [LifecycleOwner]'s extension function for [Lifecycle.repeatOnLifecycle] to allow an easier
+ * call to the API from LifecycleOwners such as Activities and Fragments.
+ *
+ * ```
+ * class MyActivity : AppCompatActivity() {
+ * override fun onCreate(savedInstanceState: Bundle?) {
+ * /* ... */
+ * // Runs the block of code in a coroutine when the lifecycle is at least STARTED.
+ * // The coroutine will be cancelled when the ON_STOP event happens and will
+ * // restart executing if the lifecycle receives the ON_START event again.
+ * lifecycleScope.launch {
+ * repeatOnLifecycle(Lifecycle.State.STARTED) {
+ * uiStateFlow.collect { uiState ->
+ * updateUi(uiState)
+ * }
+ * }
+ * }
+ * }
+ * }
+ * ```
+ *
+ * @see Lifecycle.repeatOnLifecycle
+ */
+suspend fun LifecycleOwner.repeatOnLifecycle(
+ block: suspend CoroutineScope.() -> Unit
+): Unit = lifecycle.repeatOnLifecycle(block)
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt
new file mode 100644
index 00000000..6aba4286
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt
@@ -0,0 +1,98 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import moe.tlaster.precompose.lifecycle.Lifecycle
+import moe.tlaster.precompose.lifecycle.LifecycleOwner
+import moe.tlaster.precompose.lifecycle.LifecycleRegistry
+import moe.tlaster.precompose.viewmodel.ViewModelStore
+import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner
+
+class BackStackEntry internal constructor(
+ val id: Long,
+ val route: ComposeRoute,
+ val pathMap: Map,
+ val queryString: QueryString? = null,
+ internal val viewModel: NavControllerViewModel,
+) : ViewModelStoreOwner, LifecycleOwner {
+ private var destroyAfterTransition = false
+
+ override val viewModelStore: ViewModelStore
+ get() = viewModel.get(id = id)
+
+ private val lifecycleRegistry: LifecycleRegistry by lazy {
+ LifecycleRegistry()
+ }
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleRegistry
+
+ fun active() {
+ lifecycleRegistry.currentState = Lifecycle.State.Active
+ }
+
+ fun inActive() {
+ lifecycleRegistry.currentState = Lifecycle.State.InActive
+ if (destroyAfterTransition) {
+ destroy()
+ }
+ }
+
+ fun destroy() {
+ if (lifecycleRegistry.currentState != Lifecycle.State.InActive) {
+ destroyAfterTransition = true
+ } else {
+ lifecycleRegistry.currentState = Lifecycle.State.Destroyed
+ viewModel.clear(id)
+ }
+ }
+
+ inline fun path(path: String): T {
+ val value = requireNotNull(pathMap[path])
+ return convertValue(value)
+ }
+
+ inline fun query(name: String): T? {
+ return query(name, null)
+ }
+
+ inline fun query(name: String, default: T): T {
+ val value = queryString?.map?.get(name)?.firstOrNull() ?: return default
+ return convertValue(value)
+ }
+
+ inline fun queryList(name: String): List {
+ val value = queryString?.map?.get(name) ?: return emptyList()
+ return value.map { convertValue(it) }
+ }
+}
+
+inline fun convertValue(value: String): T {
+ return when (T::class) {
+ Int::class -> value.toIntOrNull()
+ Long::class -> value.toLongOrNull()
+ String::class -> value
+ Boolean::class -> value.toBooleanStrictOrNull()
+ Float::class -> value.toFloatOrNull()
+ Double::class -> value.toDoubleOrNull()
+ else -> throw NotImplementedError()
+ } as T
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt
new file mode 100644
index 00000000..b2c6d03e
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt
@@ -0,0 +1,54 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.ViewModelStore
+import moe.tlaster.precompose.viewmodel.getViewModel
+
+internal class NavControllerViewModel : ViewModel() {
+ private val viewModelStores = hashMapOf()
+
+ fun clear(id: Long) {
+ viewModelStores.remove(id)?.clear()
+ }
+
+ operator fun get(id: Long): ViewModelStore {
+ return viewModelStores.getOrPut(id) {
+ ViewModelStore()
+ }
+ }
+
+ override fun onCleared() {
+ viewModelStores.forEach {
+ it.value.clear()
+ }
+ viewModelStores.clear()
+ }
+
+ companion object {
+ fun create(viewModelStore: ViewModelStore): NavControllerViewModel {
+ return viewModelStore.getViewModel {
+ NavControllerViewModel()
+ }
+ }
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt
new file mode 100644
index 00000000..60e2c42a
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt
@@ -0,0 +1,194 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.AnimatedContentScope
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.with
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.ModalBottomSheetState
+import androidx.compose.material.ModalBottomSheetValue
+import androidx.compose.material.SwipeableDefaults
+import androidx.compose.material.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveableStateHolder
+import moe.tlaster.precompose.ui.LocalBackDispatcherOwner
+import moe.tlaster.precompose.ui.LocalLifecycleOwner
+import moe.tlaster.precompose.ui.LocalViewModelStoreOwner
+
+/**
+ * Provides in place in the Compose hierarchy for self contained navigation to occur.
+ *
+ * Once this is called, any Composable within the given [RouteBuilder] can be navigated to from
+ * the provided [RouteBuilder].
+ *
+ * The builder passed into this method is [remember]ed. This means that for this NavHost, the
+ * contents of the builder cannot be changed.
+ *
+ * @param navController the Navigator for this host
+ * @param initialRoute the route for the start destination
+ * @param builder the builder used to construct the graph
+ */
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+fun NavHost(
+ navController: NavController,
+ initialRoute: String,
+ navTransition: NavTransition = remember { NavTransition() },
+ builder: RouteBuilder.() -> Unit,
+) {
+ NavHost(
+ navController = navController,
+ initialRoute = initialRoute,
+ navTransition = navTransition,
+ bottomSheetState = rememberModalBottomSheetState(
+ ModalBottomSheetValue.Hidden,
+ SwipeableDefaults.AnimationSpec
+ ),
+ builder = builder,
+ )
+}
+@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
+@Composable
+fun NavHost(
+ navController: NavController,
+ initialRoute: String,
+ bottomSheetState: ModalBottomSheetState,
+ navTransition: NavTransition = remember { NavTransition() },
+ builder: RouteBuilder.() -> Unit,
+) {
+ val stateHolder = rememberSaveableStateHolder()
+ val manager = remember {
+ val graph = RouteBuilder(initialRoute = initialRoute).apply(builder).build()
+ RouteStackManager(graph, stateHolder, bottomSheetState).apply {
+ navController.stackManager = this
+ }
+ }
+
+ val lifecycleOwner = checkNotNull(LocalLifecycleOwner.current) {
+ "NavHost requires a LifecycleOwner to be provided via LocalLifecycleOwner"
+ }
+ val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
+ "NavHost requires a ViewModelStoreOwner to be provided via LocalViewModelStoreOwner"
+ }
+ val backDispatcher = LocalBackDispatcherOwner.current?.backDispatcher
+ DisposableEffect(manager, lifecycleOwner, viewModelStoreOwner, backDispatcher) {
+ manager.lifeCycleOwner = lifecycleOwner
+ manager.setViewModelStore(viewModelStoreOwner.viewModelStore)
+ manager.backDispatcher = backDispatcher
+ onDispose {
+ manager.lifeCycleOwner = null
+ }
+ }
+
+ LaunchedEffect(manager, initialRoute) {
+ manager.navigateInitial(initialRoute)
+ }
+
+ val currentStack = manager.currentStack
+ if (currentStack != null) {
+
+ val finalStackEnter: AnimatedContentScope.() -> EnterTransition = {
+ if (manager.isPop.value) {
+ (targetState.navTransition ?: navTransition).popEnterTransition.invoke(this)
+ } else {
+ (targetState.navTransition ?: navTransition).enterTransition.invoke(this)
+ }
+ }
+ val finalStackExit: AnimatedContentScope.() -> ExitTransition = {
+ if (manager.isPop.value) {
+ (targetState.navTransition ?: navTransition).popExitTransition.invoke(this)
+ } else {
+ (targetState.navTransition ?: navTransition).exitTransition.invoke(this)
+ }
+ }
+
+ val finalEntryEnter: AnimatedContentScope.() -> EnterTransition = {
+ if (manager.isPop.value) {
+ (targetState.route.navTransition ?: navTransition).popEnterTransition.invoke(this)
+ } else {
+ (targetState.route.navTransition ?: navTransition).enterTransition.invoke(this)
+ }
+ }
+ val finalEntryExit: AnimatedContentScope.() -> ExitTransition = {
+ if (manager.isPop.value) {
+ (targetState.route.navTransition ?: navTransition).popExitTransition.invoke(this)
+ } else {
+ (targetState.route.navTransition ?: navTransition).exitTransition.invoke(this)
+ }
+ }
+
+ AnimatedContent(
+ currentStack,
+ transitionSpec = {
+ finalStackEnter(this) with finalStackExit(this)
+ },
+ ) { stack ->
+ DisposableEffect(stack) {
+ stack.onActive()
+ onDispose {
+ stack.onInActive()
+ }
+ }
+
+ @Composable
+ fun initEntry(entry: BackStackEntry) {
+ DisposableEffect(entry) {
+ entry.active()
+ onDispose {
+ entry.inActive()
+ }
+ }
+
+ stateHolder.SaveableStateProvider(entry.id) {
+ CompositionLocalProvider(
+ LocalViewModelStoreOwner provides entry,
+ LocalLifecycleOwner provides entry,
+ ) {
+ entry.route.content(entry)
+ }
+ }
+ }
+
+ initEntry(stack.topEntry)
+
+ val currentEntry = stack.currentDialogEntry
+ if (currentEntry != null) {
+ AnimatedContent(
+ currentEntry,
+ transitionSpec = {
+ finalEntryEnter(this) with finalEntryExit(this)
+ },
+ ) { entry ->
+ initEntry(entry)
+ }
+ }
+ }
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt
new file mode 100644
index 00000000..4298c48b
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt
@@ -0,0 +1,38 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+/**
+ * [NavOptions] stores special options for navigate actions
+ */
+data class NavOptions(
+ /**
+ * Whether this navigation action should launch as single-top (i.e., there will be at most
+ * one copy of a given destination on the top of the back stack).
+ */
+ val launchSingleTop: Boolean = false,
+ /**
+ * The destination to pop up to before navigating. When set, all non-matching destinations
+ * should be popped from the back stack.
+ * @see [PopUpTo]
+ */
+ val popUpTo: PopUpTo? = null,
+)
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavTransition.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavTransition.kt
new file mode 100644
index 00000000..fcd7bcb6
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavTransition.kt
@@ -0,0 +1,57 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import androidx.compose.animation.AnimatedContentScope
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+
+/**
+ * Create a navigation transition
+ */
+@OptIn(ExperimentalAnimationApi::class)
+data class NavTransition(
+ /**
+ * Transition the scene that about to appear for the first time, similar to activity onCreate, factor from 0.0 to 1.0
+ */
+ val enterTransition: (AnimatedContentScope<*>.() -> EnterTransition) = { fadeIn(animationSpec = tween(700)) },
+ /**
+ * Transition the scene that about to disappear forever, similar to activity onDestroy, factor from 1.0 to 0.0
+ */
+ val exitTransition: (AnimatedContentScope<*>.() -> ExitTransition) = { fadeOut(animationSpec = tween(700)) },
+ /**
+ * Transition the scene that will be pushed into back stack, similar to activity onPause, factor from 1.0 to 0.0
+ */
+ val popEnterTransition: (AnimatedContentScope<*>.() -> EnterTransition) = enterTransition,
+ /**
+ * Transition the scene that about to show from the back stack, similar to activity onResume, factor from 0.0 to 1.0
+ */
+ val popExitTransition: (AnimatedContentScope<*>.() -> ExitTransition) = exitTransition,
+) {
+ companion object {
+ val NoneEnter: (AnimatedContentScope<*>.() -> EnterTransition) = { EnterTransition.None }
+ val NoneExit: (AnimatedContentScope<*>.() -> ExitTransition) = { ExitTransition.None }
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt
new file mode 100644
index 00000000..7d626388
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt
@@ -0,0 +1,131 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshotFlow
+
+/**
+ * Creates a [Navigator] that controls the [NavHost].
+ *
+ * @see NavHost
+ */
+@Composable
+fun rememberNavController(): NavController {
+ return remember { NavController() }
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+class NavController {
+ // FIXME: 2021/4/1 Temp workaround for deeplink
+ private var pendingNavigation: String? = null
+ internal var stackManager: RouteStackManager? = null
+ set(value) {
+ field = value
+ value?.let {
+ pendingNavigation?.let { it1 -> it.navigate(it1) }
+ }
+ }
+
+ val backQueue: List
+ get() = stackManager?.backStacks?.mapNotNull { it.currentEntry } ?: emptyList()
+
+ val currentBackStackEntry: BackStackEntry?
+ get() = stackManager?.currentEntry
+
+ val sheetContent: (@Composable ColumnScope.() -> Unit)?
+ get() = stackManager?.sheetContent
+
+ fun getRouteStack(route: String): RouteStack? {
+ return stackManager?.getRouteStack(route)
+ }
+
+ /**
+ * Navigate to a route in the current RouteGraph.
+ *
+ * @param route route for the destination
+ * @param options navigation options for the destination
+ */
+ fun navigate(route: String, options: NavOptions? = null) {
+ stackManager?.navigate(route, options) ?: run {
+ pendingNavigation = route
+ }
+ }
+
+ suspend fun navigateForResult(route: String, options: NavOptions? = null): Any? {
+ stackManager?.navigate(route, options) ?: run {
+ pendingNavigation = route
+ return null
+ }
+ val currentEntry = stackManager?.currentEntry ?: return null
+ return stackManager?.waitingForResult(currentEntry)
+ }
+
+ /**
+ * Attempts to navigate up in the navigation hierarchy. Suitable for when the
+ * user presses the "Up" button marked with a left (or start)-facing arrow in the upper left
+ * (or starting) corner of the app UI.
+ */
+ fun goBack(route: String? = null, inclusive: Boolean = false) {
+ stackManager?.goBack(
+ route = route,
+ inclusive = inclusive,
+ )
+ }
+
+ fun goBackWith(result: Any? = null) {
+ stackManager?.goBack(result = result)
+ }
+
+ /**
+ * Compatibility layer for Jetpack Navigation
+ */
+ fun popBackStack() {
+ goBack()
+ }
+
+ fun popBackStack(route: String, inclusive: Boolean = false) {
+ goBack(route, inclusive)
+ }
+
+ /**
+ * Check if navigator can navigate up
+ */
+ val canGoBack: Boolean
+ get() = stackManager?.canGoBack ?: false
+}
+
+@Composable
+fun NavController.currentBackStackEntryAsState(): State {
+ val currentNavBackStackEntry = remember { mutableStateOf(stackManager?.currentEntry) }
+ LaunchedEffect(this) {
+ snapshotFlow { stackManager?.currentEntry }.collect {
+ currentNavBackStackEntry.value = it
+ }
+ }
+ return currentNavBackStackEntry
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt
new file mode 100644
index 00000000..19eb69f0
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt
@@ -0,0 +1,32 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+data class PopUpTo(
+ /**
+ * The `popUpTo` destination, if it's an empty string will clear all backstack
+ */
+ val route: String,
+ /**
+ * Whether the `popUpTo` destination should be popped from the back stack.
+ */
+ val inclusive: Boolean = false
+)
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt
new file mode 100644
index 00000000..c0ca78d1
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt
@@ -0,0 +1,42 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+data class QueryString(
+ private val rawInput: String,
+) {
+ val map by lazy {
+ rawInput
+ .split("?")
+ .lastOrNull()
+ .let { it ?: "" }
+ .split("&")
+ .asSequence()
+ .map { it.split("=") }
+ .filter { !it.firstOrNull().isNullOrEmpty() }
+ .filter { it.size in 1..2 }
+ .map { it[0] to it.elementAtOrNull(1) }
+ .groupBy { it.first }
+ .map { it.key to it.value.mapNotNull { it.second.takeIf { !it.isNullOrEmpty() } } }
+ .toList()
+ .toMap()
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Route.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Route.kt
new file mode 100644
index 00000000..00f2dcef
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Route.kt
@@ -0,0 +1,63 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import androidx.compose.runtime.Composable
+
+interface Route {
+ val route: String
+}
+
+interface ComposeRoute : Route {
+ val content: @Composable (BackStackEntry) -> Unit
+ val deepLinks: List get() = emptyList()
+ val navTransition: NavTransition? get() = null
+}
+
+internal class NavigationRoute(
+ override val route: String,
+ val graph: RouteGraph,
+ val initialRoute: ComposeRoute,
+) : ComposeRoute {
+ override val content: (BackStackEntry) -> Unit
+ get() = {}
+}
+
+internal class SceneRoute(
+ override val route: String,
+ override val deepLinks: List = emptyList(),
+ override val navTransition: NavTransition? = null,
+ override val content: @Composable (BackStackEntry) -> Unit,
+) : ComposeRoute
+
+internal class DialogRoute(
+ override val route: String,
+ override val deepLinks: List = emptyList(),
+ override val navTransition: NavTransition? = null,
+ override val content: @Composable (BackStackEntry) -> Unit
+) : ComposeRoute
+
+internal class BottomSheetRoute(
+ override val route: String,
+ override val deepLinks: List = emptyList(),
+ override val navTransition: NavTransition? = null,
+ override val content: @Composable (BackStackEntry) -> Unit
+) : ComposeRoute
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt
new file mode 100644
index 00000000..e3dbfcb2
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt
@@ -0,0 +1,119 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import androidx.compose.runtime.Composable
+
+class RouteBuilder(
+ private val initialRoute: String,
+) {
+ private val route = arrayListOf()
+
+ /**
+ * Add the scene [Composable] to the [RouteBuilder]
+ * @param route route for the destination
+ * @param navTransition navigation transition for current scene
+ * @param content composable for the destination
+ */
+ fun scene(
+ route: String,
+ deepLinks: List = emptyList(),
+ navTransition: NavTransition? = null,
+ content: @Composable (BackStackEntry) -> Unit,
+ ) {
+ this.route += SceneRoute(
+ route = route,
+ deepLinks = deepLinks,
+ navTransition = navTransition,
+ content = content,
+ )
+ }
+
+ /**
+ * Add the scene [Composable] to the [RouteBuilder], which will show over the scene
+ * @param route route for the destination
+ * @param content composable for the destination
+ */
+ fun dialog(
+ route: String,
+ deepLinks: List = emptyList(),
+ navTransition: NavTransition? = null,
+ content: @Composable (BackStackEntry) -> Unit,
+ ) {
+ this.route += DialogRoute(
+ route = route,
+ deepLinks = deepLinks,
+ navTransition = navTransition,
+ content = content,
+ )
+ }
+
+ fun bottomSheet(
+ route: String,
+ deepLinks: List = emptyList(),
+ navTransition: NavTransition? = null,
+ content: @Composable (BackStackEntry) -> Unit,
+ ) {
+ this.route += BottomSheetRoute(
+ route = route,
+ deepLinks = deepLinks,
+ navTransition = navTransition,
+ content = content,
+ )
+ }
+
+ fun navigation(
+ route: String,
+ initialRoute: String,
+ builder: RouteBuilder.() -> Unit
+ ) {
+ val graph = RouteBuilder(initialRoute).apply(builder).build()
+ val targetRoute = requireNotNull(graph.routes.find { it.route == initialRoute }) {
+ "not find initialRoute:$initialRoute in navigation"
+ }
+ require(targetRoute is ComposeRoute) {
+ "navigation initialRoute:${initialRoute} is not ComposeRoute"
+ }
+ this.route += NavigationRoute(
+ route = route,
+ graph = graph,
+ initialRoute = targetRoute,
+ )
+ }
+
+ fun addRoute(route: Route) {
+ this.route += route
+ }
+
+ internal fun build(): RouteGraph {
+ if (initialRoute.isEmpty() && route.isEmpty()) {
+ // FIXME: 2021/4/2 Show warning
+ } else {
+ require(route.any { it.route == initialRoute }) {
+ "No initial route target fot this route graph"
+ }
+ }
+ require(!route.groupBy { it.route }.any { it.value.size > 1 }) {
+ "Duplicate route can not be applied"
+ }
+ return RouteGraph(initialRoute, route.toList())
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt
new file mode 100644
index 00000000..30873580
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt
@@ -0,0 +1,57 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+internal data class RouteGraph(
+ val initialRoute: String,
+ val routes: List,
+) {
+ private val routeParser: RouteParser by lazy {
+ fun matchRoute(route: Route): List {
+ val matches = RouteParser.expandOptionalVariables(route.route)
+ if (route !is ComposeRoute) return matches
+
+ return if (route is NavigationRoute) {
+ matches + route.graph.routes.flatMapTo(mutableListOf()) { childRoute ->
+ matchRoute(childRoute)
+ }
+ } else if (route.deepLinks.isNotEmpty()) {
+ matches + route.deepLinks.flatMap {
+ RouteParser.expandOptionalVariables(it)
+ }
+ } else {
+ matches
+ }
+ }
+
+ RouteParser().apply {
+ routes.asSequence()
+ .map { route -> matchRoute(route) to route }
+ .flatMap { it.first.map { route -> route to it.second } }
+ .forEach { insert(it.first, it.second) }
+ }
+ }
+
+ fun findRoute(route: String): RouteMatchResult? {
+ val routePath = route.substringBefore('?')
+ return routeParser.find(path = routePath)
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt
new file mode 100644
index 00000000..9372a45a
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt
@@ -0,0 +1,63 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import kotlin.math.min
+
+internal class RouteMatch {
+ var matches = false
+ var route: ComposeRoute? = null
+ var vars = arrayListOf()
+ var keys = arrayListOf()
+ var pathMap = linkedMapOf()
+ fun key() {
+ val size = min(keys.size, vars.size)
+ for (i in 0 until size) {
+ pathMap[keys[i]] = vars[i]
+ }
+ for (i in 0 until size) {
+ vars.removeFirst()
+ }
+ }
+
+ fun truncate(size: Int) {
+ var sizeInt = size
+ while (sizeInt < vars.size) {
+ vars.removeAt(sizeInt++)
+ }
+ }
+
+ fun value(value: String) {
+ vars.add(value)
+ }
+
+ fun pop() {
+ if (vars.isNotEmpty()) {
+ vars.removeLast()
+ }
+ }
+
+ fun found(route: ComposeRoute): RouteMatch {
+ this.route = route
+ matches = true
+ return this
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt
new file mode 100644
index 00000000..c5ea0e7f
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt
@@ -0,0 +1,26 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+internal data class RouteMatchResult(
+ val route: Route,
+ val pathMap: Map = emptyMap(),
+)
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt
new file mode 100644
index 00000000..56bdee03
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt
@@ -0,0 +1,694 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import kotlin.math.min
+
+internal class RouteParser {
+ data class Segment(
+ val nodeType: Int = 0,
+ val rexPat: String = "",
+ val tail: Char = 0.toChar(),
+ val startIndex: Int = 0,
+ val endIndex: Int = 0,
+ )
+
+ private data class Node(
+ var type: Int = 0,
+ var prefix: String = "",
+ var label: Char = 0.toChar(),
+ var tail: Char = 0.toChar(),
+ var rex: Regex? = null,
+ var paramsKey: String? = null,
+ var route: Route? = null,
+ ) : Comparable {
+
+ // subroutes on the leaf node
+ // Routes subroutes;
+ // child nodes should be stored in-order for iteration,
+ // in groups of the node type.
+ var children = linkedMapOf>()
+ override operator fun compareTo(other: Node): Int {
+ return label - other.label
+ }
+
+ fun insertRoute(pattern: String, route: Route): Node {
+ var n = this
+ var parent: Node
+ var search = pattern
+ while (true) {
+ // Handle key exhaustion
+ if (search.isEmpty()) {
+ // Insert or update the node's leaf handler
+ n.applyRoute(route)
+ return n
+ }
+
+ // We're going to be searching for a wild node next,
+ // in this case, we need to get the tail
+ val label = search[0]
+ // char segTail;
+ // int segEndIdx;
+ // int segTyp;
+ val seg = if (label == '{' || label == '*') {
+ // segTyp, _, segRexpat, segTail, _, segEndIdx = patNextSegment(search)
+ patNextSegment(search)
+ } else {
+ Segment()
+ }
+ val prefix = if (seg.nodeType == ntRegexp) {
+ seg.rexPat
+ } else {
+ ""
+ }
+
+ // Look for the edge to attach to
+ parent = n
+ n = n.getEdge(seg.nodeType, label, seg.tail, prefix) ?: run {
+ val child = Node(label = label, tail = seg.tail, prefix = search)
+ val hn = parent.addChild(child, search)
+ hn.applyRoute(route)
+ return hn
+ }
+
+ // Found an edge to newRuntimeRoute the pattern
+ if (n.type > ntStatic) {
+ // We found a param node, trim the param from the search path and continue.
+ // This param/wild pattern segment would already be on the tree from a previous
+ // call to addChild when creating a new node.
+ search = search.substring(seg.endIndex)
+ continue
+ }
+
+ // Static nodes fall below here.
+ // Determine longest prefix of the search key on newRuntimeRoute.
+ val commonPrefix = longestPrefix(search, n.prefix)
+ if (commonPrefix == n.prefix.length) {
+ // the common prefix is as long as the current node's prefix we're attempting to insert.
+ // keep the search going.
+ search = search.substring(commonPrefix)
+ continue
+ }
+
+ // Split the node
+ val child = Node(type = ntStatic, prefix = search.substring(0, commonPrefix))
+ parent.replaceChild(search[0], seg.tail, child)
+
+ // Restore the existing node
+ n.label = n.prefix[commonPrefix]
+ n.prefix = n.prefix.substring(commonPrefix)
+ child.addChild(n, n.prefix)
+
+ // If the new key is a subset, set the route on this node and finish.
+ search = search.substring(commonPrefix)
+ if (search.isEmpty()) {
+ child.applyRoute(route)
+ return child
+ }
+
+ // Create a new edge for the node
+ val subchild = Node(type = ntStatic, label = search[0], prefix = search)
+ val hn = child.addChild(subchild, search)
+ hn.applyRoute(route)
+ return hn
+ }
+ }
+
+ // addChild appends the new `child` node to the tree using the `pattern` as the trie key.
+ // For a URL router like chi's, we split the static, param, regexp and wildcard segments
+ // into different nodes. In addition, addChild will recursively call itself until every
+ // pattern segment is added to the url pattern tree as individual nodes, depending on type.
+ fun addChild(child: Node, search: String): Node {
+ var searchStr = search
+ val n = this
+ // String search = prefix.toString();
+
+ // handler leaf node added to the tree is the child.
+ // this may be overridden later down the flow
+ var hn = child
+
+ // Parse next segment
+ // segTyp, _, segRexpat, segTail, segStartIdx, segEndIdx := patNextSegment(search)
+ val seg = patNextSegment(searchStr)
+ val segTyp = seg.nodeType
+ var segStartIdx = seg.startIndex
+ val segEndIdx = seg.endIndex
+ when (segTyp) {
+ ntStatic -> {
+ }
+ else -> {
+ // Search prefix contains a param, regexp or wildcard
+ if (segTyp == ntRegexp) {
+ child.prefix = seg.rexPat
+ child.rex = seg.rexPat.toRegex()
+ }
+ if (segStartIdx == 0) {
+ // Route starts with a param
+ child.type = segTyp
+ segStartIdx = if (segTyp == ntCatchAll) {
+ -1
+ } else {
+ segEndIdx
+ }
+ if (segStartIdx < 0) {
+ segStartIdx = searchStr.length
+ }
+ child.tail = seg.tail // for params, we set the tail
+ child.paramsKey = findParamKey(searchStr) // set paramsKey if it has keys
+ if (segStartIdx != searchStr.length) {
+ // add static edge for the remaining part, split the end.
+ // its not possible to have adjacent param nodes, so its certainly
+ // going to be a static node next.
+ searchStr = searchStr.substring(segStartIdx) // advance search position
+ val nn = Node(type = ntStatic, label = searchStr[0], prefix = searchStr)
+ hn = child.addChild(nn, searchStr)
+ }
+ } else if (segStartIdx > 0) {
+ // Route has some param
+
+ // starts with a static segment
+ child.type = ntStatic
+ child.prefix = searchStr.substring(0, segStartIdx)
+ child.rex = null
+
+ // add the param edge node
+ searchStr = searchStr.substring(segStartIdx)
+ val nn = Node(type = segTyp, label = searchStr[0], tail = seg.tail, paramsKey = findParamKey(searchStr))
+ hn = child.addChild(nn, searchStr)
+ }
+ }
+ }
+ n.children[child.type] = append(n.children[child.type], child).also {
+ tailSort(it)
+ }
+ return hn
+ }
+
+ private fun findParamKey(pattern: String): String? {
+ val startChar = "{"
+ val endChar = "}"
+ val regexStart = ":"
+ if (!pattern.startsWith("{") && !pattern.endsWith("}")) return null
+ val startIndex = pattern.indexOf(startChar)
+ val endIndex = pattern.indexOf(endChar)
+ val regexIndex = pattern.indexOf(regexStart).let {
+ if (it == -1) pattern.length else it
+ }
+ return pattern.substring(min(startIndex + 1, pattern.length - 1), min(endIndex, regexIndex))
+ }
+
+ fun replaceChild(label: Char, tail: Char, child: Node) {
+ val n = this
+ val children = n.children[child.type] ?: return
+ var i = 0
+ while (i < children.size) {
+ if (children[i].label == label && children[i].tail == tail) {
+ children[i] = child
+ children[i].label = label
+ children[i].tail = tail
+ return
+ }
+ i++
+ }
+ throw IllegalArgumentException("chi: replacing missing child")
+ }
+
+ fun getEdge(ntyp: Int, label: Char, tail: Char, prefix: String): Node? {
+ val n = this
+ val nds = n.children[ntyp] ?: return null
+ var i = 0
+ while (i < nds.size) {
+ if (nds[i].label == label && nds[i].tail == tail) {
+ if (ntyp == ntRegexp && nds[i].prefix != prefix) {
+ i++
+ continue
+ }
+ return nds[i]
+ }
+ i++
+ }
+ return null
+ }
+
+ fun applyRoute(route: Route?) {
+ val n = this
+ n.route = route
+ }
+
+ // Recursive edge traversal by checking all nodeTyp groups along the way.
+ // It's like searching through a multi-dimensional radix trie.
+ fun findRoute(rctx: RouteMatch, path: String): Route? {
+ for (ntyp in 0 until NODE_SIZE) {
+ val nds = children[ntyp]
+ if (nds == null) {
+ continue
+ }
+ var xn: Node? = null
+ var xsearch = path
+ val label = if (path.isNotEmpty()) path[0] else ZERO_CHAR
+ when (ntyp) {
+ ntStatic -> {
+ xn = findEdge(nds, label)
+ if (xn == null || !xsearch.startsWith(xn.prefix)) {
+ continue
+ }
+ xsearch = xsearch.substring(xn.prefix.length)
+ }
+ ntParam, ntRegexp -> {
+ // short-circuit and return no matching route for empty param values
+ if (xsearch.isEmpty()) {
+ continue
+ }
+ // serially loop through each node grouped by the tail delimiter
+ var idx = 0
+ while (idx < nds.size) {
+ xn = nds[idx]
+ if (xn.type != ntStatic) {
+ xn.paramsKey?.let {
+ rctx.keys.add(it)
+ }
+ }
+ // label for param nodes is the delimiter byte
+ var p = xsearch.indexOf(xn.tail)
+ if (p < 0) {
+ p = if (xn.tail == '/') {
+ xsearch.length
+ } else {
+ idx++
+ continue
+ }
+ }
+ val rex = xn.rex
+ if (ntyp == ntRegexp && rex != null) {
+ if (!rex.matches(xsearch.substring(0, p))) {
+ idx++
+ continue
+ }
+ } else if (xsearch.substring(0, p).indexOf('/') != -1) {
+ // avoid a newRuntimeRoute across path segments
+ idx++
+ continue
+ }
+
+ // rctx.routeParams.Values = append(rctx.routeParams.Values, xsearch[:p])
+ val prevlen: Int = rctx.vars.size
+ rctx.value(xsearch.substring(0, p))
+ xsearch = xsearch.substring(p)
+ if (xsearch.isEmpty()) {
+ if (xn.isLeaf) {
+ val h = xn.route
+ if (h != null) {
+ rctx.key()
+ return h
+ }
+ }
+ }
+
+ // recursively find the next node on this branch
+ val fin = xn.findRoute(rctx, xsearch)
+ if (fin != null) {
+ return fin
+ }
+
+ // not found on this branch, reset vars
+ rctx.truncate(prevlen)
+ xsearch = path
+ idx++
+ }
+ }
+ else -> {
+ // catch-all nodes
+ // rctx.routeParams.Values = append(rctx.routeParams.Values, search)
+ if (xsearch.isNotEmpty()) {
+ rctx.value(xsearch)
+ }
+ xn = nds[0]
+ xsearch = ""
+ }
+ }
+ if (xn == null) {
+ continue
+ }
+
+ // did we returnType it yet?
+ if (xsearch.isEmpty()) {
+ if (xn.isLeaf) {
+ val h = xn.route
+ if (h != null) {
+ // rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...)
+ rctx.key()
+ return h
+ }
+ }
+ }
+
+ // recursively returnType the next node..
+ val fin = xn.findRoute(rctx, xsearch)
+ if (fin != null) {
+ return fin
+ }
+
+ // Did not returnType final handler, let's remove the param here if it was set
+ if (xn.type > ntStatic) {
+ // if len(rctx.routeParams.Values) > 0 {
+ // rctx.routeParams.Values = rctx.routeParams.Values[:len(rctx.routeParams.Values) - 1]
+ // }
+ rctx.pop()
+ }
+ }
+ return null
+ }
+
+ fun findEdge(ns: Array, label: Char): Node? {
+ val num = ns.size
+ var idx = 0
+ var i = 0
+ var j = num - 1
+ while (i <= j) {
+ idx = i + (j - i) / 2
+ when {
+ label > ns[idx].label -> {
+ i = idx + 1
+ }
+ label < ns[idx].label -> {
+ j = idx - 1
+ }
+ else -> {
+ i = num // breaks cond
+ }
+ }
+ }
+ return if (ns[idx].label != label) {
+ null
+ } else ns[idx]
+ }
+
+ val isLeaf: Boolean
+ get() = route != null
+
+ // longestPrefix finds the filesize of the shared prefix of two strings
+ fun longestPrefix(k1: String, k2: String): Int {
+ val len: Int = min(k1.length, k2.length)
+ for (i in 0 until len) {
+ if (k1[i] != k2[i]) {
+ return i
+ }
+ }
+ return len
+ }
+
+ fun tailSort(ns: Array) {
+ if (ns.size > 1) {
+ ns.sort()
+ for (i in ns.indices.reversed()) {
+ if (ns[i].type > ntStatic && ns[i].tail == '/') {
+ val tmp = ns[i]
+ ns[i] = ns[ns.size - 1]
+ ns[ns.size - 1] = tmp
+ return
+ }
+ }
+ }
+ }
+
+ private fun append(src: Array?, child: Node): Array {
+ if (src == null) {
+ return arrayOf(child)
+ }
+ val result = src.copyOf()
+ return result + child
+ }
+
+ // patNextSegment returns the next segment details from a pattern:
+ // node type, param key, regexp string, param tail byte, param starting index, param ending index
+ fun patNextSegment(pattern: String): Segment {
+ val ps = pattern.indexOf('{')
+ val ws = pattern.indexOf('*')
+ if (ps < 0 && ws < 0) {
+ return Segment(
+ ntStatic, "", ZERO_CHAR, 0,
+ pattern.length
+ ) // we return the entire thing
+ }
+
+ // Sanity check
+ require(!(ps >= 0 && ws >= 0 && ws < ps)) { "chi: wildcard '*' must be the last pattern in a route, otherwise use a '{param}'" }
+ var tail = '/' // Default endpoint tail to / byte
+ if (ps >= 0) {
+ // Param/Regexp pattern is next
+ var nt = ntParam
+
+ // Read to closing } taking into account opens and closes in curl count (cc)
+ var cc = 0
+ var pe = ps
+ val range = pattern.substring(ps)
+ for (i in range.indices) {
+ val c = range[i]
+ if (c == '{') {
+ cc++
+ } else if (c == '}') {
+ cc--
+ if (cc == 0) {
+ pe = ps + i
+ break
+ }
+ }
+ }
+ require(pe != ps) { "Router: route param closing delimiter '}' is missing" }
+ val key = pattern.substring(ps + 1, pe)
+ pe++ // set end to next position
+ if (pe < pattern.length) {
+ tail = pattern[pe]
+ }
+ var rexpat = ""
+ val idx = key.indexOf(':')
+ if (idx >= 0) {
+ nt = ntRegexp
+ rexpat = key.substring(idx + 1)
+ // key = key.substring(0, idx);
+ }
+ if (rexpat.isNotEmpty()) {
+ if (rexpat[0] != '^') {
+ rexpat = "^$rexpat"
+ }
+ if (rexpat[rexpat.length - 1] != '$') {
+ rexpat = "$rexpat$"
+ }
+ }
+ return Segment(nt, rexpat, tail, ps, pe)
+ }
+
+ // Wildcard pattern as finale
+ // EDIT: should we panic if there is stuff after the * ???
+ // We allow naming a wildcard: *path
+ // String key = ws == pattern.length() - 1 ? "*" : pattern.substring(ws + 1).toString();
+ return Segment(ntCatchAll, "", ZERO_CHAR, ws, pattern.length)
+ }
+ }
+
+ private val root = Node()
+
+ private val staticPaths = linkedMapOf()
+ fun insert(pattern: String, route: Route) {
+ var patternStr = pattern
+ val baseCatchAll = baseCatchAll(patternStr)
+ if (baseCatchAll.length > 1) {
+ // Add route pattern: /static/?* => /static
+ insert(baseCatchAll, route)
+ val tail = patternStr.substring(baseCatchAll.length + 2)
+ patternStr = "$baseCatchAll/$tail"
+ }
+ if (patternStr == BASE_CATCH_ALL) {
+ patternStr = "/*"
+ }
+ if (pathKeys(patternStr).isEmpty()) {
+ staticPaths[patternStr] = route
+ }
+ root.insertRoute(patternStr, route)
+ }
+
+ private fun baseCatchAll(pattern: String): String {
+ val i = pattern.indexOf(BASE_CATCH_ALL)
+ return if (i > 0) {
+ pattern.substring(0, i)
+ } else ""
+ }
+
+ fun insert(route: Route) {
+ insert(route.route, route)
+ }
+
+ fun find(path: String): RouteMatchResult? {
+ val staticRoute = staticPaths[path]
+ return if (staticRoute == null) {
+ findInternal(path)
+ } else {
+ return RouteMatchResult(staticRoute)
+ }
+ }
+
+ private fun findInternal(path: String): RouteMatchResult? {
+ // use radix tree
+ val result = RouteMatch()
+ val route = root.findRoute(result, path) ?: return null
+ return RouteMatchResult(route, result.pathMap)
+ }
+
+ companion object {
+ fun pathKeys(
+ pattern: String,
+ onItem: (key: String, value: String?) -> Unit = { _, _ -> },
+ ): List {
+ val result = arrayListOf()
+ var start = -1
+ var end = Int.MAX_VALUE
+ val len = pattern.length
+ var curly = 0
+ var i = 0
+ while (i < len) {
+ val ch = pattern[i]
+ if (ch == '{') {
+ if (curly == 0) {
+ start = i + 1
+ end = Int.MAX_VALUE
+ }
+ curly += 1
+ } else if (ch == ':') {
+ end = i
+ } else if (ch == '}') {
+ curly -= 1
+ if (curly == 0) {
+ val id = pattern.substring(start, min(i, end))
+ if (end == Int.MAX_VALUE) {
+ null
+ } else {
+ pattern.substring(end + 1, i)
+ }.let {
+ onItem.invoke(id, it)
+ }
+ result.add(id)
+ start = -1
+ end = Int.MAX_VALUE
+ }
+ } else if (ch == '*') {
+ val id: String = if (i == len - 1) {
+ "*"
+ } else {
+ pattern.substring(i + 1)
+ }
+ onItem.invoke(id, "\\.*")
+ result.add(id)
+ i = len
+ }
+ i++
+ }
+ return result
+ }
+
+ fun expandOptionalVariables(pattern: String): List {
+ if (pattern.isEmpty() || pattern == "/") {
+ return listOf("/")
+ }
+ val len = pattern.length
+ var key = 0
+ val paths = linkedMapOf()
+ val pathAppender = { index: Int, segment: StringBuilder ->
+ for (i in index until index - 1) {
+ paths[i]?.append(segment)
+ }
+ paths.getOrPut(index) {
+ val value = StringBuilder()
+ if (index > 0) {
+ val previous = paths[index - 1]
+ if (previous.toString() != "/") {
+ value.append(previous)
+ }
+ }
+ value
+ }.append(segment)
+ }
+ val segment = StringBuilder()
+ var isLastOptional = false
+ var i = 0
+ while (i < len) {
+ val ch = pattern[i]
+ if (ch == '/') {
+ if (segment.isNotEmpty()) {
+ pathAppender.invoke(key, segment)
+ segment.setLength(0)
+ }
+ segment.append(ch)
+ i += 1
+ } else if (ch == '{') {
+ segment.append(ch)
+ var curly = 1
+ var j = i + 1
+ while (j < len) {
+ val next = pattern[j++]
+ segment.append(next)
+ if (next == '{') {
+ curly += 1
+ } else if (next == '}') {
+ curly -= 1
+ if (curly == 0) {
+ break
+ }
+ }
+ }
+ if (j < len && pattern[j] == '?') {
+ j += 1
+ isLastOptional = true
+ if (paths.isEmpty()) {
+ paths[0] = StringBuilder("/")
+ }
+ pathAppender.invoke(++key, segment)
+ } else {
+ isLastOptional = false
+ pathAppender.invoke(key, segment)
+ }
+ segment.setLength(0)
+ i = j
+ } else {
+ segment.append(ch)
+ i += 1
+ }
+ }
+ if (paths.isEmpty()) {
+ return listOf(pattern)
+ }
+ if (segment.isNotEmpty()) {
+ pathAppender.invoke(key, segment)
+ if (isLastOptional) {
+ paths[++key] = segment
+ }
+ }
+ return paths.values.map { it.toString() }
+ }
+
+ private const val ntStatic = 0 // /home
+ private const val ntRegexp = 1 // /{id:[0-9]+}
+ private const val ntParam = 2 // /{user}
+ private const val ntCatchAll = 3 // /api/v1/*
+ private const val NODE_SIZE = ntCatchAll + 1
+ private const val ZERO_CHAR = 0.toChar()
+ private const val BASE_CATCH_ALL = "/?*"
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt
new file mode 100644
index 00000000..142ef51f
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt
@@ -0,0 +1,100 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.snapshots.SnapshotStateList
+import moe.tlaster.precompose.lifecycle.Lifecycle
+import moe.tlaster.precompose.lifecycle.LifecycleOwner
+import moe.tlaster.precompose.lifecycle.LifecycleRegistry
+import moe.tlaster.precompose.viewmodel.ViewModelStore
+import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner
+
+@Stable
+class RouteStack internal constructor(
+ val id: Long,
+ val topEntry: BackStackEntry,
+ val entries: SnapshotStateList = mutableStateListOf(),
+ val navTransition: NavTransition? = null,
+ internal val stackRoute: String? = null,
+ internal val viewModel: NavControllerViewModel,
+) : ViewModelStoreOwner, LifecycleOwner {
+
+ private var destroyAfterTransition = false
+
+ override val viewModelStore: ViewModelStore
+ get() = viewModel.get(id = id)
+
+ private val lifecycleRegistry: LifecycleRegistry by lazy {
+ LifecycleRegistry()
+ }
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleRegistry
+
+ val currentEntry: BackStackEntry
+ get() = entries.lastOrNull() ?: topEntry
+
+ val canGoBack: Boolean
+ get() = entries.isNotEmpty()
+
+ internal val currentDialogEntry: BackStackEntry?
+ get() = entries.lastOrNull { it.route is DialogRoute }
+
+ internal val currentBottomSheetEntry: BackStackEntry?
+ get() = entries.lastOrNull { it.route is BottomSheetRoute }
+
+ fun goBack(): BackStackEntry {
+ return entries.removeLast().also {
+ it.destroy()
+ }
+ }
+
+ fun onActive() {
+ lifecycleRegistry.currentState = Lifecycle.State.Active
+ currentEntry.active()
+ }
+
+ fun onInActive() {
+ lifecycleRegistry.currentState = Lifecycle.State.InActive
+ currentEntry.inActive()
+ if (destroyAfterTransition) {
+ onDestroyed()
+ }
+ }
+
+ fun destroyAfterTransition() {
+ destroyAfterTransition = true
+ }
+
+ fun onDestroyed() {
+ lifecycleRegistry.currentState = Lifecycle.State.Destroyed
+ entries.forEach { it.destroy() }
+ entries.clear()
+ viewModel.clear(id)
+ }
+
+ fun hasRoute(route: String): Boolean {
+ return topEntry.route.route == route || entries.any { it.route.route == route }
+ || stackRoute == route
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt
new file mode 100644
index 00000000..904a157e
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt
@@ -0,0 +1,320 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.navigation
+
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.ModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.SaveableStateHolder
+import androidx.compose.runtime.saveable.rememberSaveableStateHolder
+import moe.tlaster.precompose.lifecycle.Lifecycle
+import moe.tlaster.precompose.lifecycle.LifecycleObserver
+import moe.tlaster.precompose.lifecycle.LifecycleOwner
+import moe.tlaster.precompose.navigation.bottomsheet.SheetContentHost
+import moe.tlaster.precompose.ui.BackDispatcher
+import moe.tlaster.precompose.ui.BackHandler
+import moe.tlaster.precompose.viewmodel.ViewModelStore
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+@OptIn(ExperimentalMaterialApi::class)
+@Stable
+internal class RouteStackManager(
+ private val routeGraph: RouteGraph,
+ private val stateHolder: SaveableStateHolder,
+ private val sheetState: ModalBottomSheetState,
+) : LifecycleObserver, BackHandler {
+ // FIXME: 2021/4/1 Temp workaround for deeplink
+ private var pendingNavigation: String? = null
+
+ private val _suspendResult = linkedMapOf>()
+
+ private var stackEntryId = Long.MIN_VALUE
+ private var routeStackId = Long.MIN_VALUE
+
+ var backDispatcher: BackDispatcher? = null
+ set(value) {
+ field?.unregister(this)
+ field = value
+ value?.register(this)
+ }
+
+ var lifeCycleOwner: LifecycleOwner? = null
+ set(value) {
+ field?.lifecycle?.removeObserver(this)
+ field = value
+ value?.lifecycle?.addObserver(this)
+ }
+
+ private var viewModel: NavControllerViewModel? = null
+
+ private val _backStacks = mutableStateListOf()
+
+ internal val backStacks: List
+ get() = _backStacks
+
+ internal val currentStack: RouteStack?
+ get() = _backStacks.lastOrNull()
+
+ internal val currentEntry: BackStackEntry?
+ get() = currentStack?.currentEntry
+
+ val canGoBack: Boolean
+ get() = currentStack?.canGoBack == true || _backStacks.size > 1
+
+ val isPop = mutableStateOf(false)
+
+ internal val sheetContent: @Composable ColumnScope.() -> Unit = @Composable {
+ // val columnScope = this
+ val saveableStateHolder = rememberSaveableStateHolder()
+
+ val currentEntry = currentStack?.currentBottomSheetEntry
+ SheetContentHost(
+ backStackEntry = currentEntry,
+ sheetState = sheetState,
+ stateHolder = saveableStateHolder,
+ onSheetShown = {
+
+ },
+ onSheetDismissed = {
+ goBack()
+ }
+ )
+ }
+
+ internal fun getRouteStack(route: String): RouteStack? {
+ val matchResult = routeGraph.findRoute(route) ?: return null
+ return _backStacks.find { it.hasRoute(matchResult.route.route) }
+ }
+
+ internal fun setViewModelStore(viewModelStore: ViewModelStore) {
+ if (viewModel != NavControllerViewModel.create(viewModelStore)) {
+ viewModel = NavControllerViewModel.create(viewModelStore)
+ }
+ }
+
+ fun navigate(route: String, options: NavOptions? = null) {
+ val vm = viewModel ?: run {
+ pendingNavigation = route
+ return
+ }
+ isPop.value = false
+
+ val matchResult = routeGraph.findRoute(route)
+ checkNotNull(matchResult) {
+ "RouteStackManager: navigate target $route not found"
+ }
+ require(matchResult.route is ComposeRoute) {
+ "RouteStackManager: navigate target $route is not ComposeRoute"
+ }
+
+ if (options?.popUpTo != null) {
+ popTo(options.popUpTo.route, options.popUpTo.inclusive)
+ }
+
+ val query = route.substringAfter('?', "")
+ fun newEntry(route: ComposeRoute): BackStackEntry {
+ return BackStackEntry(
+ id = stackEntryId++,
+ route = route,
+ pathMap = matchResult.pathMap,
+ queryString = query.takeIf { it.isNotEmpty() }?.let {
+ QueryString(it)
+ },
+ viewModel = vm,
+ )
+ }
+
+ fun newStack(
+ entry: BackStackEntry,
+ navTransition: NavTransition? = null,
+ stackRoute: String? = null,
+ ): RouteStack {
+ return RouteStack(
+ id = routeStackId++,
+ topEntry = entry,
+ navTransition = navTransition,
+ stackRoute = stackRoute,
+ viewModel = vm,
+ )
+ }
+
+ var launchSingleTopSuccess = false
+ if (options?.launchSingleTop == true && matchResult.route is SceneRoute) {
+ _backStacks.firstOrNull { it.hasRoute(matchResult.route.route) }
+ ?.let {
+ _backStacks.remove(it)
+ _backStacks.add(it)
+ launchSingleTopSuccess = true
+ }
+ }
+
+ if (!launchSingleTopSuccess) {
+ when (val matchRoute = matchResult.route) {
+ is SceneRoute -> {
+ val entry = newEntry(matchRoute)
+ val stack = newStack(entry, matchRoute.navTransition)
+ _backStacks.add(stack)
+ }
+ is DialogRoute,
+ is BottomSheetRoute -> {
+ val entry = newEntry(matchRoute)
+ currentStack?.entries?.add(entry)
+ }
+ is NavigationRoute -> {
+ val currentStack = currentStack
+
+ val stack = if (currentStack?.stackRoute == matchRoute.route) {
+ currentStack
+ } else {
+ val initialEntry = newEntry(matchRoute.initialRoute)
+ val stack = newStack(
+ initialEntry,
+ navTransition = matchRoute.initialRoute.navTransition,
+ stackRoute = matchRoute.route,
+ )
+ _backStacks.add(stack)
+ stack
+ }
+
+ if (route != matchRoute.route) {
+ val childMatchResult = matchRoute.graph.findRoute(route)
+ requireNotNull(childMatchResult) {
+ "RouteStackManager: child navigate target $route not found"
+ }
+ require(childMatchResult.route is ComposeRoute) {
+ "RouteStackManager: child navigate target $route is not ComposeRoute"
+ }
+
+ if (childMatchResult.route != matchRoute.initialRoute) {
+ stack.entries.add(newEntry(childMatchResult.route))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fun goBack(
+ route: String? = null,
+ inclusive: Boolean = false,
+ result: Any? = null,
+ ): Boolean {
+ if (!canGoBack) {
+ return false
+ }
+ isPop.value = true
+
+ when {
+ !route.isNullOrEmpty() -> {
+ popTo(route, inclusive)
+ }
+ currentStack?.canGoBack == true -> {
+ currentStack?.goBack()
+ }
+ _backStacks.size > 1 -> {
+ val stack = _backStacks.removeLast()
+ val entry = stack.currentEntry
+ stateHolder.removeState(stack.id)
+ stack.destroyAfterTransition()
+ entry
+ }
+ else -> {
+ null
+ }
+ }?.takeIf { backStackEntry ->
+ _suspendResult.containsKey(backStackEntry)
+ }?.let {
+ _suspendResult.remove(it)?.resume(result)
+ }
+ return true
+ }
+
+ private fun popTo(route: String, inclusive: Boolean = false): BackStackEntry? {
+ val matchResult = routeGraph.findRoute(route) ?: return null
+
+ val matchRoute = matchResult.route
+ val stackIndex = _backStacks.indexOfLast { it.hasRoute(matchRoute.route) }
+ if (stackIndex == -1) return null
+
+ val stack = _backStacks[stackIndex]
+ val entryIndex = stack.entries.indexOfLast { it.route.route == matchRoute.route }
+
+ return when (matchRoute) {
+ is SceneRoute, is NavigationRoute -> {
+ if (entryIndex == -1) {
+ val fromIndex = if (inclusive) stackIndex else stackIndex + 1
+ val toIndex = _backStacks.lastIndex
+ if (fromIndex <= toIndex) {
+ _backStacks.removeRange(fromIndex, toIndex)
+ }
+ currentEntry
+ } else {
+ val fromIndex = if (inclusive) entryIndex else entryIndex + 1
+ val toIndex = stack.entries.lastIndex
+ if (fromIndex <= toIndex) {
+ stack.entries.removeRange(fromIndex, toIndex)
+ }
+ stack.currentEntry
+ }
+ }
+ else -> null
+ }
+ }
+
+ suspend fun waitingForResult(entry: BackStackEntry): Any? = suspendCoroutine {
+ _suspendResult[entry] = it
+ }
+
+ override fun onStateChanged(state: Lifecycle.State) {
+ when (state) {
+ Lifecycle.State.Initialized -> Unit
+ Lifecycle.State.Active -> currentStack?.onActive()
+ Lifecycle.State.InActive -> currentStack?.onInActive()
+ Lifecycle.State.Destroyed -> {
+ _backStacks.forEach {
+ it.onDestroyed()
+ }
+ _backStacks.clear()
+ }
+ }
+ }
+
+ internal fun indexOf(stack: RouteStack): Int {
+ return _backStacks.indexOf(stack)
+ }
+
+ override fun handleBackPress(): Boolean {
+ return goBack()
+ }
+
+ fun navigateInitial(initialRoute: String) {
+ navigate(initialRoute)
+ pendingNavigation?.let {
+ navigate(it)
+ }
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/bottomsheet/SheetContentHost.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/bottomsheet/SheetContentHost.kt
new file mode 100644
index 00000000..12f74de1
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/bottomsheet/SheetContentHost.kt
@@ -0,0 +1,165 @@
+package moe.tlaster.precompose.navigation.bottomsheet
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.ModalBottomSheetState
+import androidx.compose.material.ModalBottomSheetValue
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.saveable.SaveableStateHolder
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.runtime.withFrameNanos
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeout
+import moe.tlaster.precompose.navigation.BackStackEntry
+import moe.tlaster.precompose.ui.LocalLifecycleOwner
+import moe.tlaster.precompose.ui.LocalViewModelStoreOwner
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+internal fun SheetContentHost(
+ backStackEntry: BackStackEntry?,
+ sheetState: ModalBottomSheetState,
+ stateHolder: SaveableStateHolder,
+ onSheetShown: (entry: BackStackEntry) -> Unit,
+ onSheetDismissed: (entry: BackStackEntry) -> Unit,
+) {
+ val scope = rememberCoroutineScope()
+ if (backStackEntry != null) {
+ val currentOnSheetShown by rememberUpdatedState(onSheetShown)
+ val currentOnSheetDismissed by rememberUpdatedState(onSheetDismissed)
+ var hideCalled by remember(backStackEntry) { mutableStateOf(false) }
+ LaunchedEffect(backStackEntry, hideCalled) {
+ val sheetVisibility = snapshotFlow { sheetState.isVisible }
+ sheetVisibility
+ // We are only interested in changes in the sheet's visibility
+ .distinctUntilChanged()
+ // distinctUntilChanged emits the initial value which we don't need
+ .drop(1)
+ // We want to know when the sheet was visible but is not anymore
+ .filter { isVisible -> !isVisible }
+ // Finally, pop the back stack when the sheet has been hidden
+ .collect { if (!hideCalled) currentOnSheetDismissed(backStackEntry) }
+ }
+
+ // We use this signal to know when its (almost) safe to `show` the bottom sheet
+ // It will be set after the sheet's content has been `onGloballyPositioned`
+ val contentPositionedSignal = remember(backStackEntry) {
+ CompletableDeferred()
+ }
+
+ // Whenever the composable associated with the backStackEntry enters the composition, we
+ // want to show the sheet, and hide it when this composable leaves the composition
+ DisposableEffect(backStackEntry) {
+ scope.launch {
+ contentPositionedSignal.await()
+ try {
+ // If we don't wait for a few frames before calling `show`, we will be too early
+ // and the sheet content won't have been laid out yet (even with our content
+ // positioned signal). If a sheet is tall enough to have a HALF_EXPANDED state,
+ // we might be here before the SwipeableState's anchors have been properly
+ // calculated, resulting in the sheet animating to the EXPANDED state when
+ // calling `show`. As a workaround, we wait for a magic number of frames.
+ // https://issuetracker.google.com/issues/200980998
+ repeat(AWAIT_FRAMES_BEFORE_SHOW) { awaitFrame() }
+ sheetState.show()
+ } catch (sheetShowCancelled: CancellationException) {
+ // There is a race condition in ModalBottomSheetLayout that happens when the
+ // sheet content changes due to the anchors being re-calculated. This causes an
+ // animation to run for a short time, cancelling any currently running animation
+ // such as the one triggered by our `show` call.
+ // The sheet will still snap to the EXPANDED or HALF_EXPANDED state.
+ // In that case we want to wait until the sheet is visible. For safety, we only
+ // wait for 800 milliseconds - if the sheet is not visible until then, something
+ // has gone horribly wrong.
+ // https://issuetracker.google.com/issues/200980998
+ withTimeout(800) {
+ while (!sheetState.isVisible) {
+ awaitFrame()
+ }
+ }
+ } finally {
+ // If, for some reason, the sheet is in a state where the animation is still
+ // running, there is a chance that it is already targeting the EXPANDED or
+ // HALF_EXPANDED state and will snap to that. In that case we can be fairly
+ // certain that the sheet will actually end up in that state.
+ if (sheetState.isVisible || sheetState.willBeVisible) {
+ currentOnSheetShown(backStackEntry)
+ }
+ }
+ }
+ onDispose {
+ scope.launch {
+ hideCalled = true
+ try {
+ sheetState.internalHide()
+ } finally {
+ hideCalled = false
+ }
+ }
+ }
+ }
+
+ stateHolder.SaveableStateProvider(backStackEntry.id) {
+ CompositionLocalProvider(
+ LocalViewModelStoreOwner provides backStackEntry,
+ LocalLifecycleOwner provides backStackEntry,
+ ) {
+ Box(Modifier.onGloballyPositioned { contentPositionedSignal.complete(Unit) }) {
+ backStackEntry.route.content(backStackEntry)
+ }
+ }
+ }
+
+ } else {
+ EmptySheet()
+ }
+}
+
+@Composable
+private fun EmptySheet() {
+ // The swipeable modifier has a bug where it doesn't support having something with
+ // height = 0
+ // b/178529942
+ // If there are no destinations on the back stack, we need to add something to work
+ // around this
+ Box(Modifier.height(1.dp))
+}
+
+private suspend fun awaitFrame() = withFrameNanos(onFrame = {})
+
+/**
+ * This magic number has been chosen through painful experiments.
+ * - Waiting for 1 frame still results in the sheet fully expanding, which we don't want
+ * - Waiting for 2 frames results in the `show` call getting cancelled
+ * - Waiting for 3+ frames results in the sheet expanding to the correct state. Success!
+ * We wait for a few frames more just to be sure.
+ */
+private const val AWAIT_FRAMES_BEFORE_SHOW = 3
+
+// We have the same issue when we are hiding the sheet, but snapTo works better
+@OptIn(ExperimentalMaterialApi::class)
+private suspend fun ModalBottomSheetState.internalHide() {
+ snapTo(ModalBottomSheetValue.Hidden)
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+private val ModalBottomSheetState.willBeVisible: Boolean
+ get() = targetValue == ModalBottomSheetValue.HalfExpanded || targetValue == ModalBottomSheetValue.Expanded
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackHandler.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackHandler.kt
new file mode 100644
index 00000000..f779552f
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackHandler.kt
@@ -0,0 +1,60 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+
+@Composable
+fun BackHandler(enabled: Boolean = true, onBack: () -> Unit) {
+ // Safely update the current `onBack` lambda when a new one is provided
+ val currentOnBack by rememberUpdatedState(onBack)
+ // Remember in Composition a back callback that calls the `onBack` lambda
+ val backCallback = remember {
+ object : OnBackPressedCallback(enabled) {
+ override fun handleOnBackPressed() {
+ currentOnBack.invoke()
+ }
+ }
+ }
+
+ SideEffect {
+ backCallback.isEnabled = enabled
+ }
+
+ val backDispatcher = checkNotNull(LocalBackDispatcherOwner.current) {
+ "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner"
+ }.backDispatcher
+ val lifecycleOwner = LocalLifecycleOwner.current
+ DisposableEffect(lifecycleOwner, backDispatcher) {
+ // Add callback to the backDispatcher
+ // backDispatcher.addCallback(lifecycleOwner, backCallback)
+ backDispatcher.register(backCallback)
+ // When the effect leaves the Composition, remove the callback
+ onDispose {
+ backDispatcher.unregister(backCallback)
+ }
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt
new file mode 100644
index 00000000..fb7c9519
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt
@@ -0,0 +1,67 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.ui
+
+import androidx.compose.runtime.compositionLocalOf
+
+val LocalBackDispatcherOwner = compositionLocalOf { null }
+
+interface BackDispatcherOwner {
+ val backDispatcher: BackDispatcher
+}
+
+class BackDispatcher {
+ private val handlers = arrayListOf()
+
+ fun onBackPress(): Boolean {
+ for (it in handlers) {
+ if (it.handleBackPress()) {
+ return true
+ }
+ }
+ return false
+ }
+
+ internal fun register(handler: BackHandler) {
+ handlers.add(0, handler)
+ }
+
+ internal fun unregister(handler: BackHandler) {
+ handlers.remove(handler)
+ }
+}
+
+abstract class OnBackPressedCallback(enabled: Boolean) : BackHandler {
+
+ var isEnabled: Boolean = enabled
+ internal set
+
+ abstract fun handleOnBackPressed()
+
+ override fun handleBackPress(): Boolean {
+ if (isEnabled) handleOnBackPressed()
+ return isEnabled
+ }
+}
+
+interface BackHandler {
+ fun handleBackPress(): Boolean
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt
new file mode 100644
index 00000000..cc0710a7
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt
@@ -0,0 +1,33 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.ui
+
+import androidx.compose.runtime.compositionLocalOf
+import moe.tlaster.precompose.lifecycle.LifecycleOwner
+import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner
+
+val LocalLifecycleOwner = compositionLocalOf { noLocalProvidedFor("LocalLifecycleOwner") }
+
+val LocalViewModelStoreOwner = compositionLocalOf { noLocalProvidedFor("ViewModelStoreOwner") }
+
+private fun noLocalProvidedFor(name: String): Nothing {
+ error("CompositionLocal $name not present")
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt
new file mode 100644
index 00000000..720e8dcf
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt
@@ -0,0 +1,70 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner
+import kotlin.reflect.KClass
+
+@Composable
+inline fun viewModel(
+ keys: List = emptyList(),
+ noinline creator: () -> T,
+): T = viewModel(T::class, keys, creator = creator)
+
+@Composable
+fun viewModel(
+ modelClass: KClass,
+ keys: List = emptyList(),
+ creator: () -> T,
+): T {
+ val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
+ "Require LocalViewModelStoreOwner not null for $modelClass"
+ }
+ return remember(
+ modelClass, keys, creator, viewModelStoreOwner
+ ) {
+ viewModelStoreOwner.getViewModel(keys, modelClass = modelClass, creator = creator)
+ }
+}
+
+private fun ViewModelStoreOwner.getViewModel(
+ keys: List = emptyList(),
+ modelClass: KClass,
+ creator: () -> T,
+): T {
+ val key = (keys.map { it.hashCode().toString() } + modelClass.qualifiedName).joinToString()
+ val existing = viewModelStore[key]
+ if (existing != null && modelClass.isInstance(existing)) {
+ @Suppress("UNCHECKED_CAST")
+ return existing as T
+ } else {
+ @Suppress("ControlFlowWithEmptyBody")
+ if (existing != null) {
+ // TODO: log a warning.
+ }
+ }
+ val viewModel = creator.invoke()
+ viewModelStore.put(key, viewModel)
+ return viewModel
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt
new file mode 100644
index 00000000..6845dfdb
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt
@@ -0,0 +1,57 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.viewmodel
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import java.io.Closeable
+import kotlin.coroutines.CoroutineContext
+
+private const val JOB_KEY = "moe.tlaster.precompose.viewmodel.ViewModelCoroutineScope.JOB_KEY"
+
+/**
+ * [CoroutineScope] tied to this [ViewModel].
+ * This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called
+ *
+ * This scope is bound to
+ * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
+ */
+val ViewModel.viewModelScope: CoroutineScope
+ get() {
+ val scope: CoroutineScope? = getTag(JOB_KEY)
+ if (scope != null) {
+ return scope
+ }
+ return setTagIfAbsent(
+ JOB_KEY,
+ CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
+ )
+ }
+
+internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
+ override val coroutineContext: CoroutineContext = context
+
+ override fun close() {
+ coroutineContext.cancel()
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt
new file mode 100644
index 00000000..0c65427f
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt
@@ -0,0 +1,63 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.viewmodel
+
+import java.io.Closeable
+
+abstract class ViewModel {
+ @Volatile
+ private var disposed = false
+ private val bagOfTags = hashMapOf()
+
+ protected open fun onCleared() {}
+
+ fun clear() {
+ disposed = true
+ bagOfTags.let {
+ for (value in it.values) {
+ disposeWithRuntimeException(value)
+ }
+ }
+ onCleared()
+ }
+
+ open fun setTagIfAbsent(key: String, newValue: T): T {
+ @Suppress("UNCHECKED_CAST")
+ return bagOfTags.getOrPut(key) {
+ newValue as Any
+ }.also {
+ if (disposed) {
+ disposeWithRuntimeException(it)
+ }
+ } as T
+ }
+
+ open fun getTag(key: String): T? {
+ @Suppress("UNCHECKED_CAST")
+ return bagOfTags[key] as T?
+ }
+
+ private fun disposeWithRuntimeException(obj: Any) {
+ if (obj is Closeable) {
+ obj.close()
+ }
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt
new file mode 100644
index 00000000..04ccb6f4
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt
@@ -0,0 +1,50 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.viewmodel
+
+import kotlin.reflect.KClass
+
+inline fun ViewModelStore.getViewModel(
+ noinline creator: () -> T,
+): T {
+ val key = T::class.qualifiedName.toString()
+ return getViewModel(key, T::class, creator)
+}
+
+fun ViewModelStore.getViewModel(
+ key: String,
+ clazz: KClass,
+ creator: () -> T,
+): T {
+ val existing = get(key)
+ if (existing != null && clazz.isInstance(existing)) {
+ @Suppress("UNCHECKED_CAST")
+ return existing as T
+ } else {
+ @Suppress("ControlFlowWithEmptyBody")
+ if (existing != null) {
+ // TODO: log a warning.
+ }
+ }
+ val viewModel = creator.invoke()
+ put(key, viewModel)
+ return viewModel
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt
new file mode 100644
index 00000000..d3f26248
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt
@@ -0,0 +1,45 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.viewmodel
+
+class ViewModelStore {
+ private val map = hashMapOf()
+
+ fun put(key: String, viewModel: ViewModel) {
+ val oldViewModel = map.put(key, viewModel)
+ oldViewModel?.clear()
+ }
+
+ operator fun get(key: String): ViewModel? {
+ return map[key]
+ }
+
+ fun keys(): Set {
+ return HashSet(map.keys)
+ }
+
+ fun clear() {
+ for (vm in map.values) {
+ vm.clear()
+ }
+ map.clear()
+ }
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt
new file mode 100644
index 00000000..d4817655
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt
@@ -0,0 +1,25 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.viewmodel
+
+interface ViewModelStoreOwner {
+ val viewModelStore: ViewModelStore
+}
diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt
new file mode 100644
index 00000000..0eeb19cf
--- /dev/null
+++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt
@@ -0,0 +1,42 @@
+/*
+ * Mask-Android
+ *
+ * Copyright (C) 2022 DimensionDev and Contributors
+ *
+ * This file is part of Mask-Android.
+ *
+ * Mask-Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mask-Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Mask-Android. If not, see .
+ */
+package moe.tlaster.precompose.viewmodel.compose
+
+import androidx.compose.runtime.Composable
+import moe.tlaster.precompose.ui.LocalViewModelStoreOwner
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner
+import moe.tlaster.precompose.viewmodel.getViewModel
+
+@Composable
+inline fun viewModel(
+ viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
+ "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
+ },
+ key: String? = null,
+ noinline creator: () -> VM,
+): VM = viewModelStoreOwner.viewModelStore.let {
+ if (key == null) {
+ it.getViewModel(creator)
+ } else {
+ it.getViewModel(key, VM::class, creator)
+ }
+}
diff --git a/common/src/commonMain/kotlin/com/dimension/maskbook/common/route/CommonRoute.kt b/common/src/commonMain/route/com/dimension/maskbook/common/route/CommonRoute.kt
similarity index 100%
rename from common/src/commonMain/kotlin/com/dimension/maskbook/common/route/CommonRoute.kt
rename to common/src/commonMain/route/com/dimension/maskbook/common/route/CommonRoute.kt
diff --git a/common/src/commonMain/kotlin/com/dimension/maskbook/common/route/Deeplinks.kt b/common/src/commonMain/route/com/dimension/maskbook/common/route/Deeplinks.kt
similarity index 100%
rename from common/src/commonMain/kotlin/com/dimension/maskbook/common/route/Deeplinks.kt
rename to common/src/commonMain/route/com/dimension/maskbook/common/route/Deeplinks.kt
diff --git a/common/src/commonMain/kotlin/com/dimension/maskbook/common/route/WebDeepLinks.kt b/common/src/commonMain/route/com/dimension/maskbook/common/route/WebDeepLinks.kt
similarity index 100%
rename from common/src/commonMain/kotlin/com/dimension/maskbook/common/route/WebDeepLinks.kt
rename to common/src/commonMain/route/com/dimension/maskbook/common/route/WebDeepLinks.kt
diff --git a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/EntrySetup.kt b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/EntrySetup.kt
index 751bdc53..4be1aeb8 100644
--- a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/EntrySetup.kt
+++ b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/EntrySetup.kt
@@ -21,8 +21,6 @@
package com.dimension.maskbook.entry
import android.content.Context
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
import com.dimension.maskbook.common.ModuleSetup
import com.dimension.maskbook.common.route.Navigator
import com.dimension.maskbook.entry.data.JSMethod
@@ -34,11 +32,13 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.RouteBuilder
import org.koin.dsl.module
import org.koin.mp.KoinPlatformTools
object EntrySetup : ModuleSetup {
- override fun NavGraphBuilder.route(navController: NavController) {
+ override fun RouteBuilder.route(navController: NavController) {
generatedRoute(navController)
}
diff --git a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/ComposeDebugTool.kt b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/ComposeDebugTool.kt
index dd434788..250cfceb 100644
--- a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/ComposeDebugTool.kt
+++ b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/ComposeDebugTool.kt
@@ -34,8 +34,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import androidx.navigation.compose.currentBackStackEntryAsState
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.currentBackStackEntryAsState
@Composable
fun ComposeDebugTool(
@@ -57,7 +57,7 @@ fun ComposeDebugTool(
if (debugOpen) {
Text(
modifier = Modifier.background(MaterialTheme.colors.surface),
- text = state?.destination?.route ?: "UnKnow route",
+ text = state?.route?.route ?: "UnKnow route",
color = MaterialTheme.colors.primary
)
}
diff --git a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/Router.kt b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/Router.kt
index 4f5db8ff..460e2135 100644
--- a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/Router.kt
+++ b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/Router.kt
@@ -22,15 +22,17 @@ package com.dimension.maskbook.entry.ui
import android.net.Uri
import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import com.dimension.maskbook.common.CommonSetup
+import com.dimension.maskbook.common.ext.navigate
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route
import com.dimension.maskbook.common.route.DeeplinkNavigateArgs
import com.dimension.maskbook.common.route.Navigator
import com.dimension.maskbook.common.route.RouteNavigateArgs
import com.dimension.maskbook.common.ui.widget.RouteHost
-import com.dimension.maskbook.common.ui.widget.rememberMaskBottomSheetNavigator
import com.dimension.maskbook.entry.BuildConfig
import com.dimension.maskbook.entry.EntrySetup
import com.dimension.maskbook.entry.repository.EntryRepository
@@ -43,18 +45,16 @@ import com.dimension.maskbook.persona.export.PersonaServices
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.setting.SettingSetup
import com.dimension.maskbook.wallet.WalletSetup
-import com.google.accompanist.navigation.animation.rememberAnimatedNavController
-import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import kotlinx.coroutines.flow.firstOrNull
+import moe.tlaster.precompose.navigation.rememberNavController
import org.koin.mp.KoinPlatformTools
-@OptIn(ExperimentalMaterialNavigationApi::class, ExperimentalAnimationApi::class)
+@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
@Composable
fun Router(
startDestination: String,
) {
- val bottomSheetNavigator = rememberMaskBottomSheetNavigator()
- val navController = rememberAnimatedNavController(bottomSheetNavigator)
+ val navController = rememberNavController()
LaunchedEffect(Unit) {
val initialRoute = getInitialRoute()
navController.navigate(initialRoute) {
@@ -67,14 +67,13 @@ fun Router(
Navigator.navigateEvent.collect {
it.getContentIfNotHandled()?.let { it1 ->
when (it1) {
- is DeeplinkNavigateArgs -> navController.navigate(Uri.parse(it1.url))
+ is DeeplinkNavigateArgs -> navController.navigateUri(Uri.parse(it1.url))
is RouteNavigateArgs -> navController.navigate(it1.route)
}
}
}
}
RouteHost(
- bottomSheetNavigator = bottomSheetNavigator,
navController = navController,
startDestination = startDestination,
) {
diff --git a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/scene/IntroScene.kt b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/scene/IntroScene.kt
index 04df9d94..bcd94bdb 100644
--- a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/scene/IntroScene.kt
+++ b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/scene/IntroScene.kt
@@ -62,7 +62,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import androidx.navigation.NavController
+import com.dimension.maskbook.common.ext.navigate
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
import com.dimension.maskbook.common.routeProcessor.annotations.NavGraphDestination
@@ -76,7 +76,8 @@ import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.rememberPagerState
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
+import moe.tlaster.precompose.navigation.NavController
private data class IntroData(
@DrawableRes val img: Int,
diff --git a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/scene/MainHost.kt b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/scene/MainHost.kt
index 8e9eff2e..c3e524ed 100644
--- a/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/scene/MainHost.kt
+++ b/entry/src/androidMain/kotlin/com/dimension/maskbook/entry/ui/scene/MainHost.kt
@@ -51,7 +51,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.ext.getAll
import com.dimension.maskbook.common.ext.navigateToExtension
import com.dimension.maskbook.common.route.CommonRoute
@@ -68,6 +67,7 @@ import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.rememberPagerState
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.navigation.NavController
import kotlin.math.max
private val tabOrder = listOf(
diff --git a/extension/export/build.gradle.kts b/extension/export/build.gradle.kts
index c6a4f35b..1b9c5d43 100644
--- a/extension/export/build.gradle.kts
+++ b/extension/export/build.gradle.kts
@@ -8,8 +8,8 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
- implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
+ compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
+ compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
}
}
val commonTest by getting {
diff --git a/extension/src/androidMain/kotlin/com/dimension/maskbook/extension/ExtensionSetup.kt b/extension/src/androidMain/kotlin/com/dimension/maskbook/extension/ExtensionSetup.kt
index 3d8f4aaa..db1d0dd2 100644
--- a/extension/src/androidMain/kotlin/com/dimension/maskbook/extension/ExtensionSetup.kt
+++ b/extension/src/androidMain/kotlin/com/dimension/maskbook/extension/ExtensionSetup.kt
@@ -25,15 +25,9 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.runtime.getValue
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
-import androidx.navigation.NavType
-import androidx.navigation.compose.currentBackStackEntryAsState
-import androidx.navigation.navArgument
-import androidx.navigation.navDeepLink
import com.dimension.maskbook.common.IoScopeName
import com.dimension.maskbook.common.ModuleSetup
-import com.dimension.maskbook.common.ext.navigate
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.gecko.WebContentController
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
@@ -44,40 +38,44 @@ import com.dimension.maskbook.extension.route.ExtensionRoute
import com.dimension.maskbook.extension.ui.WebContentScene
import com.dimension.maskbook.extension.utils.BackgroundMessageChannel
import com.dimension.maskbook.extension.utils.ContentMessageChannel
-import com.google.accompanist.navigation.animation.composable
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.RouteBuilder
+import moe.tlaster.precompose.navigation.currentBackStackEntryAsState
+import moe.tlaster.precompose.navigation.NavTransition
import org.koin.core.qualifier.named
import org.koin.dsl.module
import org.koin.mp.KoinPlatformTools
object ExtensionSetup : ModuleSetup {
+
@OptIn(ExperimentalAnimationApi::class)
- override fun NavGraphBuilder.route(navController: NavController) {
- composable(
+ override fun RouteBuilder.route(navController: NavController) {
+ scene(
route = ExtensionRoute.WebContent.path,
deepLinks = listOf(
- navDeepLink { uriPattern = Deeplinks.WebContent.path }
+ Deeplinks.WebContent.path
),
- arguments = listOf(
- navArgument("site") { type = NavType.StringType; nullable = true }
+ navTransition = NavTransition(
+ enterTransition = NavTransition.NoneEnter,
+ exitTransition = {
+ scaleOut(
+ targetScale = 0.9f,
+ )
+ },
+ popExitTransition = NavTransition.NoneExit,
+ popEnterTransition = {
+ scaleIn(
+ initialScale = 0.9f,
+ )
+ }
),
- exitTransition = {
- scaleOut(
- targetScale = 0.9f,
- )
- },
- popExitTransition = null,
- popEnterTransition = {
- scaleIn(
- initialScale = 0.9f,
- )
- }
) {
val backStackEntry by navController.currentBackStackEntryAsState()
- val site = it.arguments?.getString("site")?.let { Site.valueOf(it) }
+ val site = it.query("site")?.let { Site.valueOf(it) }
WebContentScene(
onPersonaClicked = {
- navController.navigate(Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona))) {
+ navController.navigateUri(Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona))) {
launchSingleTop = true
popUpTo(ExtensionRoute.WebContent.path) {
inclusive = false
diff --git a/extension/src/androidMain/kotlin/com/dimension/maskbook/extension/ui/WebContentScene.kt b/extension/src/androidMain/kotlin/com/dimension/maskbook/extension/ui/WebContentScene.kt
index 22b2adc4..aa41dac6 100644
--- a/extension/src/androidMain/kotlin/com/dimension/maskbook/extension/ui/WebContentScene.kt
+++ b/extension/src/androidMain/kotlin/com/dimension/maskbook/extension/ui/WebContentScene.kt
@@ -20,7 +20,6 @@
*/
package com.dimension.maskbook.extension.ui
-import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -63,7 +62,8 @@ import com.dimension.maskbook.common.ui.widget.button.clickable
import com.dimension.maskbook.extension.export.model.Site
import com.dimension.maskbook.extension.ext.site
import com.dimension.maskbook.localization.R
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
+import moe.tlaster.precompose.ui.BackHandler
import kotlin.math.roundToInt
@Composable
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/LabsSetup.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/LabsSetup.kt
index 4fde3462..c98b05fa 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/LabsSetup.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/LabsSetup.kt
@@ -21,8 +21,6 @@
package com.dimension.maskbook.labs
import android.content.Context
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
import com.dimension.maskbook.common.IoScopeName
import com.dimension.maskbook.common.ModuleSetup
import com.dimension.maskbook.common.ui.tab.TabScreen
@@ -38,7 +36,9 @@ import com.dimension.maskbook.labs.ui.tab.LabsTabScreen
import com.dimension.maskbook.labs.viewmodel.LabsViewModel
import com.dimension.maskbook.labs.viewmodel.LuckDropViewModel
import com.dimension.maskbook.labs.viewmodel.PluginSettingsViewModel
-import org.koin.androidx.viewmodel.dsl.viewModel
+import moe.tlaster.koin.viewModel
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.RouteBuilder
import org.koin.core.qualifier.named
import org.koin.dsl.bind
import org.koin.dsl.module
@@ -46,7 +46,7 @@ import org.koin.mp.KoinPlatformTools
object LabsSetup : ModuleSetup {
- override fun NavGraphBuilder.route(navController: NavController) {
+ override fun RouteBuilder.route(navController: NavController) {
generatedRoute(navController)
}
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/LabsScene.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/LabsScene.kt
index 79b64f38..584dcb20 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/LabsScene.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/LabsScene.kt
@@ -32,6 +32,7 @@ import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowForwardIos
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@@ -39,7 +40,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.MaskListItem
import com.dimension.maskbook.common.ui.widget.MaskScaffold
import com.dimension.maskbook.common.ui.widget.MaskSingleLineTopAppBar
@@ -50,7 +50,7 @@ import com.dimension.maskbook.labs.R
import com.dimension.maskbook.labs.export.model.AppKey
import com.dimension.maskbook.labs.viewmodel.AppDisplayData
import com.dimension.maskbook.labs.viewmodel.LabsViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@Composable
fun LabsScene(
@@ -58,7 +58,7 @@ fun LabsScene(
onItemClick: (AppKey) -> Unit,
) {
val viewModel: LabsViewModel = getViewModel()
- val apps by viewModel.apps.observeAsState(initial = emptyList())
+ val apps by viewModel.apps.collectAsState(initial = emptyList())
MaskScaffold(
topBar = {
MaskSingleLineTopAppBar(
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/LabsTransakScene.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/LabsTransakScene.kt
index 16afd877..fb41eae0 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/LabsTransakScene.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/LabsTransakScene.kt
@@ -30,6 +30,7 @@ import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -39,7 +40,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.viewinterop.AndroidView
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
@@ -54,7 +54,7 @@ import com.dimension.maskbook.labs.R
import com.dimension.maskbook.labs.export.model.TransakConfig
import com.dimension.maskbook.labs.route.LabsRoute
import com.dimension.maskbook.wallet.export.WalletServices
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
@NavGraphDestination(
route = LabsRoute.LabsTransak,
@@ -68,7 +68,7 @@ fun LabsTransakScene(
@Back onBack: () -> Unit,
) {
val repo = get()
- val currentWallet by repo.currentWallet.observeAsState(null)
+ val currentWallet by repo.currentWallet.collectAsState(null)
val transakConfig = remember(currentWallet) {
TransakConfig(
isStaging = BuildConfig.DEBUG,
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/PluginSettingsScene.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/PluginSettingsScene.kt
index 32e36bf2..c6edbf6e 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/PluginSettingsScene.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/PluginSettingsScene.kt
@@ -54,7 +54,7 @@ import com.dimension.maskbook.labs.R
import com.dimension.maskbook.labs.route.LabsRoute
import com.dimension.maskbook.labs.viewmodel.PluginDisplayData
import com.dimension.maskbook.labs.viewmodel.PluginSettingsViewModel
-import org.koin.androidx.compose.viewModel
+import moe.tlaster.koin.compose.getViewModel
@NavGraphDestination(
route = LabsRoute.PluginSettings,
@@ -65,7 +65,7 @@ import org.koin.androidx.compose.viewModel
fun PluginSettingsScene(
@Back onBack: () -> Unit,
) {
- val viewModel by viewModel()
+ val viewModel = getViewModel()
val apps by viewModel.apps.collectAsState()
val shouldShowPluginSettingsTipDialog by viewModel.shouldShowPluginSettingsTipDialog.collectAsState()
MaskScene {
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/redpacket/LuckyDropModal.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/redpacket/LuckyDropModal.kt
index c2e89bc3..ca0280ea 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/redpacket/LuckyDropModal.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/scenes/redpacket/LuckyDropModal.kt
@@ -20,6 +20,7 @@
*/
package com.dimension.maskbook.labs.ui.scenes.redpacket
+import android.net.Uri
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -51,7 +52,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import coil.compose.rememberImagePainter
import com.dimension.maskbook.common.bigDecimal.BigDecimal
import com.dimension.maskbook.common.ext.eventFlow
@@ -75,7 +75,8 @@ import com.dimension.maskbook.labs.route.LabsRoute
import com.dimension.maskbook.labs.ui.widget.ClaimLoadingIndicator
import com.dimension.maskbook.labs.ui.widget.RedPacketClaimButton
import com.dimension.maskbook.labs.viewmodel.LuckDropViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
import kotlin.math.pow
@@ -138,7 +139,7 @@ fun LuckDropModal(
WalletTokenCard(
wallet = stateData.wallet,
onClick = {
- navController.navigateUri(Deeplinks.Wallet.SwitchWallet)
+ navController.navigateUri(Uri.parse(Deeplinks.Wallet.SwitchWallet))
}
)
Spacer(Modifier.height(24.dp))
@@ -146,7 +147,7 @@ fun LuckDropModal(
enabled = stateData.buttonEnabled && !loading,
onClick = {
viewModel.getSendTransactionData(stateData)?.let { data ->
- navController.navigateUri(Deeplinks.Wallet.SendTokenConfirm(data))
+ navController.navigateUri(Uri.parse(Deeplinks.Wallet.SendTokenConfirm(data)))
}
},
modifier = Modifier.fillMaxWidth(),
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/tab/LabsTabScreen.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/tab/LabsTabScreen.kt
index e3b56110..ba506281 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/tab/LabsTabScreen.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/ui/tab/LabsTabScreen.kt
@@ -21,13 +21,13 @@
package com.dimension.maskbook.labs.ui.tab
import androidx.compose.runtime.Composable
-import androidx.navigation.NavController
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.ui.tab.TabScreen
import com.dimension.maskbook.labs.R
import com.dimension.maskbook.labs.export.model.AppKey
import com.dimension.maskbook.labs.route.LabsRoute
import com.dimension.maskbook.labs.ui.scenes.LabsScene
+import moe.tlaster.precompose.navigation.NavController
class LabsTabScreen : TabScreen {
override val route = CommonRoute.Main.Tabs.Labs
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/LabsViewModel.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/LabsViewModel.kt
index 4f81641a..e48ec341 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/LabsViewModel.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/LabsViewModel.kt
@@ -22,8 +22,6 @@ package com.dimension.maskbook.labs.viewmodel
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.labs.R
import com.dimension.maskbook.labs.export.model.AppKey
@@ -32,6 +30,8 @@ import com.dimension.maskbook.wallet.export.WalletServices
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
data class AppDisplayData(
val key: AppKey,
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/LuckDropViewModel.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/LuckDropViewModel.kt
index b764d18c..df9176de 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/LuckDropViewModel.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/LuckDropViewModel.kt
@@ -21,8 +21,6 @@
package com.dimension.maskbook.labs.viewmodel
import android.util.Log
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.exception.NullTransactionReceiptException
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.ext.decodeJson
@@ -49,6 +47,8 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
import org.web3j.abi.FunctionEncoder
import kotlin.time.Duration.Companion.seconds
diff --git a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/PluginSettingsViewModel.kt b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/PluginSettingsViewModel.kt
index d80c6747..c4ecdb15 100644
--- a/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/PluginSettingsViewModel.kt
+++ b/labs/src/androidMain/kotlin/com/dimension/maskbook/labs/viewmodel/PluginSettingsViewModel.kt
@@ -22,8 +22,6 @@ package com.dimension.maskbook.labs.viewmodel
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.labs.R
import com.dimension.maskbook.labs.export.model.AppKey
@@ -33,6 +31,8 @@ import com.dimension.maskbook.wallet.export.WalletServices
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
data class PluginDisplayData(
val key: AppKey,
diff --git a/persona/export/build.gradle.kts b/persona/export/build.gradle.kts
index c6a4f35b..1b9c5d43 100644
--- a/persona/export/build.gradle.kts
+++ b/persona/export/build.gradle.kts
@@ -8,8 +8,8 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
- implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
+ compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
+ compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
}
}
val commonTest by getting {
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/PersonaSetup.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/PersonaSetup.kt
index 5257fed8..359d3dc8 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/PersonaSetup.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/PersonaSetup.kt
@@ -22,12 +22,11 @@ package com.dimension.maskbook.persona
import android.content.Context
import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
import androidx.room.Room
import com.dimension.maskbook.common.IoScopeName
import com.dimension.maskbook.common.LocalBackupAccount
import com.dimension.maskbook.common.ModuleSetup
+import com.dimension.maskbook.common.route.navigation
import com.dimension.maskbook.common.ui.tab.TabScreen
import com.dimension.maskbook.persona.data.JSMethod
import com.dimension.maskbook.persona.data.JSMethodV2
@@ -76,10 +75,11 @@ import com.dimension.maskbook.persona.viewmodel.register.PhoneRemoteBackupRecove
import com.dimension.maskbook.persona.viewmodel.register.RemoteBackupRecoveryViewModelBase
import com.dimension.maskbook.persona.viewmodel.social.DisconnectSocialViewModel
import com.dimension.maskbook.persona.viewmodel.social.UserNameModalViewModel
-import com.google.accompanist.navigation.animation.navigation
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
-import org.koin.androidx.viewmodel.dsl.viewModel
+import moe.tlaster.koin.viewModel
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.RouteBuilder
import org.koin.core.qualifier.named
import org.koin.dsl.bind
import org.koin.dsl.binds
@@ -89,7 +89,7 @@ import org.koin.mp.KoinPlatformTools
object PersonaSetup : ModuleSetup {
@OptIn(ExperimentalAnimationApi::class)
- override fun NavGraphBuilder.route(navController: NavController) {
+ override fun RouteBuilder.route(navController: NavController) {
generatedRoute(navController)
navigation(
startDestination = PersonaRoute.Register.CreateIdentity.Backup.path,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/route/RegisterRoute.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/route/RegisterRoute.kt
index 6226c12d..c064e9c0 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/route/RegisterRoute.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/route/RegisterRoute.kt
@@ -27,14 +27,15 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.ext.navigate
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
@@ -60,7 +61,8 @@ import com.dimension.maskbook.persona.ui.scenes.register.recovery.RecoveryHomeSc
import com.dimension.maskbook.persona.viewmodel.recovery.IdentityViewModel
import com.dimension.maskbook.persona.viewmodel.recovery.PrivateKeyViewModel
import kotlinx.coroutines.launch
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -205,8 +207,8 @@ fun RegisterRecoveryIdentity(
val viewModel: IdentityViewModel = getViewModel {
parametersOf(name)
}
- val identity by viewModel.identity.observeAsState()
- val canConfirm by viewModel.canConfirm.observeAsState()
+ val identity by viewModel.identity.collectAsState()
+ val canConfirm by viewModel.canConfirm.collectAsState()
val from = stringResource(R.string.scene_identity_mnemonic_import_title)
val scope = rememberCoroutineScope()
IdentityScene(
@@ -256,8 +258,8 @@ fun RegisterRecoveryPrivateKey(
@Back onBack: () -> Unit,
) {
val viewModel: PrivateKeyViewModel = getViewModel()
- val privateKey by viewModel.privateKey.observeAsState()
- val canConfirm by viewModel.canConfirm.observeAsState()
+ val privateKey by viewModel.privateKey.collectAsState()
+ val canConfirm by viewModel.canConfirm.collectAsState()
val scope = rememberCoroutineScope()
val from = stringResource(R.string.scene_identity_privatekey_import_title)
PrivateKeyScene(
@@ -310,7 +312,7 @@ fun RegisterRecoveryAlreadyExists(
PersonaAlreadyExitsDialog(
onBack = onBack,
onConfirm = {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona)),
navOptions {
popUpTo(PersonaRoute.Register.Init) {
@@ -343,7 +345,7 @@ fun RegisterRecoveryComplected(
},
buttons = {
PrimaryButton(onClick = {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona)),
navOptions {
popUpTo(PersonaRoute.Register.Init) {
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/route/SynchronizationRoute.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/route/SynchronizationRoute.kt
index 2c014adc..720de4a3 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/route/SynchronizationRoute.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/route/SynchronizationRoute.kt
@@ -29,10 +29,10 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
import com.dimension.maskbook.common.ext.decodeBase64
import com.dimension.maskbook.common.ext.ifNullOrEmpty
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.Persona
@@ -54,7 +54,8 @@ import com.dimension.maskbook.persona.ui.scenes.register.recovery.PersonaAlready
import com.dimension.maskbook.persona.viewmodel.recovery.IdentityViewModel
import com.dimension.maskbook.persona.viewmodel.recovery.PrivateKeyViewModel
import kotlinx.coroutines.launch
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -71,7 +72,7 @@ fun SynchronizationScan(
onBack = onBack,
onResult = {
try {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(it),
navOptions {
popUpTo(PersonaRoute.Synchronization.Scan) {
@@ -113,7 +114,7 @@ fun SynchronizationSuccess(
},
buttons = {
PrimaryButton(onClick = {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona)),
navOptions {
launchSingleTop = true
@@ -170,7 +171,7 @@ fun SynchronizationPersonaAlreadyExists(
PersonaAlreadyExitsDialog(
onBack = onBack,
onConfirm = {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona)),
navOptions {
launchSingleTop = true
@@ -236,7 +237,7 @@ private fun NavController.handleResult(result: Result) {
PersonaRoute.Synchronization.Success,
navOptions {
currentBackStackEntry?.let { backStackEntry ->
- popUpTo(backStackEntry.destination.id) {
+ popUpTo(backStackEntry.route.route) {
inclusive = true
}
}
@@ -250,7 +251,7 @@ private fun NavController.handleResult(result: Result) {
PersonaRoute.Synchronization.Failed,
navOptions {
currentBackStackEntry?.let { backStackEntry ->
- popUpTo(backStackEntry.destination.id) {
+ popUpTo(backStackEntry.route.route) {
inclusive = true
}
}
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/BackupPasswordScene.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/BackupPasswordScene.kt
index 96c671a8..1342dc86 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/BackupPasswordScene.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/BackupPasswordScene.kt
@@ -21,10 +21,9 @@
package com.dimension.maskbook.persona.ui.scenes
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navOptions
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
import com.dimension.maskbook.common.route.navigationComposeBottomSheetPackage
@@ -33,7 +32,8 @@ import com.dimension.maskbook.common.routeProcessor.annotations.Path
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.ui.scenes.register.BackUpPasswordModal
import com.dimension.maskbook.persona.viewmodel.BackUpPasswordViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
@NavGraphDestination(
route = PersonaRoute.BackUpPassword.path,
@@ -49,8 +49,8 @@ fun BackUpPassword(
@Path("target") target: String,
) {
val viewModel = getViewModel()
- val password by viewModel.password.observeAsState(initial = "")
- val passwordValid by viewModel.passwordValid.observeAsState(initial = false)
+ val password by viewModel.password.collectAsState(initial = "")
+ val passwordValid by viewModel.passwordValid.collectAsState(initial = false)
BackUpPasswordModal(
password = password,
onPasswordChanged = { viewModel.setPassword(it) },
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/DownloadQrCodeScene.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/DownloadQrCodeScene.kt
index 857b98fb..5741c051 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/DownloadQrCodeScene.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/DownloadQrCodeScene.kt
@@ -25,11 +25,10 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
-import androidx.navigation.NavController
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ext.onFinished
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
@@ -42,7 +41,8 @@ import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.DownloadQrCodeViewModel
import kotlinx.coroutines.launch
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -59,8 +59,8 @@ fun DownloadQrCodeScene(
val viewModel = getViewModel {
parametersOf(DownloadQrCodeViewModel.IdType.valueOf(idType), idBase64)
}
- val personaQrCode by viewModel.personaQrCode.observeAsState()
- val filePickerLaunched by viewModel.filePickerLaunched.observeAsState()
+ val personaQrCode by viewModel.personaQrCode.collectAsState()
+ val filePickerLaunched by viewModel.filePickerLaunched.collectAsState()
val context = LocalContext.current
val scope = rememberCoroutineScope()
val inAppNotification = LocalInAppNotification.current
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/ExportPrivateKeyScene.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/ExportPrivateKeyScene.kt
index fe8c2539..34676eba 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/ExportPrivateKeyScene.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/ExportPrivateKeyScene.kt
@@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
@@ -40,7 +41,6 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
import com.dimension.maskbook.common.routeProcessor.annotations.Back
@@ -56,7 +56,7 @@ import com.dimension.maskbook.common.ui.widget.button.SecondaryButton
import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.ExportPrivateKeyViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@NavGraphDestination(
route = PersonaRoute.ExportPrivateKey,
@@ -68,7 +68,7 @@ fun ExportPrivateKeyScene(
@Back onBack: () -> Unit,
) {
val viewModel = getViewModel()
- val text by viewModel.privateKey.observeAsState(initial = "")
+ val text by viewModel.privateKey.collectAsState(initial = "")
val annotatedText = buildAnnotatedString {
append(stringResource(R.string.scene_persona_export_private_key_tips))
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/LogoutDialog.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/LogoutDialog.kt
index ed0bb321..1d6c58da 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/LogoutDialog.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/LogoutDialog.kt
@@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.navigationComposeDialog
import com.dimension.maskbook.common.route.navigationComposeDialogPackage
@@ -42,7 +41,8 @@ import com.dimension.maskbook.common.ui.widget.button.SecondaryButton
import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.repository.IPersonaRepository
import com.dimension.maskbook.persona.route.PersonaRoute
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
+import moe.tlaster.precompose.navigation.NavController
@NavGraphDestination(
route = PersonaRoute.Logout,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaInfoScene.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaInfoScene.kt
index 1d7ec3b3..6b42a9ce 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaInfoScene.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaInfoScene.kt
@@ -84,8 +84,8 @@ import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.calculateCurrentOffsetForPage
import com.google.accompanist.pager.rememberPagerState
-import org.koin.androidx.compose.get
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.get
+import moe.tlaster.koin.compose.getViewModel
import kotlin.math.absoluteValue
private enum class PersonaInfoData(val title: String) {
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaMenuScene.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaMenuScene.kt
index 86854b00..e1a67f1f 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaMenuScene.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaMenuScene.kt
@@ -46,8 +46,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.ext.encodeBase64
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
@@ -63,7 +63,8 @@ import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.DownloadQrCodeViewModel
import com.dimension.maskbook.persona.viewmodel.PersonaMenuViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
@NavGraphDestination(
route = PersonaRoute.PersonaMenu,
@@ -151,9 +152,9 @@ fun PersonaMenuScene(
onClick = {
// first check if it has backup password
if (backupPassword.isEmpty()) {
- navController.navigate(Uri.parse(Deeplinks.Setting.SetupPasswordDialog))
+ navController.navigateUri(Uri.parse(Deeplinks.Setting.SetupPasswordDialog))
} else {
- navController.navigate(Uri.parse(Deeplinks.Persona.BackUpPassword(PersonaRoute.ExportPrivateKey)))
+ navController.navigateUri(Uri.parse(Deeplinks.Persona.BackUpPassword(PersonaRoute.ExportPrivateKey)))
}
}
) {
@@ -176,10 +177,10 @@ fun PersonaMenuScene(
onClick = {
// first check if it has backup password
if (backupPassword.isEmpty()) {
- navController.navigate(Uri.parse(Deeplinks.Setting.SetupPasswordDialog))
+ navController.navigateUri(Uri.parse(Deeplinks.Setting.SetupPasswordDialog))
} else {
currentPersona?.let {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(
Deeplinks.Persona.BackUpPassword(
PersonaRoute.DownloadQrCode(
@@ -210,7 +211,7 @@ fun PersonaMenuScene(
modifier = Modifier.fillMaxWidth(),
elevation = 0.dp,
onClick = {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(if (backupPassword.isEmpty() || paymentPassword.isEmpty()) Deeplinks.Setting.SetupPasswordDialog else Deeplinks.Setting.BackupData.BackupSelection)
)
}
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaScene.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaScene.kt
index 6bcc6ff9..6b097128 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaScene.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/PersonaScene.kt
@@ -44,7 +44,7 @@ import com.dimension.maskbook.persona.export.model.Network
import com.dimension.maskbook.persona.export.model.PersonaData
import com.dimension.maskbook.persona.export.model.SocialData
import com.dimension.maskbook.persona.viewmodel.PersonaViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@ExperimentalAnimationApi
@Composable
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/RenamePersonaModal.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/RenamePersonaModal.kt
index 44ae9a94..c6448774 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/RenamePersonaModal.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/RenamePersonaModal.kt
@@ -31,7 +31,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
import com.dimension.maskbook.common.route.navigationComposeBottomSheetPackage
import com.dimension.maskbook.common.routeProcessor.annotations.NavGraphDestination
@@ -42,7 +41,8 @@ import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.RenamePersonaViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/SwitchPersonaModal.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/SwitchPersonaModal.kt
index 06f693e9..64c9e39c 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/SwitchPersonaModal.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/SwitchPersonaModal.kt
@@ -33,14 +33,14 @@ import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Add
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
import com.dimension.maskbook.common.route.navigationComposeBottomSheetPackage
@@ -50,7 +50,8 @@ import com.dimension.maskbook.common.ui.widget.MaskSelection
import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.SwitchPersonaViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
@NavGraphDestination(
route = PersonaRoute.SwitchPersona,
@@ -62,8 +63,8 @@ fun SwitchPersonaModal(
navController: NavController,
) {
val viewModel = getViewModel()
- val currentPersonaData by viewModel.current.observeAsState(initial = null)
- val items by viewModel.items.observeAsState(initial = emptyList())
+ val currentPersonaData by viewModel.current.collectAsState(initial = null)
+ val items by viewModel.items.collectAsState(initial = emptyList())
MaskModal(
title = {
@@ -98,9 +99,7 @@ fun SwitchPersonaModal(
MaskSelection(
selected = false,
onClicked = {
- navController.navigate(
- Uri.parse(Deeplinks.Persona.Register.CreatePersona)
- )
+ navController.navigateUri(Uri.parse(Deeplinks.Persona.Register.CreatePersona))
},
content = {
Text(
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/avatar/PersonaAvatarModal.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/avatar/PersonaAvatarModal.kt
index 9b169632..dd35db61 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/avatar/PersonaAvatarModal.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/avatar/PersonaAvatarModal.kt
@@ -27,7 +27,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
import com.dimension.maskbook.common.route.navigationComposeBottomSheetPackage
import com.dimension.maskbook.common.routeProcessor.annotations.Back
@@ -37,7 +36,8 @@ import com.dimension.maskbook.common.ui.widget.button.MaskListItemButton
import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.repository.IPersonaRepository
import com.dimension.maskbook.persona.route.PersonaRoute
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
+import moe.tlaster.precompose.navigation.NavController
@NavGraphDestination(
route = PersonaRoute.PersonaAvatarModal,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/avatar/SetAvatarScene.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/avatar/SetAvatarScene.kt
index bad9c9fc..0c03d3fe 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/avatar/SetAvatarScene.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/avatar/SetAvatarScene.kt
@@ -56,7 +56,7 @@ import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.utils.ImagePicker
import com.dimension.maskbook.persona.viewmodel.avatar.SetAvatarViewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@OptIn(ExperimentalPermissionsApi::class)
@NavGraphDestination(
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/post/PostScene.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/post/PostScene.kt
index 1ecb11fe..8bd22151 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/post/PostScene.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/post/PostScene.kt
@@ -36,22 +36,22 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.Pages
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.viewmodel.post.PostViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PostScene() {
val viewModel = getViewModel()
- val items by viewModel.items.observeAsState(initial = emptyList())
+ val items by viewModel.items.collectAsState(initial = emptyList())
if (!items.any()) {
EmptyPostScene()
} else {
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/createidentity/CreateIdentityHost.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/createidentity/CreateIdentityHost.kt
index 96ecdf9a..8f0fdede 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/createidentity/CreateIdentityHost.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/createidentity/CreateIdentityHost.kt
@@ -26,15 +26,15 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.navigation.NavController
import com.dimension.maskbook.common.ext.encodeBase64
import com.dimension.maskbook.common.ext.getNestedNavigationViewModel
import com.dimension.maskbook.common.ext.navigate
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
@@ -50,6 +50,7 @@ import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.DownloadQrCodeViewModel
import com.dimension.maskbook.persona.viewmodel.register.CreateIdentityViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
private const val GeneratedRouteName = "createIdentityRoute"
@@ -71,8 +72,8 @@ fun BackupRoute(
.getNestedNavigationViewModel(PersonaRoute.Register.CreateIdentity.Route) {
parametersOf(personaName)
}
- val words by viewModel.words.observeAsState(emptyList())
- val showNext by viewModel.showNext.observeAsState()
+ val words by viewModel.words.collectAsState(emptyList())
+ val showNext by viewModel.showNext.collectAsState()
BackupIdentityScene(
words = words.map { it.word },
onRefreshWords = {
@@ -127,7 +128,7 @@ fun ConfirmRoute(
PrimaryButton(
modifier = Modifier.fillMaxWidth(),
onClick = {
- navController.navigate(Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona))) {
+ navController.navigateUri(Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona))) {
launchSingleTop = true
if (isWelcome) {
popUpTo(PersonaRoute.Register.Init) {
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/local/RecoveryLocalHost.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/local/RecoveryLocalHost.kt
index ec92d428..508d045f 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/local/RecoveryLocalHost.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/local/RecoveryLocalHost.kt
@@ -55,11 +55,10 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.ext.getNestedNavigationViewModel
import com.dimension.maskbook.common.ext.humanizeFileSize
import com.dimension.maskbook.common.ext.humanizeTimestamp
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navigate
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
@@ -87,6 +86,7 @@ import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.recovery.RecoveryLocalViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
import java.io.File
@@ -384,7 +384,7 @@ fun ImportWalletScene(
parametersOf(uri, account)
}
val paymentPassword by viewModel.paymentPassword.collectAsState(initial = null)
- val file by viewModel.file.observeAsState(initial = null)
+ val file by viewModel.file.collectAsState(initial = null)
MaskModal(
title = { Text(text = "Wallets for recovery") },
) {
@@ -456,7 +456,7 @@ fun ImportSuccessScene(
.getNestedNavigationViewModel(PersonaRoute.Register.Recovery.LocalBackup.Route) {
parametersOf(uri, account)
}
- val meta by viewModel.meta.observeAsState(initial = null)
+ val meta by viewModel.meta.collectAsState(initial = null)
MaskScene {
MaskScaffold(
topBar = {
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/local/VerifyPaymentPasswordModal.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/local/VerifyPaymentPasswordModal.kt
index 125ca984..376f1ea8 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/local/VerifyPaymentPasswordModal.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/local/VerifyPaymentPasswordModal.kt
@@ -37,7 +37,7 @@ import com.dimension.maskbook.common.ui.widget.MaskPasswordInputField
import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.viewmodel.VerifyPaymentPasswordViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@Composable
fun VerifyPaymentPasswordModal(
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/remote/RemoteBackupRecoveryHost.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/remote/RemoteBackupRecoveryHost.kt
index 36499622..7bd15b78 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/remote/RemoteBackupRecoveryHost.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/register/recovery/remote/RemoteBackupRecoveryHost.kt
@@ -32,6 +32,7 @@ import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -40,8 +41,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navigate
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
import com.dimension.maskbook.common.route.navigationComposeBottomSheetPackage
import com.dimension.maskbook.common.route.navigationComposeDialog
@@ -59,7 +59,8 @@ import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.register.EmailRemoteBackupRecoveryViewModel
import com.dimension.maskbook.persona.viewmodel.register.PhoneRemoteBackupRecoveryViewModel
import com.dimension.maskbook.persona.viewmodel.register.RemoteBackupRecoveryViewModelBase
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -130,11 +131,11 @@ fun RegisterRecoveryRemoteBackupRecoveryRemoteBackupRecoveryEmailCode(
LaunchedEffect(Unit) {
viewModel.startCountDown()
}
- val canSend by viewModel.canSend.observeAsState(initial = false)
- val countDown by viewModel.countdown.observeAsState(initial = 60)
- val loading by viewModel.loading.observeAsState(initial = false)
- val code by viewModel.code.observeAsState(initial = "")
- val codeValid by viewModel.codeValid.observeAsState(initial = true)
+ val canSend by viewModel.canSend.collectAsState(initial = false)
+ val countDown by viewModel.countdown.collectAsState(initial = 60)
+ val loading by viewModel.loading.collectAsState(initial = false)
+ val code by viewModel.code.collectAsState(initial = "")
+ val codeValid by viewModel.codeValid.collectAsState(initial = true)
EmailCodeInputModal(
email = email,
code = code,
@@ -168,9 +169,9 @@ fun RegisterRecoveryRemoteBackupRecoveryRemoteBackupRecoveryEmail(
val viewModel = getViewModel {
parametersOf(requestNavigate)
}
- val email by viewModel.value.observeAsState(initial = "")
- val emailValid by viewModel.valueValid.observeAsState(initial = true)
- val loading by viewModel.loading.observeAsState(initial = false)
+ val email by viewModel.value.collectAsState(initial = "")
+ val emailValid by viewModel.valueValid.collectAsState(initial = true)
+ val loading by viewModel.loading.collectAsState(initial = false)
MaskModal(
title = {
Text(text = stringResource(R.string.scene_restore_titles_recovery_with_email))
@@ -254,11 +255,11 @@ fun RegisterRecoveryRemoteBackupRecoveryRemoteBackupRecoveryPhoneCode(
LaunchedEffect(Unit) {
viewModel.startCountDown()
}
- val canSend by viewModel.canSend.observeAsState(initial = false)
- val countDown by viewModel.countdown.observeAsState(initial = 60)
- val loading by viewModel.loading.observeAsState(initial = false)
- val code by viewModel.code.observeAsState(initial = "")
- val codeValid by viewModel.codeValid.observeAsState(initial = true)
+ val canSend by viewModel.canSend.collectAsState(initial = false)
+ val countDown by viewModel.countdown.collectAsState(initial = 60)
+ val loading by viewModel.loading.collectAsState(initial = false)
+ val code by viewModel.code.collectAsState(initial = "")
+ val codeValid by viewModel.codeValid.collectAsState(initial = true)
MaskModal(
title = {
Text(text = stringResource(R.string.scene_restore_titles_recovery_with_mobile))
@@ -335,10 +336,10 @@ fun RegisterRecoveryRemoteBackupRecoveryRemoteBackupRecoveryPhone(
val viewModel = getViewModel {
parametersOf(requestNavigate)
}
- val regionCode by viewModel.regionCode.observeAsState(initial = "+86")
- val phone by viewModel.value.observeAsState(initial = "")
- val phoneValid by viewModel.valueValid.observeAsState(initial = true)
- val loading by viewModel.loading.observeAsState(initial = false)
+ val regionCode by viewModel.regionCode.collectAsState(initial = "+86")
+ val phone by viewModel.value.collectAsState(initial = "")
+ val phoneValid by viewModel.valueValid.collectAsState(initial = true)
+ val loading by viewModel.loading.collectAsState(initial = false)
MaskModal(
title = {
Text(text = stringResource(R.string.scene_restore_titles_recovery_with_mobile))
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/ConnectAccountModal.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/ConnectAccountModal.kt
index 95202eba..5a48c578 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/ConnectAccountModal.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/ConnectAccountModal.kt
@@ -41,8 +41,8 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import coil.compose.rememberImagePainter
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
@@ -56,7 +56,8 @@ import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.model.SocialProfile
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.social.UserNameModalViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -113,7 +114,7 @@ fun ConnectAccountModal(
modifier = Modifier.fillMaxWidth(),
onClick = {
viewModel.done(personaId, name)
- navController.navigate(Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona)))
+ navController.navigateUri(Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Persona)))
},
) {
Text(text = stringResource(R.string.scene_social_connect_button_title))
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/ConnectSocial.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/ConnectSocial.kt
index ba965182..63fd3e97 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/ConnectSocial.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/ConnectSocial.kt
@@ -20,12 +20,12 @@
*/
package com.dimension.maskbook.persona.ui.scenes.social
-import androidx.navigation.NavController
import com.dimension.maskbook.common.ext.navigateToExtension
import com.dimension.maskbook.common.ext.toSite
import com.dimension.maskbook.persona.export.model.PlatformType
import com.dimension.maskbook.persona.repository.IPersonaRepository
import com.dimension.maskbook.persona.route.PersonaRoute
+import moe.tlaster.precompose.navigation.NavController
fun connectSocial(
controller: NavController,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/DisconnectSocialDialog.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/DisconnectSocialDialog.kt
index 16384da5..4e937c06 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/DisconnectSocialDialog.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/DisconnectSocialDialog.kt
@@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.route.navigationComposeDialog
import com.dimension.maskbook.common.route.navigationComposeDialogPackage
import com.dimension.maskbook.common.routeProcessor.annotations.Back
@@ -44,7 +43,8 @@ import com.dimension.maskbook.persona.R
import com.dimension.maskbook.persona.export.model.PlatformType
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.viewmodel.social.DisconnectSocialViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
@NavGraphDestination(
route = PersonaRoute.DisconnectSocial.path,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/SelectPlatformModal.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/SelectPlatformModal.kt
index 6d7715e1..0b7c72dd 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/SelectPlatformModal.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/scenes/social/SelectPlatformModal.kt
@@ -32,7 +32,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
import com.dimension.maskbook.common.route.navigationComposeBottomSheetPackage
import com.dimension.maskbook.common.routeProcessor.annotations.NavGraphDestination
@@ -45,7 +44,8 @@ import com.dimension.maskbook.persona.model.platform
import com.dimension.maskbook.persona.model.title
import com.dimension.maskbook.persona.repository.IPersonaRepository
import com.dimension.maskbook.persona.route.PersonaRoute
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
+import moe.tlaster.precompose.navigation.NavController
private val items = listOf(
Network.Twitter,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/tab/PersonasTabScreen.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/tab/PersonasTabScreen.kt
index d598fbb4..76b21ed9 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/tab/PersonasTabScreen.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/ui/tab/PersonasTabScreen.kt
@@ -23,8 +23,8 @@ package com.dimension.maskbook.persona.ui.tab
import android.net.Uri
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.ui.tab.TabScreen
@@ -35,7 +35,8 @@ import com.dimension.maskbook.persona.repository.IPersonaRepository
import com.dimension.maskbook.persona.route.PersonaRoute
import com.dimension.maskbook.persona.ui.scenes.PersonaScene
import com.dimension.maskbook.persona.ui.scenes.social.connectSocial
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
+import moe.tlaster.precompose.navigation.NavController
class PersonasTabScreen : TabScreen {
override val route = CommonRoute.Main.Tabs.Persona
@@ -49,10 +50,10 @@ class PersonasTabScreen : TabScreen {
PersonaScene(
onBack = onBack,
onPersonaCreateClick = {
- navController.navigate(Uri.parse(Deeplinks.Persona.Register.WelcomeCreatePersona))
+ navController.navigateUri(Uri.parse(Deeplinks.Persona.Register.WelcomeCreatePersona))
},
onPersonaRecoveryClick = {
- navController.navigate(Uri.parse(Deeplinks.Persona.Recovery))
+ navController.navigateUri(Uri.parse(Deeplinks.Persona.Recovery))
},
onPersonaNameClick = {
navController.navigate(PersonaRoute.PersonaMenu)
@@ -83,7 +84,7 @@ class PersonasTabScreen : TabScreen {
onSocialItemClick = { _, social ->
social.network.toPlatform()?.let {
repository.setPlatform(it)
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.WebContent(null)),
navOptions {
launchSingleTop = true
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/BackUpPasswordViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/BackUpPasswordViewModel.kt
index d1ea3e54..c70575a3 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/BackUpPasswordViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/BackUpPasswordViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.persona.viewmodel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.util.BiometricAuthenticator
import com.dimension.maskbook.common.viewmodel.BiometricViewModel
import com.dimension.maskbook.setting.export.SettingServices
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.viewModelScope
class BackUpPasswordViewModel(
settingsRepository: SettingServices,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/DownloadQrCodeViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/DownloadQrCodeViewModel.kt
index 2f860515..eb4540f6 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/DownloadQrCodeViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/DownloadQrCodeViewModel.kt
@@ -31,8 +31,6 @@ import android.graphics.pdf.PdfDocument
import android.graphics.pdf.PdfDocument.PageInfo
import android.net.Uri
import android.util.Base64
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.ext.decodeBase64
import com.dimension.maskbook.common.ext.encodeBase64
@@ -45,6 +43,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.withContext
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class DownloadQrCodeViewModel(
private val idType: IdType,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/ExportPrivateKeyViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/ExportPrivateKeyViewModel.kt
index 245b5b33..ab6f2164 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/ExportPrivateKeyViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/ExportPrivateKeyViewModel.kt
@@ -20,12 +20,12 @@
*/
package com.dimension.maskbook.persona.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.repository.IPersonaRepository
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class ExportPrivateKeyViewModel(
private val repository: IPersonaRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/PersonaMenuViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/PersonaMenuViewModel.kt
index 99a197e6..14fc11b3 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/PersonaMenuViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/PersonaMenuViewModel.kt
@@ -20,11 +20,11 @@
*/
package com.dimension.maskbook.persona.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.repository.IPersonaRepository
import com.dimension.maskbook.setting.export.SettingServices
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class PersonaMenuViewModel(
private val repository: IPersonaRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/PersonaViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/PersonaViewModel.kt
index 4d300991..380f21aa 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/PersonaViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/PersonaViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.persona.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.export.model.PersonaData
import com.dimension.maskbook.persona.repository.IPersonaRepository
@@ -30,6 +28,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class PersonaViewModel(
private val personaRepository: IPersonaRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/RenamePersonaViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/RenamePersonaViewModel.kt
index 40ac2bc8..43ad451e 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/RenamePersonaViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/RenamePersonaViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.persona.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.datasource.DbPersonaDataSource
import com.dimension.maskbook.persona.repository.IPersonaRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class RenamePersonaViewModel(
private val personaRepository: IPersonaRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/SwitchPersonaViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/SwitchPersonaViewModel.kt
index 25c5d699..1e99cacf 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/SwitchPersonaViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/SwitchPersonaViewModel.kt
@@ -20,12 +20,12 @@
*/
package com.dimension.maskbook.persona.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.datasource.DbPersonaDataSource
import com.dimension.maskbook.persona.export.model.PersonaData
import com.dimension.maskbook.persona.repository.IPersonaRepository
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class SwitchPersonaViewModel(
private val personaRepository: IPersonaRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/VerifyPaymentPasswordViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/VerifyPaymentPasswordViewModel.kt
index a1c95699..b06b9cf5 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/VerifyPaymentPasswordViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/VerifyPaymentPasswordViewModel.kt
@@ -20,12 +20,12 @@
*/
package com.dimension.maskbook.persona.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.export.SettingServices
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class VerifyPaymentPasswordViewModel(
settingsRepository: SettingServices,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/avatar/SetAvatarViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/avatar/SetAvatarViewModel.kt
index cdaa66a3..798fe699 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/avatar/SetAvatarViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/avatar/SetAvatarViewModel.kt
@@ -21,9 +21,9 @@
package com.dimension.maskbook.persona.viewmodel.avatar
import android.net.Uri
-import androidx.lifecycle.ViewModel
import com.dimension.maskbook.persona.repository.IPersonaRepository
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
class SetAvatarViewModel(
private val repository: IPersonaRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/contacts/ContactsViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/contacts/ContactsViewModel.kt
index db7946f9..ef48f338 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/contacts/ContactsViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/contacts/ContactsViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.persona.viewmodel.contacts
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.repository.IContactsRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class ContactsViewModel(
repository: IContactsRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/post/PostViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/post/PostViewModel.kt
index 47552932..1190eb25 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/post/PostViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/post/PostViewModel.kt
@@ -20,14 +20,14 @@
*/
package com.dimension.maskbook.persona.viewmodel.post
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.export.model.PersonaData
import com.dimension.maskbook.persona.model.PostData
import com.dimension.maskbook.persona.repository.IPersonaRepository
import com.dimension.maskbook.persona.repository.IPostRepository
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class PostViewModel(
repository: IPostRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/IdentityViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/IdentityViewModel.kt
index 24626243..956c9b5d 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/IdentityViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/IdentityViewModel.kt
@@ -20,14 +20,14 @@
*/
package com.dimension.maskbook.persona.viewmodel.recovery
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.export.PersonaServices
import com.dimension.maskbook.wallet.export.WalletServices
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class IdentityViewModel(
private val personaServices: PersonaServices,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/PrivateKeyViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/PrivateKeyViewModel.kt
index a15d402b..7068742c 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/PrivateKeyViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/PrivateKeyViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.persona.viewmodel.recovery
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.export.PersonaServices
import com.dimension.maskbook.wallet.export.WalletServices
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class PrivateKeyViewModel(
private val personaServices: PersonaServices,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/RecoveryLocalViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/RecoveryLocalViewModel.kt
index 79d1331d..5054bdb7 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/RecoveryLocalViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/recovery/RecoveryLocalViewModel.kt
@@ -22,8 +22,6 @@ package com.dimension.maskbook.persona.viewmodel.recovery
import android.content.ContentResolver
import android.net.Uri
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.export.BackupServices
@@ -33,6 +31,8 @@ import com.dimension.maskbook.setting.export.model.BackupMetaFile
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class RecoveryLocalViewModel(
private val backupServices: BackupServices,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/register/CreateIdentityViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/register/CreateIdentityViewModel.kt
index c99230fa..5a859451 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/register/CreateIdentityViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/register/CreateIdentityViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.persona.viewmodel.register
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.viewmodel.BaseMnemonicPhraseViewModel
import com.dimension.maskbook.persona.repository.IPersonaRepository
import com.dimension.maskbook.wallet.export.WalletServices
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.viewModelScope
class CreateIdentityViewModel(
private val personaName: String,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/register/RemoteBackupRecoveryViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/register/RemoteBackupRecoveryViewModel.kt
index e232d9f4..752fdb40 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/register/RemoteBackupRecoveryViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/register/RemoteBackupRecoveryViewModel.kt
@@ -21,14 +21,14 @@
package com.dimension.maskbook.persona.viewmodel.register
import android.os.CountDownTimer
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.export.BackupServices
import com.dimension.maskbook.setting.export.model.BackupFileMeta
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class PhoneRemoteBackupRecoveryViewModel(
requestNavigate: (NavigateArgs) -> Unit,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/social/DisconnectSocialViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/social/DisconnectSocialViewModel.kt
index d5ee172b..0b2815fd 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/social/DisconnectSocialViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/social/DisconnectSocialViewModel.kt
@@ -20,8 +20,8 @@
*/
package com.dimension.maskbook.persona.viewmodel.social
-import androidx.lifecycle.ViewModel
import com.dimension.maskbook.persona.repository.IPersonaRepository
+import moe.tlaster.precompose.viewmodel.ViewModel
class DisconnectSocialViewModel(
private val repository: IPersonaRepository,
diff --git a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/social/UserNameModalViewModel.kt b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/social/UserNameModalViewModel.kt
index c9ceb8a6..b53d1a47 100644
--- a/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/social/UserNameModalViewModel.kt
+++ b/persona/src/androidMain/kotlin/com/dimension/maskbook/persona/viewmodel/social/UserNameModalViewModel.kt
@@ -20,12 +20,12 @@
*/
package com.dimension.maskbook.persona.viewmodel.social
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.model.SocialProfile
import com.dimension.maskbook.persona.repository.IPersonaRepository
import kotlinx.coroutines.flow.MutableStateFlow
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class UserNameModalViewModel(
private val personaRepository: IPersonaRepository,
diff --git a/setting/export/build.gradle.kts b/setting/export/build.gradle.kts
index c6a4f35b..1b9c5d43 100644
--- a/setting/export/build.gradle.kts
+++ b/setting/export/build.gradle.kts
@@ -8,8 +8,8 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
- implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
+ compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
+ compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
}
}
val commonTest by getting {
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/SettingSetup.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/SettingSetup.kt
index e57e410b..8d7843bd 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/SettingSetup.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/SettingSetup.kt
@@ -21,11 +21,9 @@
package com.dimension.maskbook.setting
import android.content.Context
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
-import androidx.navigation.navigation
import com.dimension.maskbook.common.ModuleSetup
import com.dimension.maskbook.common.retrofit.retrofit
+import com.dimension.maskbook.common.route.navigation
import com.dimension.maskbook.common.ui.tab.TabScreen
import com.dimension.maskbook.setting.SettingSetup.route
import com.dimension.maskbook.setting.data.JSDataSource
@@ -54,13 +52,15 @@ import com.dimension.maskbook.setting.viewmodel.LanguageSettingsViewModel
import com.dimension.maskbook.setting.viewmodel.PaymentPasswordSettingsViewModel
import com.dimension.maskbook.setting.viewmodel.PhoneBackupViewModel
import com.dimension.maskbook.setting.viewmodel.PhoneSetupViewModel
-import org.koin.androidx.viewmodel.dsl.viewModel
+import moe.tlaster.koin.viewModel
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.RouteBuilder
import org.koin.dsl.bind
import org.koin.dsl.module
import org.koin.mp.KoinPlatformTools
object SettingSetup : ModuleSetup {
- override fun NavGraphBuilder.route(navController: NavController) {
+ override fun RouteBuilder.route(navController: NavController) {
generatedRoute(navController)
navigation(
startDestination = SettingRoute.BackupData.BackupLocal.Backup,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/route/BackupRoute.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/route/BackupRoute.kt
index d4cb2427..24827e6a 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/route/BackupRoute.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/route/BackupRoute.kt
@@ -38,6 +38,7 @@ import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -49,8 +50,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navigate
+import com.dimension.maskbook.common.ext.navigateWithPopSelf
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
@@ -81,8 +82,9 @@ import com.dimension.maskbook.setting.viewmodel.BackupMergeConfirmViewModel
import com.dimension.maskbook.setting.viewmodel.EmailBackupViewModel
import com.dimension.maskbook.setting.viewmodel.PhoneBackupViewModel
import kotlinx.coroutines.launch
-import org.koin.androidx.compose.get
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.get
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -172,13 +174,7 @@ fun BackupDataBackupCould(
navController.popBackStack()
},
onConfirm = {
- navController.navigate(SettingRoute.BackupData.BackupData_BackupCloud_Execute(it, type, value, code)) {
- navController.currentBackStackEntry?.let { backStackEntry ->
- popUpTo(backStackEntry.destination.id) {
- inclusive = true
- }
- }
- }
+ navController.navigateWithPopSelf(SettingRoute.BackupData.BackupData_BackupCloud_Execute(it, type, value, code))
}
)
}
@@ -206,21 +202,9 @@ fun BackupDataBackupCouldExecute(
withWallet = withWallet,
)
if (result) {
- navController.navigate(SettingRoute.BackupData.BackupData_Cloud_Success) {
- navController.currentBackStackEntry?.let { backStackEntry ->
- popUpTo(backStackEntry.destination.id) {
- inclusive = true
- }
- }
- }
+ navController.navigateWithPopSelf(SettingRoute.BackupData.BackupData_Cloud_Success)
} else {
- navController.navigate(SettingRoute.BackupData.BackupData_Cloud_Failed) {
- navController.currentBackStackEntry?.let { backStackEntry ->
- popUpTo(backStackEntry.destination.id) {
- inclusive = true
- }
- }
- }
+ navController.navigateWithPopSelf(SettingRoute.BackupData.BackupData_Cloud_Failed)
}
}
@@ -356,9 +340,9 @@ fun BackupDataBackupMergeConfirm(
parametersOf(onDone)
}
- val passwordValid by viewModel.passwordValid.observeAsState(initial = false)
- val loading by viewModel.loading.observeAsState(initial = false)
- val password by viewModel.backupPassword.observeAsState(initial = "")
+ val passwordValid by viewModel.passwordValid.collectAsState(initial = false)
+ val loading by viewModel.loading.collectAsState(initial = false)
+ val password by viewModel.backupPassword.collectAsState(initial = "")
MaskModal(
title = {
@@ -497,15 +481,15 @@ fun BackupSelectionEmail(
val scope = rememberCoroutineScope()
val repository = get()
- val persona by repository.currentPersona.observeAsState(initial = null)
+ val persona by repository.currentPersona.collectAsState(initial = null)
val phone = persona?.phone
val viewModel = getViewModel()
- val code by viewModel.code.observeAsState()
- val valid by viewModel.codeValid.observeAsState()
- val loading by viewModel.loading.observeAsState()
- val canSend by viewModel.canSend.observeAsState()
- val countDown by viewModel.countdown.observeAsState()
+ val code by viewModel.code.collectAsState()
+ val valid by viewModel.codeValid.collectAsState()
+ val loading by viewModel.loading.collectAsState()
+ val canSend by viewModel.canSend.collectAsState()
+ val countDown by viewModel.countdown.collectAsState()
LaunchedEffect(Unit) {
viewModel.startCountDown()
@@ -595,15 +579,15 @@ fun BackupSelectionPhone(
val scope = rememberCoroutineScope()
val repository = get()
- val persona by repository.currentPersona.observeAsState(initial = null)
+ val persona by repository.currentPersona.collectAsState(initial = null)
val email = persona?.email
val viewModel = getViewModel()
- val code by viewModel.code.observeAsState()
- val canSend by viewModel.canSend.observeAsState()
- val valid by viewModel.codeValid.observeAsState()
- val countDown by viewModel.countdown.observeAsState()
- val loading by viewModel.loading.observeAsState()
+ val code by viewModel.code.collectAsState()
+ val canSend by viewModel.canSend.collectAsState()
+ val valid by viewModel.codeValid.collectAsState()
+ val countDown by viewModel.countdown.collectAsState()
+ val loading by viewModel.loading.collectAsState()
LaunchedEffect(Unit) {
viewModel.sendCodeNow(phone)
@@ -692,7 +676,7 @@ fun BackupSelection(
navController: NavController,
) {
val repository = get()
- val persona by repository.currentPersona.observeAsState(initial = null)
+ val persona by repository.currentPersona.collectAsState(initial = null)
BackupSelectionModal(
onLocal = {
navController.navigate(SettingRoute.BackupData.BackupLocal.Backup)
@@ -721,7 +705,7 @@ fun BackupDataPassword(
navController: NavController,
) {
val repository = get()
- val currentPassword by repository.backupPassword.observeAsState(initial = "")
+ val currentPassword by repository.backupPassword.collectAsState(initial = "")
var password by remember { mutableStateOf("") }
BackupPasswordInputModal(
password = password,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/route/SettingsRoute.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/route/SettingsRoute.kt
index daf6561d..4e7cf30a 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/route/SettingsRoute.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/route/SettingsRoute.kt
@@ -25,13 +25,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.navigation.NavController
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navigate
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
@@ -55,7 +55,8 @@ import com.dimension.maskbook.setting.ui.scenes.PhoneInputModal
import com.dimension.maskbook.setting.viewmodel.EmailSetupViewModel
import com.dimension.maskbook.setting.viewmodel.PhoneSetupViewModel
import kotlinx.coroutines.launch
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
@NavGraphDestination(
route = SettingRoute.SetupPasswordDialog,
@@ -266,9 +267,9 @@ fun SettingsChangeEmailSetup(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val email by viewModel.value.observeAsState()
- val emailValid by viewModel.valueValid.observeAsState()
- val loading by viewModel.loading.observeAsState()
+ val email by viewModel.value.collectAsState()
+ val emailValid by viewModel.valueValid.collectAsState()
+ val loading by viewModel.loading.collectAsState()
EmailInputModal(
email = email,
onEmailChange = { viewModel.setValue(it) },
@@ -300,11 +301,11 @@ fun SettingsChangeEmailSetupCode(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val code by viewModel.code.observeAsState()
- val valid by viewModel.codeValid.observeAsState()
- val loading by viewModel.loading.observeAsState()
- val canSend by viewModel.canSend.observeAsState()
- val countDown by viewModel.countdown.observeAsState()
+ val code by viewModel.code.collectAsState()
+ val valid by viewModel.codeValid.collectAsState()
+ val loading by viewModel.loading.collectAsState()
+ val canSend by viewModel.canSend.collectAsState()
+ val countDown by viewModel.countdown.collectAsState()
LaunchedEffect(Unit) {
viewModel.sendCodeNow(email)
@@ -385,11 +386,11 @@ fun SettingsChangeEmailChangeCode(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val code by viewModel.code.observeAsState()
- val valid by viewModel.codeValid.observeAsState()
- val loading by viewModel.loading.observeAsState()
- val canSend by viewModel.canSend.observeAsState()
- val countDown by viewModel.countdown.observeAsState()
+ val code by viewModel.code.collectAsState()
+ val valid by viewModel.codeValid.collectAsState()
+ val loading by viewModel.loading.collectAsState()
+ val canSend by viewModel.canSend.collectAsState()
+ val countDown by viewModel.countdown.collectAsState()
EmailCodeInputModal(
email = email,
@@ -428,9 +429,9 @@ fun SettingsChangeEmailChangeNew(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val email by viewModel.value.observeAsState()
- val emailValid by viewModel.valueValid.observeAsState()
- val loading by viewModel.loading.observeAsState()
+ val email by viewModel.value.collectAsState()
+ val emailValid by viewModel.valueValid.collectAsState()
+ val loading by viewModel.loading.collectAsState()
EmailInputModal(
email = email,
onEmailChange = { viewModel.setValue(it) },
@@ -462,11 +463,11 @@ fun SettingsChangeEmailChangeNewCode(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val code by viewModel.code.observeAsState()
- val valid by viewModel.codeValid.observeAsState()
- val loading by viewModel.loading.observeAsState()
- val canSend by viewModel.canSend.observeAsState()
- val countDown by viewModel.countdown.observeAsState()
+ val code by viewModel.code.collectAsState()
+ val valid by viewModel.codeValid.collectAsState()
+ val loading by viewModel.loading.collectAsState()
+ val canSend by viewModel.canSend.collectAsState()
+ val countDown by viewModel.countdown.collectAsState()
LaunchedEffect(Unit) {
viewModel.sendCodeNow(email)
@@ -545,10 +546,10 @@ fun SettingsChangePhoneSetup(
) {
val viewModel = getViewModel()
- val regionCode by viewModel.regionCode.observeAsState()
- val phone by viewModel.value.observeAsState()
- val valid by viewModel.valueValid.observeAsState()
- val loading by viewModel.loading.observeAsState()
+ val regionCode by viewModel.regionCode.collectAsState()
+ val phone by viewModel.value.collectAsState()
+ val valid by viewModel.valueValid.collectAsState()
+ val loading by viewModel.loading.collectAsState()
PhoneInputModal(
regionCode = regionCode,
onRegionCodeChange = { viewModel.setRegionCode(it) },
@@ -580,11 +581,11 @@ fun SettingsChangePhoneSetupCode(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val code by viewModel.code.observeAsState()
- val canSend by viewModel.canSend.observeAsState()
- val valid by viewModel.codeValid.observeAsState()
- val countDown by viewModel.countdown.observeAsState()
- val loading by viewModel.loading.observeAsState()
+ val code by viewModel.code.collectAsState()
+ val canSend by viewModel.canSend.collectAsState()
+ val valid by viewModel.codeValid.collectAsState()
+ val countDown by viewModel.countdown.collectAsState()
+ val loading by viewModel.loading.collectAsState()
LaunchedEffect(Unit) {
viewModel.sendCodeNow(phone)
@@ -661,11 +662,11 @@ fun SettingsChangePhoneChangeCode(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val code by viewModel.code.observeAsState()
- val canSend by viewModel.canSend.observeAsState()
- val valid by viewModel.codeValid.observeAsState()
- val countDown by viewModel.countdown.observeAsState()
- val loading by viewModel.loading.observeAsState()
+ val code by viewModel.code.collectAsState()
+ val canSend by viewModel.canSend.collectAsState()
+ val valid by viewModel.codeValid.collectAsState()
+ val countDown by viewModel.countdown.collectAsState()
+ val loading by viewModel.loading.collectAsState()
PhoneCodeInputModal(
phone = phone,
@@ -704,10 +705,10 @@ fun SettingsChangePhoneChangeNew(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val regionCode by viewModel.regionCode.observeAsState()
- val phone by viewModel.value.observeAsState()
- val valid by viewModel.valueValid.observeAsState()
- val loading by viewModel.loading.observeAsState()
+ val regionCode by viewModel.regionCode.collectAsState()
+ val phone by viewModel.value.collectAsState()
+ val valid by viewModel.valueValid.collectAsState()
+ val loading by viewModel.loading.collectAsState()
PhoneInputModal(
regionCode = regionCode,
onRegionCodeChange = { viewModel.setRegionCode(it) },
@@ -744,11 +745,11 @@ fun SettingsChangePhoneChangeCodeNew(
val scope = rememberCoroutineScope()
val viewModel = getViewModel()
- val code by viewModel.code.observeAsState()
- val canSend by viewModel.canSend.observeAsState()
- val valid by viewModel.codeValid.observeAsState()
- val countDown by viewModel.countdown.observeAsState()
- val loading by viewModel.loading.observeAsState()
+ val code by viewModel.code.collectAsState()
+ val canSend by viewModel.canSend.collectAsState()
+ val valid by viewModel.codeValid.collectAsState()
+ val countDown by viewModel.countdown.collectAsState()
+ val loading by viewModel.loading.collectAsState()
LaunchedEffect(Unit) {
viewModel.sendCodeNow(phone)
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/AppearanceSettings.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/AppearanceSettings.kt
index 98ab1b5e..5c94d66c 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/AppearanceSettings.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/AppearanceSettings.kt
@@ -23,15 +23,15 @@ package com.dimension.maskbook.setting.ui.scenes
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.MaskModal
import com.dimension.maskbook.common.ui.widget.MaskSelection
import com.dimension.maskbook.localization.R
import com.dimension.maskbook.setting.export.model.Appearance
import com.dimension.maskbook.setting.viewmodel.AppearanceSettingsViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
val appearanceMap = mapOf(
Appearance.default to R.string.scene_setting_detail_automatic,
@@ -44,7 +44,7 @@ fun AppearanceSettings(
onBack: () -> Unit,
) {
val viewModel: AppearanceSettingsViewModel = getViewModel()
- val appearance by viewModel.appearance.observeAsState(initial = Appearance.default)
+ val appearance by viewModel.appearance.collectAsState(initial = Appearance.default)
MaskModal {
Column {
appearanceMap.forEach {
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/ChangeBackUpPasswordModal.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/ChangeBackUpPasswordModal.kt
index 1a67b2ea..e8dd4899 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/ChangeBackUpPasswordModal.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/ChangeBackUpPasswordModal.kt
@@ -31,18 +31,18 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.MaskModal
import com.dimension.maskbook.common.ui.widget.MaskPasswordInputField
import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.localization.R
import com.dimension.maskbook.setting.viewmodel.BackupPasswordSettingsViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@OptIn(ExperimentalAnimationApi::class)
@Composable
@@ -51,13 +51,13 @@ fun ChangeBackUpPasswordModal(
) {
val viewModel: BackupPasswordSettingsViewModel = getViewModel()
- val currentPassword by viewModel.currentPassword.observeAsState("")
- val isNext by viewModel.isNext.observeAsState(false)
- val password by viewModel.password.observeAsState(initial = "")
- val newPassword by viewModel.newPassword.observeAsState(initial = "")
- val newPasswordConfirm by viewModel.newPasswordConfirm.observeAsState(initial = "")
- val confirmPassword by viewModel.confirmPassword.observeAsState(false)
- val confirmNewPassword by viewModel.confirmNewPassword.observeAsState(false)
+ val currentPassword by viewModel.currentPassword.collectAsState("")
+ val isNext by viewModel.isNext.collectAsState(false)
+ val password by viewModel.password.collectAsState(initial = "")
+ val newPassword by viewModel.newPassword.collectAsState(initial = "")
+ val newPasswordConfirm by viewModel.newPasswordConfirm.collectAsState(initial = "")
+ val confirmPassword by viewModel.confirmPassword.collectAsState(false)
+ val confirmNewPassword by viewModel.confirmNewPassword.collectAsState(false)
MaskModal(
title = {
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/ChangePaymentPasswordModal.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/ChangePaymentPasswordModal.kt
index 87547675..7ebeee62 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/ChangePaymentPasswordModal.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/ChangePaymentPasswordModal.kt
@@ -31,18 +31,18 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.MaskModal
import com.dimension.maskbook.common.ui.widget.MaskPasswordInputField
import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.localization.R
import com.dimension.maskbook.setting.viewmodel.PaymentPasswordSettingsViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@OptIn(ExperimentalAnimationApi::class)
@Composable
@@ -51,12 +51,12 @@ fun ChangePaymentPasswordModal(
) {
val viewModel: PaymentPasswordSettingsViewModel = getViewModel()
- val isNext by viewModel.isNext.observeAsState(false)
- val password by viewModel.password.observeAsState(initial = "")
- val newPassword by viewModel.newPassword.observeAsState(initial = "")
- val newPasswordConfirm by viewModel.newPasswordConfirm.observeAsState(initial = "")
- val confirmPassword by viewModel.confirmPassword.observeAsState(false)
- val confirmNewPassword by viewModel.confirmNewPassword.observeAsState(false)
+ val isNext by viewModel.isNext.collectAsState(false)
+ val password by viewModel.password.collectAsState(initial = "")
+ val newPassword by viewModel.newPassword.collectAsState(initial = "")
+ val newPasswordConfirm by viewModel.newPasswordConfirm.collectAsState(initial = "")
+ val confirmPassword by viewModel.confirmPassword.collectAsState(false)
+ val confirmNewPassword by viewModel.confirmNewPassword.collectAsState(false)
MaskModal(
title = {
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/DataSourceSettings.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/DataSourceSettings.kt
index 8d65dd4b..6218562c 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/DataSourceSettings.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/DataSourceSettings.kt
@@ -23,13 +23,13 @@ package com.dimension.maskbook.setting.ui.scenes
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.MaskModal
import com.dimension.maskbook.common.ui.widget.MaskSelection
import com.dimension.maskbook.setting.export.model.DataProvider
import com.dimension.maskbook.setting.viewmodel.DataSourceSettingsViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
val dataProviderMap = mapOf(
DataProvider.COIN_GECKO to "CoinGecko",
@@ -42,7 +42,7 @@ fun DataSourceSettings(
onBack: () -> Unit,
) {
val viewModel: DataSourceSettingsViewModel = getViewModel()
- val dataProvider by viewModel.dataProvider.observeAsState(initial = DataProvider.COIN_GECKO)
+ val dataProvider by viewModel.dataProvider.collectAsState(initial = DataProvider.COIN_GECKO)
MaskModal {
Column {
dataProviderMap.forEach {
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/LanguageSettings.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/LanguageSettings.kt
index f9b49c07..c59d6ba4 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/LanguageSettings.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/LanguageSettings.kt
@@ -23,13 +23,13 @@ package com.dimension.maskbook.setting.ui.scenes
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.MaskModal
import com.dimension.maskbook.common.ui.widget.MaskSelection
import com.dimension.maskbook.setting.export.model.Language
import com.dimension.maskbook.setting.viewmodel.LanguageSettingsViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
val languageMap = Language.values().map {
it to it.value
@@ -40,7 +40,7 @@ fun LanguageSettings(
onBack: () -> Unit,
) {
val viewModel: LanguageSettingsViewModel = getViewModel()
- val language by viewModel.language.observeAsState(initial = Language.auto)
+ val language by viewModel.language.collectAsState(initial = Language.auto)
MaskModal {
Column {
languageMap.forEach {
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/SettingsScene.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/SettingsScene.kt
index d87e45c4..32cfdbb8 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/SettingsScene.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/SettingsScene.kt
@@ -44,6 +44,7 @@ import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowForwardIos
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -52,8 +53,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.ui.widget.IosSwitch
import com.dimension.maskbook.common.ui.widget.MaskCard
@@ -70,8 +70,9 @@ import com.dimension.maskbook.setting.export.model.DataProvider
import com.dimension.maskbook.setting.export.model.Language
import com.dimension.maskbook.setting.repository.ISettingsRepository
import com.dimension.maskbook.setting.route.SettingRoute
-import org.koin.androidx.compose.get
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.get
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
@OptIn(ExperimentalMaterialApi::class)
@Composable
@@ -80,14 +81,14 @@ fun SettingsScene(
onBack: () -> Unit,
) {
val repository = get()
- val language by repository.language.observeAsState(initial = Language.auto)
- val appearance by repository.appearance.observeAsState(initial = Appearance.default)
- val dataProvider by repository.dataProvider.observeAsState(initial = DataProvider.UNISWAP_INFO)
- val backupPassword by repository.backupPassword.observeAsState(initial = "")
- val paymentPassword by repository.paymentPassword.observeAsState(initial = "")
- val biometricEnabled by repository.biometricEnabled.observeAsState(initial = false)
+ val language by repository.language.collectAsState(initial = Language.auto)
+ val appearance by repository.appearance.collectAsState(initial = Appearance.default)
+ val dataProvider by repository.dataProvider.collectAsState(initial = DataProvider.UNISWAP_INFO)
+ val backupPassword by repository.backupPassword.collectAsState(initial = "")
+ val paymentPassword by repository.paymentPassword.collectAsState(initial = "")
+ val biometricEnabled by repository.biometricEnabled.collectAsState(initial = false)
val personaRepository = get()
- val persona by personaRepository.currentPersona.observeAsState(initial = null)
+ val persona by personaRepository.currentPersona.collectAsState(initial = null)
val biometricEnableViewModel = getViewModel()
val context = LocalContext.current
MaskScaffold(
@@ -192,7 +193,7 @@ fun SettingsScene(
title = stringResource(R.string.scene_setting_backup_recovery_restore_data),
icon = R.drawable.ic_settings_restore_data,
onClick = {
- navController.navigate(Uri.parse(Deeplinks.Persona.Recovery))
+ navController.navigateUri(Uri.parse(Deeplinks.Persona.Recovery))
}
)
SettingsDivider()
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/backup/BackupCloudScene.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/backup/BackupCloudScene.kt
index 06393953..6c1cf2e4 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/backup/BackupCloudScene.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/backup/BackupCloudScene.kt
@@ -33,13 +33,13 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Checkbox
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ui.widget.BackMetaDisplay
import com.dimension.maskbook.common.ui.widget.MaskPasswordInputField
import com.dimension.maskbook.common.ui.widget.MaskScaffold
@@ -52,7 +52,7 @@ import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.common.ui.widget.button.clickable
import com.dimension.maskbook.localization.R
import com.dimension.maskbook.setting.viewmodel.BackupCloudViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@Composable
fun BackupCloudScene(
@@ -60,12 +60,12 @@ fun BackupCloudScene(
onConfirm: (withWallet: Boolean) -> Unit,
) {
val viewModel = getViewModel()
- val meta by viewModel.meta.observeAsState(initial = null)
- val withWallet by viewModel.withLocalWallet.observeAsState(initial = false)
- val backupPassword by viewModel.backupPassword.observeAsState(initial = "")
- val backupPasswordValid by viewModel.backupPasswordValid.observeAsState(initial = false)
- val paymentPassword by viewModel.paymentPassword.observeAsState(initial = "")
- val paymentPasswordValid by viewModel.paymentPasswordValid.observeAsState(initial = false)
+ val meta by viewModel.meta.collectAsState(initial = null)
+ val withWallet by viewModel.withLocalWallet.collectAsState(initial = false)
+ val backupPassword by viewModel.backupPassword.collectAsState(initial = "")
+ val backupPasswordValid by viewModel.backupPasswordValid.collectAsState(initial = false)
+ val paymentPassword by viewModel.paymentPassword.collectAsState(initial = "")
+ val paymentPasswordValid by viewModel.paymentPasswordValid.collectAsState(initial = false)
MaskScene {
MaskScaffold(
topBar = {
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/backup/BackupLocalScene.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/backup/BackupLocalScene.kt
index 7b2fd228..0898974f 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/backup/BackupLocalScene.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/scenes/backup/BackupLocalScene.kt
@@ -46,9 +46,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.ext.getNestedNavigationViewModel
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navigate
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
import com.dimension.maskbook.common.routeProcessor.annotations.Back
@@ -68,6 +67,7 @@ import com.dimension.maskbook.setting.route.SettingRoute
import com.dimension.maskbook.setting.viewmodel.BackupLocalViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import moe.tlaster.precompose.navigation.NavController
private const val GeneratedRouteName = "backupLocalRoute"
@@ -145,12 +145,12 @@ fun BackupLocalScene(
navController: NavController,
) {
val viewModel: BackupLocalViewModel = navController.getNestedNavigationViewModel(SettingRoute.BackupData.BackupLocal.Route)
- val meta by viewModel.meta.observeAsState(initial = null)
- val password by viewModel.password.observeAsState(initial = "")
- val backupPasswordValid by viewModel.backupPasswordValid.observeAsState(initial = false)
- val withWallet by viewModel.withWallet.observeAsState(initial = false)
- val paymentPassword by viewModel.paymentPassword.observeAsState(initial = "")
- val paymentPasswordValid by viewModel.paymentPasswordValid.observeAsState(initial = false)
+ val meta by viewModel.meta.collectAsState(initial = null)
+ val password by viewModel.password.collectAsState(initial = "")
+ val backupPasswordValid by viewModel.backupPasswordValid.collectAsState(initial = false)
+ val withWallet by viewModel.withWallet.collectAsState(initial = false)
+ val paymentPassword by viewModel.paymentPassword.collectAsState(initial = "")
+ val paymentPasswordValid by viewModel.paymentPasswordValid.collectAsState(initial = false)
MaskScene {
MaskScaffold(
topBar = {
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/tab/SettingsTabScreen.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/tab/SettingsTabScreen.kt
index 205f6e28..3c5ac956 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/tab/SettingsTabScreen.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/ui/tab/SettingsTabScreen.kt
@@ -21,11 +21,11 @@
package com.dimension.maskbook.setting.ui.tab
import androidx.compose.runtime.Composable
-import androidx.navigation.NavController
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.ui.tab.TabScreen
import com.dimension.maskbook.setting.R
import com.dimension.maskbook.setting.ui.scenes.SettingsScene
+import moe.tlaster.precompose.navigation.NavController
class SettingsTabScreen : TabScreen {
override val route = CommonRoute.Main.Tabs.Setting
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/AppearanceSettingsViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/AppearanceSettingsViewModel.kt
index 67a2bd78..559f0332 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/AppearanceSettingsViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/AppearanceSettingsViewModel.kt
@@ -20,11 +20,11 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.export.model.Appearance
import com.dimension.maskbook.setting.repository.ISettingsRepository
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class AppearanceSettingsViewModel(
private val repository: ISettingsRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupCloudExecuteViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupCloudExecuteViewModel.kt
index a292ad32..134f8552 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupCloudExecuteViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupCloudExecuteViewModel.kt
@@ -20,12 +20,12 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.ViewModel
import com.dimension.maskbook.persona.export.PersonaServices
import com.dimension.maskbook.setting.repository.BackupRepository
import com.dimension.maskbook.setting.repository.ISettingsRepository
import com.dimension.maskbook.setting.services.model.AccountType
import kotlinx.coroutines.flow.firstOrNull
+import moe.tlaster.precompose.viewmodel.ViewModel
class BackupCloudExecuteViewModel(
private val settingsRepository: ISettingsRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupCloudViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupCloudViewModel.kt
index f9c1f0a7..520f2bf0 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupCloudViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupCloudViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.repository.ISettingsRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class BackupCloudViewModel(
private val settingsRepository: ISettingsRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupLocalViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupLocalViewModel.kt
index 43d2c1ec..8b6d32dc 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupLocalViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupLocalViewModel.kt
@@ -21,8 +21,6 @@
package com.dimension.maskbook.setting.viewmodel
import android.net.Uri
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.LocalBackupAccount
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.repository.BackupRepository
@@ -32,6 +30,8 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class BackupLocalViewModel(
private val repository: ISettingsRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupMergeConfirmViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupMergeConfirmViewModel.kt
index 9f871b16..6763cabb 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupMergeConfirmViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupMergeConfirmViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.repository.BackupRepository
@@ -29,6 +27,8 @@ import com.dimension.maskbook.setting.repository.ISettingsRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import java.io.File
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class BackupMergeConfirmViewModel(
private val backupRepository: BackupRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupPasswordSettingsViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupPasswordSettingsViewModel.kt
index be23a0b2..29b0784a 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupPasswordSettingsViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/BackupPasswordSettingsViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.repository.ISettingsRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class BackupPasswordSettingsViewModel(
private val repository: ISettingsRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/DataSourceSettingsViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/DataSourceSettingsViewModel.kt
index 68ef5041..ff781989 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/DataSourceSettingsViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/DataSourceSettingsViewModel.kt
@@ -20,11 +20,11 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.export.model.DataProvider
import com.dimension.maskbook.setting.repository.ISettingsRepository
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class DataSourceSettingsViewModel(
private val repository: ISettingsRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/EmailBackupViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/EmailBackupViewModel.kt
index ae242d32..956c7f7e 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/EmailBackupViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/EmailBackupViewModel.kt
@@ -20,7 +20,6 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.defaultRegionCode
@@ -31,6 +30,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.viewModelScope
import retrofit2.HttpException
class EmailBackupViewModel(
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/EmailSetupViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/EmailSetupViewModel.kt
index 76c47136..62a6b09b 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/EmailSetupViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/EmailSetupViewModel.kt
@@ -20,7 +20,6 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.defaultRegionCode
@@ -31,6 +30,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.viewModelScope
class EmailSetupViewModel(
private val settingsRepository: ISettingsRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/LanguageSettingsViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/LanguageSettingsViewModel.kt
index d787629a..aab1b534 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/LanguageSettingsViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/LanguageSettingsViewModel.kt
@@ -20,11 +20,11 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.export.model.Language
import com.dimension.maskbook.setting.repository.ISettingsRepository
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class LanguageSettingsViewModel(
private val repository: ISettingsRepository
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/PaymentPasswordSettingsViewModel.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/PaymentPasswordSettingsViewModel.kt
index f7820b1f..35c32992 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/PaymentPasswordSettingsViewModel.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/PaymentPasswordSettingsViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.setting.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.repository.ISettingsRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class PaymentPasswordSettingsViewModel(
private val repository: ISettingsRepository,
diff --git a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/base/RemoteBackupRecoveryViewModelBase.kt b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/base/RemoteBackupRecoveryViewModelBase.kt
index 1dc1af54..3de8815e 100644
--- a/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/base/RemoteBackupRecoveryViewModelBase.kt
+++ b/setting/src/androidMain/kotlin/com/dimension/maskbook/setting/viewmodel/base/RemoteBackupRecoveryViewModelBase.kt
@@ -20,14 +20,14 @@
*/
package com.dimension.maskbook.setting.viewmodel.base
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.defaultCountDownTime
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
import kotlin.time.Duration.Companion.seconds
abstract class RemoteBackupRecoveryViewModelBase : ViewModel() {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 610a851d..f6dc9d55 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -25,8 +25,6 @@ include(
":common",
":common:gecko",
":common:gecko:sample",
- ":common:okhttp",
- ":common:retrofit",
":common:routeProcessor",
":common:routeProcessor:annotations",
":common:bigDecimal",
diff --git a/wallet/export/build.gradle.kts b/wallet/export/build.gradle.kts
index 43a10bc9..0f3ce071 100644
--- a/wallet/export/build.gradle.kts
+++ b/wallet/export/build.gradle.kts
@@ -8,9 +8,9 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- implementation(projects.common.bigDecimal)
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
- implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
+ compileOnly(projects.common.bigDecimal)
+ compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}")
+ compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}")
}
}
val commonTest by getting {
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/WalletSetup.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/WalletSetup.kt
index af3d64e3..70996c93 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/WalletSetup.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/WalletSetup.kt
@@ -21,11 +21,9 @@
package com.dimension.maskbook.wallet
import android.content.Context
-import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
import androidx.room.Room
import com.dimension.maskbook.common.ModuleSetup
+import com.dimension.maskbook.common.route.navigation
import com.dimension.maskbook.common.ui.tab.TabScreen
import com.dimension.maskbook.wallet.data.JSMethod
import com.dimension.maskbook.wallet.db.AppDatabase
@@ -108,12 +106,13 @@ import com.dimension.maskbook.wallet.walletconnect.WalletConnectClientManager
import com.dimension.maskbook.wallet.walletconnect.WalletConnectServerManager
import com.dimension.maskbook.wallet.walletconnect.v1.client.WalletConnectClientManagerV1
import com.dimension.maskbook.wallet.walletconnect.v1.server.WalletConnectServerManagerV1
-import com.google.accompanist.navigation.animation.navigation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.launch
-import org.koin.androidx.viewmodel.dsl.viewModel
+import moe.tlaster.koin.viewModel
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.RouteBuilder
import org.koin.core.module.Module
import org.koin.dsl.bind
import org.koin.dsl.module
@@ -122,8 +121,7 @@ import com.dimension.maskbook.wallet.export.WalletServices as ExportWalletServic
object WalletSetup : ModuleSetup {
- @OptIn(ExperimentalAnimationApi::class)
- override fun NavGraphBuilder.route(navController: NavController) {
+ override fun RouteBuilder.route(navController: NavController) {
generatedRoute(navController)
navigation(
startDestination = WalletRoute.Transfer.SearchAddress.path,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/repository/WalletRepository.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/repository/WalletRepository.kt
index 51652e97..b283bce5 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/repository/WalletRepository.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/repository/WalletRepository.kt
@@ -842,7 +842,11 @@ internal class WalletRepository(
override suspend fun createWalletBackup(): List {
return database.walletDao().getAll().map {
val privateKey = WalletKey.load(it.storedKey.data).firstOrNull()
- val mnemonic = privateKey?.exportMnemonic("")
+ val mnemonic = try {
+ privateKey?.exportMnemonic("")
+ } catch (ignored: Exception) {
+ null
+ }
// TODO: support other coin types
val privateKeyHex = privateKey?.exportPrivateKey(CoinType.Ethereum, "")
BackupWalletData(
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/route/WalletsRoute.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/route/WalletsRoute.kt
index c34c1ee9..3c1a6b43 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/route/WalletsRoute.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/route/WalletsRoute.kt
@@ -36,11 +36,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
-import androidx.navigation.NavBackStackEntry
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.ext.navigate
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.bigDecimal.BigDecimal
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ext.shareText
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
@@ -97,8 +96,10 @@ import com.dimension.maskbook.wallet.viewmodel.wallets.management.WalletRenameVi
import com.dimension.maskbook.wallet.viewmodel.wallets.management.WalletSwitchEditViewModel
import com.dimension.maskbook.wallet.viewmodel.wallets.management.WalletSwitchViewModel
import com.dimension.maskbook.wallet.viewmodel.wallets.management.WalletTransactionHistoryViewModel
-import org.koin.androidx.compose.get
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.get
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.BackStackEntry
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -115,9 +116,9 @@ fun CollectibleDetail(
val viewModel = getViewModel {
parametersOf(id)
}
- val data by viewModel.data.observeAsState(initial = null)
- val transactions by viewModel.transactions.observeAsState()
- val nativeToken by viewModel.walletNativeToken.observeAsState()
+ val data by viewModel.data.collectAsState(initial = null)
+ val transactions by viewModel.transactions.collectAsState()
+ val nativeToken by viewModel.walletNativeToken.collectAsState()
CollectibleDetailScene(
data = data,
onBack = onBack,
@@ -150,7 +151,7 @@ fun WalletQrcode(
@Path("name") name: String,
) {
val repository = get()
- val currentWallet by repository.currentWallet.observeAsState(initial = null)
+ val currentWallet by repository.currentWallet.collectAsState(initial = null)
val context = LocalContext.current
val clipboardManager = LocalClipboardManager.current
val inAppNotification = LocalInAppNotification.current
@@ -180,11 +181,11 @@ fun TokenDetail(
val viewModel = getViewModel {
parametersOf(id)
}
- val token by viewModel.tokenData.observeAsState()
- val transactions by viewModel.transactions.observeAsState()
- val walletTokenData by viewModel.walletTokenData.observeAsState()
- val dWebData by viewModel.dWebData.observeAsState()
- val nativeToken by viewModel.walletNativeToken.observeAsState()
+ val token by viewModel.tokenData.collectAsState()
+ val transactions by viewModel.transactions.collectAsState()
+ val walletTokenData by viewModel.walletTokenData.collectAsState()
+ val dWebData by viewModel.dWebData.collectAsState()
+ val nativeToken by viewModel.walletNativeToken.collectAsState()
TokenDetailScene(
onBack = onBack,
@@ -257,7 +258,7 @@ fun WalletNetworkSwitch(
) {
val target = remember(targetString) { ChainType.valueOf(targetString) }
val viewModel = getViewModel()
- val currentNetwork by viewModel.network.observeAsState(initial = ChainType.eth)
+ val currentNetwork by viewModel.network.collectAsState(initial = ChainType.eth)
WalletNetworkSwitchWarningDialog(
currentNetwork = currentNetwork.name,
connectingNetwork = target.name,
@@ -279,8 +280,8 @@ fun WalletNetworkSwitchWarningDialog(
@Back onBack: () -> Unit,
) {
val viewModel = getViewModel()
- val currentNetwork by viewModel.network.observeAsState(initial = ChainType.eth)
- val wallet by viewModel.currentWallet.observeAsState(initial = null)
+ val currentNetwork by viewModel.network.collectAsState(initial = ChainType.eth)
+ val wallet by viewModel.currentWallet.collectAsState(initial = null)
LaunchedEffect(wallet) {
wallet?.let { wallet ->
if (!wallet.fromWalletConnect || wallet.walletConnectChainType == currentNetwork || wallet.walletConnectChainType == null) {
@@ -314,9 +315,9 @@ fun SwitchWallet(
navController: NavController,
) {
val viewModel = getViewModel()
- val wallet by viewModel.currentWallet.observeAsState(initial = null)
- val wallets by viewModel.wallets.observeAsState(initial = emptyList())
- val chainType by viewModel.network.observeAsState(initial = ChainType.eth)
+ val wallet by viewModel.currentWallet.collectAsState(initial = null)
+ val wallets by viewModel.wallets.collectAsState(initial = emptyList())
+ val chainType by viewModel.network.collectAsState(initial = ChainType.eth)
WalletSwitchSceneModal(
selectedWallet = wallet,
wallets = wallets,
@@ -403,7 +404,7 @@ fun WalletBalancesMenu(
@Back onBack: () -> Unit,
) {
val viewModel = getViewModel()
- val currentWallet by viewModel.currentWallet.observeAsState(initial = null)
+ val currentWallet by viewModel.currentWallet.collectAsState(initial = null)
val wcViewModel = getViewModel()
WalletManagementModal(
walletData = currentWallet,
@@ -447,11 +448,11 @@ fun WalletManagementDeleteDialog(
parametersOf(id)
}
val biometricViewModel = getViewModel()
- val wallet by viewModel.wallet.observeAsState(initial = null)
- val biometricEnabled by biometricViewModel.biometricEnabled.observeAsState(initial = false)
+ val wallet by viewModel.wallet.collectAsState(initial = null)
+ val biometricEnabled by biometricViewModel.biometricEnabled.collectAsState(initial = false)
val context = LocalContext.current
- val password by viewModel.password.observeAsState(initial = "")
- val canConfirm by viewModel.canConfirm.observeAsState(initial = false)
+ val password by viewModel.password.collectAsState(initial = "")
+ val canConfirm by viewModel.canConfirm.collectAsState(initial = false)
WalletDeleteDialog(
walletData = wallet,
password = password,
@@ -486,8 +487,8 @@ fun WalletManagementBackup(
@Back onBack: () -> Unit,
) {
val viewModel = getViewModel()
- val keyStore by viewModel.keyStore.observeAsState(initial = "")
- val privateKey by viewModel.privateKey.observeAsState(initial = "")
+ val keyStore by viewModel.keyStore.collectAsState(initial = "")
+ val privateKey by viewModel.privateKey.collectAsState(initial = "")
BackupWalletScene(
keyStore = keyStore,
privateKey = privateKey,
@@ -505,7 +506,7 @@ fun WalletManagementTransactionHistory(
@Back onBack: () -> Unit,
) {
val viewModel = getViewModel()
- val transactions by viewModel.transactions.observeAsState()
+ val transactions by viewModel.transactions.collectAsState()
WalletTransactionHistoryScene(
onBack = onBack,
transactions = transactions,
@@ -532,13 +533,13 @@ fun WalletManagementRename(
val viewModel = getViewModel {
parametersOf(walletId, walletName)
}
- val name by viewModel.name.observeAsState()
+ val name by viewModel.name.collectAsState()
WalletRenameModal(
name = name,
onNameChanged = { viewModel.setName(it) },
onDone = {
viewModel.confirm()
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Wallet)),
navOptions {
launchSingleTop = true
@@ -559,15 +560,15 @@ fun WalletManagementRename(
@Composable
fun WalletIntroHostLegal(
navController: NavController,
- navBackStackEntry: NavBackStackEntry,
+ navBackStackEntry: BackStackEntry,
@Back onBack: () -> Unit,
@Path("type") typeString: String,
) {
val type = remember(typeString) { CreateType.valueOf(typeString) }
val repo = get()
- val password by repo.paymentPassword.observeAsState(initial = null)
- val enableBiometric by repo.biometricEnabled.observeAsState(initial = false)
- val shouldShowLegalScene by repo.shouldShowLegalScene.observeAsState(initial = true)
+ val password by repo.paymentPassword.collectAsState(initial = null)
+ val enableBiometric by repo.biometricEnabled.collectAsState(initial = false)
+ val shouldShowLegalScene by repo.shouldShowLegalScene.collectAsState(initial = true)
val biometricEnableViewModel: BiometricEnableViewModel = getViewModel()
val context = LocalContext.current
val next: () -> Unit = {
@@ -581,7 +582,7 @@ fun WalletIntroHostLegal(
navController.navigate(
route,
navOptions {
- popUpTo(id = navBackStackEntry.destination.id) {
+ popUpTo(route = navBackStackEntry.route.route) {
inclusive = true
}
launchSingleTop = true
@@ -618,7 +619,7 @@ fun WalletIntroHostPassword(
@Path("type") typeString: String,
) {
val type = remember(typeString) { CreateType.valueOf(typeString) }
- val enableBiometric by get().biometricEnabled.observeAsState(initial = false)
+ val enableBiometric by get().biometricEnabled.collectAsState(initial = false)
val biometricEnableViewModel: BiometricEnableViewModel = getViewModel()
val context = LocalContext.current
SetUpPaymentPassword(
@@ -801,9 +802,9 @@ fun UnlockWalletDialog(
@Path("target") target: String,
) {
val viewModel = getViewModel()
- val biometricEnable by viewModel.biometricEnabled.observeAsState(initial = false)
- val password by viewModel.password.observeAsState(initial = "")
- val passwordValid by viewModel.passwordValid.observeAsState(initial = false)
+ val biometricEnable by viewModel.biometricEnabled.collectAsState(initial = false)
+ val password by viewModel.password.collectAsState(initial = "")
+ val passwordValid by viewModel.passwordValid.collectAsState(initial = false)
val context = LocalContext.current
UnlockWalletDialog(
onBack = onBack,
@@ -855,7 +856,7 @@ fun EmptyTokenDialogRoute(
tokenSymbol = tokenSymbol,
onCancel = onBack,
onBuy = {
- navController.navigate(deepLink = Uri.parse(Deeplinks.Labs.Transak))
+ navController.navigateUri(Uri.parse(Deeplinks.Labs.Transak))
}
)
}
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/CreateOrImportWalletScene.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/CreateOrImportWalletScene.kt
index 50df97d7..62a0ad95 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/CreateOrImportWalletScene.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/CreateOrImportWalletScene.kt
@@ -43,7 +43,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import com.dimension.maskbook.common.ui.widget.HorizontalScenePadding
import com.dimension.maskbook.common.ui.widget.MaskDialog
import com.dimension.maskbook.common.ui.widget.MaskInputField
@@ -57,6 +56,7 @@ import com.dimension.maskbook.common.ui.widget.button.MaskIconButton
import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.wallet.R
import com.dimension.maskbook.wallet.route.WalletRoute
+import moe.tlaster.precompose.navigation.NavController
@Composable
fun CreateOrImportWalletScene(
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/create/CreateWalletHost.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/create/CreateWalletHost.kt
index 5373e4cb..d69c2ffd 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/create/CreateWalletHost.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/create/CreateWalletHost.kt
@@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -34,10 +35,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
import com.dimension.maskbook.common.ext.getNestedNavigationViewModel
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
@@ -55,6 +55,7 @@ import com.dimension.maskbook.wallet.repository.WalletCreateOrImportResult
import com.dimension.maskbook.wallet.route.WalletRoute
import com.dimension.maskbook.wallet.ui.scenes.wallets.common.Dialog
import com.dimension.maskbook.wallet.viewmodel.wallets.create.CreateWalletRecoveryKeyViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
private const val GeneratedRouteName = "createWalletRoute"
@@ -75,7 +76,7 @@ fun PharseRoute(
.getNestedNavigationViewModel(WalletRoute.CreateWallet.Route) {
parametersOf(wallet)
}
- val words by viewModel.words.observeAsState(initial = emptyList())
+ val words by viewModel.words.collectAsState(initial = emptyList())
MnemonicPhraseScene(
words = words.map { it.word },
onRefreshWords = {
@@ -102,10 +103,10 @@ fun VerifyRoute(
.getNestedNavigationViewModel(WalletRoute.CreateWallet.Route) {
parametersOf(wallet)
}
- val result by viewModel.result.observeAsState(initial = null)
- val correct by viewModel.correct.observeAsState(initial = false)
- val selectedWords by viewModel.selectedWords.observeAsState(initial = emptyList())
- val wordsInRandomOrder by viewModel.wordsInRandomOrder.observeAsState(initial = emptyList())
+ val result by viewModel.result.collectAsState(initial = null)
+ val correct by viewModel.correct.collectAsState(initial = false)
+ val selectedWords by viewModel.selectedWords.collectAsState(initial = emptyList())
+ val wordsInRandomOrder by viewModel.wordsInRandomOrder.collectAsState(initial = emptyList())
var showDialog by remember {
mutableStateOf(false)
}
@@ -159,7 +160,7 @@ fun ConfirmRoute(
) {
val onDone = remember {
{
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Wallet)),
navOptions = navOptions {
launchSingleTop = true
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletDerivationPathScene.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletDerivationPathScene.kt
index b7cd7601..f627c0d5 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletDerivationPathScene.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletDerivationPathScene.kt
@@ -57,9 +57,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
@@ -86,7 +85,8 @@ import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.rememberPagerState
import kotlinx.coroutines.launch
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
typealias DerivationPathItem = ImportWalletDerivationPathViewModel.BalanceRow
@@ -109,8 +109,8 @@ fun ImportWalletDerivationPathScene(
val viewModel = getViewModel {
parametersOf(wallet, code)
}
- val path by viewModel.derivationPath.observeAsState(initial = "")
- val checked by viewModel.checked.observeAsState(initial = emptyList())
+ val path by viewModel.derivationPath.collectAsState(initial = "")
+ val checked by viewModel.checked.collectAsState(initial = emptyList())
var showDialog by remember { mutableStateOf(false) }
var result by remember { mutableStateOf(null) }
@@ -217,7 +217,7 @@ fun ImportWalletDerivationPathScene(
it.Dialog(onDismissRequest = {
showDialog = false
if (it.type == WalletCreateOrImportResult.Type.SUCCESS) {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Wallet)),
navOptions = navOptions {
launchSingleTop = true
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletKeystoreScene.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletKeystoreScene.kt
index 1afbbeaa..694126f6 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletKeystoreScene.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletKeystoreScene.kt
@@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -38,9 +39,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
@@ -61,7 +61,8 @@ import com.dimension.maskbook.wallet.repository.WalletCreateOrImportResult
import com.dimension.maskbook.wallet.route.WalletRoute
import com.dimension.maskbook.wallet.ui.scenes.wallets.common.Dialog
import com.dimension.maskbook.wallet.viewmodel.wallets.import.ImportWalletKeystoreViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -91,9 +92,9 @@ fun ImportWalletKeyStoreScene(
val viewModel = getViewModel {
parametersOf(wallet)
}
- val keystore by viewModel.keystore.observeAsState(initial = "")
- val password by viewModel.password.observeAsState(initial = "")
- val canConfirm by viewModel.canConfirm.observeAsState(initial = false)
+ val keystore by viewModel.keystore.collectAsState(initial = "")
+ val password by viewModel.password.collectAsState(initial = "")
+ val canConfirm by viewModel.canConfirm.collectAsState(initial = false)
var showDialog by remember {
mutableStateOf(false)
}
@@ -133,7 +134,7 @@ fun ImportWalletKeyStoreScene(
onClick = {
viewModel.confirm {
if (it.type == WalletCreateOrImportResult.Type.SUCCESS) {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Wallet)),
navOptions = navOptions {
launchSingleTop = true
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletMnemonicScene.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletMnemonicScene.kt
index 0362ba7c..045c85d3 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletMnemonicScene.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletMnemonicScene.kt
@@ -37,6 +37,7 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -44,8 +45,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
import com.dimension.maskbook.common.routeProcessor.annotations.Back
@@ -63,7 +62,8 @@ import com.dimension.maskbook.wallet.repository.WalletCreateOrImportResult
import com.dimension.maskbook.wallet.route.WalletRoute
import com.dimension.maskbook.wallet.ui.scenes.wallets.common.Dialog
import com.dimension.maskbook.wallet.viewmodel.wallets.import.ImportWalletMnemonicViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -94,9 +94,9 @@ fun ImportWalletMnemonicScene(
val viewModel = getViewModel {
parametersOf(walletAddress)
}
- val words by viewModel.words.observeAsState(initial = "")
- val canConfirm by viewModel.canConfirm.observeAsState(initial = false)
- val hintWords by viewModel.hintWords.observeAsState(initial = emptyList())
+ val words by viewModel.words.collectAsState(initial = "")
+ val canConfirm by viewModel.canConfirm.collectAsState(initial = false)
+ val hintWords by viewModel.hintWords.collectAsState(initial = emptyList())
var showDialog by remember {
mutableStateOf(false)
}
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletPrivateKeyScene.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletPrivateKeyScene.kt
index 00dbfee8..ecaf97fe 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletPrivateKeyScene.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/create/import/ImportWalletPrivateKeyScene.kt
@@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -37,9 +38,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import androidx.navigation.navOptions
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.ext.navigateUri
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.route.Deeplinks
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
@@ -59,7 +59,8 @@ import com.dimension.maskbook.wallet.repository.WalletCreateOrImportResult
import com.dimension.maskbook.wallet.route.WalletRoute
import com.dimension.maskbook.wallet.ui.scenes.wallets.common.Dialog
import com.dimension.maskbook.wallet.viewmodel.wallets.import.ImportWalletPrivateKeyViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
@NavGraphDestination(
@@ -89,8 +90,8 @@ fun ImportWalletPrivateKeyScene(
val viewModel = getViewModel {
parametersOf(wallet)
}
- val privateKey by viewModel.privateKey.observeAsState(initial = "")
- val canConfirm by viewModel.canConfirm.observeAsState(initial = false)
+ val privateKey by viewModel.privateKey.collectAsState(initial = "")
+ val canConfirm by viewModel.canConfirm.collectAsState(initial = false)
var showDialog by remember {
mutableStateOf(false)
}
@@ -119,7 +120,7 @@ fun ImportWalletPrivateKeyScene(
onClick = {
viewModel.confirm {
if (it.type == WalletCreateOrImportResult.Type.SUCCESS) {
- navController.navigate(
+ navController.navigateUri(
Uri.parse(Deeplinks.Main.Home(CommonRoute.Main.Tabs.Wallet)),
navOptions = navOptions {
launchSingleTop = true
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/WalletIntroHost.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/WalletIntroHost.kt
index 28f8563a..3d8a57da 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/WalletIntroHost.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/WalletIntroHost.kt
@@ -21,36 +21,34 @@
package com.dimension.maskbook.wallet.ui.scenes.wallets.intro
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.buildAnnotatedString
-import androidx.navigation.NavController
import androidx.paging.compose.collectAsLazyPagingItems
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.wallet.route.WalletRoute
import com.dimension.maskbook.wallet.route.transfer
import com.dimension.maskbook.wallet.ui.scenes.wallets.create.CreateType
import com.dimension.maskbook.wallet.ui.scenes.wallets.management.WalletBalancesScene
import com.dimension.maskbook.wallet.viewmodel.wallets.WalletBalancesViewModel
-import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
-@ExperimentalMaterialNavigationApi
@Composable
fun WalletIntroHost(navController: NavController) {
val clipboardManager = LocalClipboardManager.current
val viewModel = getViewModel()
val collectible = viewModel.collectible.collectAsLazyPagingItems()
- val dWebData by viewModel.dWebData.observeAsState()
- val sceneType by viewModel.sceneType.observeAsState()
- val wallet by viewModel.currentWallet.observeAsState()
- val wallets by viewModel.wallets.observeAsState()
- val displayChainType by viewModel.displayChainType.observeAsState()
- val showTokens by viewModel.showTokens.observeAsState()
- val showTokensLess by viewModel.showTokensLess.observeAsState()
- val showTokensLessAmount by viewModel.showTokensLessAmount.observeAsState()
- val nativeToken by viewModel.walletNativeToken.observeAsState()
+ val dWebData by viewModel.dWebData.collectAsState()
+ val sceneType by viewModel.sceneType.collectAsState()
+ val wallet by viewModel.currentWallet.collectAsState()
+ val wallets by viewModel.wallets.collectAsState()
+ val displayChainType by viewModel.displayChainType.collectAsState()
+ val showTokens by viewModel.showTokens.collectAsState()
+ val showTokensLess by viewModel.showTokensLess.collectAsState()
+ val showTokensLessAmount by viewModel.showTokensLessAmount.collectAsState()
+ val nativeToken by viewModel.walletNativeToken.collectAsState()
val currentWallet = wallet
val currentDWebData = dWebData
@@ -68,7 +66,7 @@ fun WalletIntroHost(navController: NavController) {
)
} else if (currentDWebData != null) {
val swipeRefreshState = rememberSwipeRefreshState(false)
- val refreshing by viewModel.refreshingWallet.observeAsState()
+ val refreshing by viewModel.refreshingWallet.collectAsState()
swipeRefreshState.isRefreshing = refreshing
WalletBalancesScene(
wallets = wallets,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/password/BiometricsEnableScene.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/password/BiometricsEnableScene.kt
index 1b2c85e4..c61a3d33 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/password/BiometricsEnableScene.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/password/BiometricsEnableScene.kt
@@ -46,7 +46,7 @@ import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.common.ui.widget.button.SecondaryButton
import com.dimension.maskbook.common.viewmodel.BiometricEnableViewModel
import com.dimension.maskbook.wallet.R
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@Composable
fun BiometricsEnableScene(
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/password/TouchIdEnableScene.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/password/TouchIdEnableScene.kt
index af5185eb..3842c70c 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/password/TouchIdEnableScene.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/intro/password/TouchIdEnableScene.kt
@@ -45,7 +45,7 @@ import com.dimension.maskbook.common.ui.widget.button.PrimaryButton
import com.dimension.maskbook.common.ui.widget.button.SecondaryButton
import com.dimension.maskbook.wallet.R
import com.dimension.maskbook.wallet.viewmodel.wallets.TouchIdEnableViewModel
-import org.koin.androidx.compose.get
+import moe.tlaster.koin.compose.get
@Composable
fun TouchIdEnableScene(
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/send/SendTokenConfirmModal.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/send/SendTokenConfirmModal.kt
index 2c109483..6a0b88f5 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/send/SendTokenConfirmModal.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/send/SendTokenConfirmModal.kt
@@ -21,22 +21,18 @@
package com.dimension.maskbook.wallet.ui.scenes.wallets.send
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
-import androidx.navigation.NavController
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import androidx.navigation.compose.dialog
-import androidx.navigation.compose.rememberNavController
import com.dimension.maskbook.common.ext.decodeJson
import com.dimension.maskbook.common.ext.fromHexString
import com.dimension.maskbook.common.ext.humanizeDollar
import com.dimension.maskbook.common.ext.humanizeToken
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.ext.sendEvent
import com.dimension.maskbook.common.model.ResultEvent
import com.dimension.maskbook.common.route.Deeplinks
+import com.dimension.maskbook.common.route.composable
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
import com.dimension.maskbook.common.route.navigationComposeBottomSheetPackage
import com.dimension.maskbook.common.routeProcessor.annotations.Back
@@ -52,7 +48,10 @@ import com.dimension.maskbook.wallet.ui.scenes.wallets.UnlockWalletDialog
import com.dimension.maskbook.wallet.viewmodel.wallets.UnlockWalletViewModel
import com.dimension.maskbook.wallet.viewmodel.wallets.send.GasFeeViewModel
import com.dimension.maskbook.wallet.viewmodel.wallets.send.Web3TransactionConfirmViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.NavHost
+import moe.tlaster.precompose.navigation.rememberNavController
import org.koin.core.parameter.parametersOf
import java.math.BigDecimal
@@ -78,29 +77,29 @@ fun SendTokenConfirmModal(
val viewModel = getViewModel {
parametersOf(data, request)
}
- val token by viewModel.tokenData.observeAsState(null)
- val address by viewModel.addressData.observeAsState(null)
- val amount by viewModel.amount.observeAsState(BigDecimal.ZERO)
+ val token by viewModel.tokenData.collectAsState(null)
+ val address by viewModel.addressData.collectAsState(null)
+ val amount by viewModel.amount.collectAsState(BigDecimal.ZERO)
address?.let { addressData ->
token?.let { tokenData ->
val gasFeeViewModel = getViewModel {
parametersOf(data.gasLimit?.fromHexString()?.toDouble() ?: 21000.0)
}
- val gasLimit by gasFeeViewModel.gasLimit.observeAsState()
- val maxPriorityFee by gasFeeViewModel.maxPriorityFeePerGas.observeAsState()
- val maxFee by gasFeeViewModel.maxFeePerGas.observeAsState()
- val arrives by gasFeeViewModel.arrives.observeAsState()
- val gasUsdTotal by gasFeeViewModel.gasUsdTotal.observeAsState()
- val gasTotal by gasFeeViewModel.gasTotal.observeAsState()
- val loadingState by gasFeeViewModel.loadingState.observeAsState()
- val gasFeeUnit by gasFeeViewModel.gasFeeUnit.observeAsState()
+ val gasLimit by gasFeeViewModel.gasLimit.collectAsState()
+ val maxPriorityFee by gasFeeViewModel.maxPriorityFeePerGas.collectAsState()
+ val maxFee by gasFeeViewModel.maxFeePerGas.collectAsState()
+ val arrives by gasFeeViewModel.arrives.collectAsState()
+ val gasUsdTotal by gasFeeViewModel.gasUsdTotal.collectAsState()
+ val gasTotal by gasFeeViewModel.gasTotal.collectAsState()
+ val loadingState by gasFeeViewModel.loadingState.collectAsState()
+ val gasFeeUnit by gasFeeViewModel.gasFeeUnit.collectAsState()
NavHost(
navController,
- startDestination = "SendConfirm"
+ initialRoute = "SendConfirm"
) {
composable("SendConfirm") {
- val sending by viewModel.loadingState.observeAsState()
+ val sending by viewModel.loadingState.collectAsState()
SendConfirmSheet(
addressData = addressData,
tokenData = WalletTokenData(
@@ -127,8 +126,8 @@ fun SendTokenConfirmModal(
)
}
composable("EditGasFee") {
- val mode by gasFeeViewModel.gasPriceEditMode.observeAsState(initial = GasPriceEditMode.MEDIUM)
- val loading by gasFeeViewModel.loadingState.observeAsState()
+ val mode by gasFeeViewModel.gasPriceEditMode.collectAsState(initial = GasPriceEditMode.MEDIUM)
+ val loading by gasFeeViewModel.loadingState.collectAsState()
EditGasPriceSheet(
price = gasUsdTotal.humanizeDollar(),
costFee = gasTotal.humanizeToken(),
@@ -170,9 +169,9 @@ fun SendTokenConfirmModal(
"UnlockWalletDialog",
) {
val unlockViewModel = getViewModel()
- val biometricEnable by unlockViewModel.biometricEnabled.observeAsState(initial = false)
- val password by unlockViewModel.password.observeAsState(initial = "")
- val passwordValid by unlockViewModel.passwordValid.observeAsState(initial = false)
+ val biometricEnable by unlockViewModel.biometricEnabled.collectAsState(initial = false)
+ val password by unlockViewModel.password.collectAsState(initial = "")
+ val passwordValid by unlockViewModel.passwordValid.collectAsState(initial = false)
val context = LocalContext.current
val onSuccess: () -> Unit = {
navController.popBackStack()
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/send/TranserHost.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/send/TranserHost.kt
index 9b65e1a8..2a598a26 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/send/TranserHost.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/send/TranserHost.kt
@@ -30,13 +30,11 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.buildAnnotatedString
-import androidx.navigation.NavController
import androidx.paging.compose.collectAsLazyPagingItems
import com.dimension.maskbook.common.bigDecimal.BigDecimal
import com.dimension.maskbook.common.ext.getNestedNavigationViewModel
import com.dimension.maskbook.common.ext.humanizeDollar
import com.dimension.maskbook.common.ext.humanizeToken
-import com.dimension.maskbook.common.ext.observeAsState
import com.dimension.maskbook.common.route.navigationComposeAnimComposable
import com.dimension.maskbook.common.route.navigationComposeAnimComposablePackage
import com.dimension.maskbook.common.route.navigationComposeBottomSheet
@@ -60,7 +58,8 @@ import com.dimension.maskbook.wallet.viewmodel.wallets.send.SearchAddressViewMod
import com.dimension.maskbook.wallet.viewmodel.wallets.send.SearchTradableViewModel
import com.dimension.maskbook.wallet.viewmodel.wallets.send.SendConfirmViewModel
import com.dimension.maskbook.wallet.viewmodel.wallets.send.TransferDetailViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
import org.koin.core.parameter.parametersOf
private const val GeneratedRouteName = "transferRoute"
@@ -80,12 +79,12 @@ fun SearchAddressRoute(
val searchAddressViewModel = navController
.getNestedNavigationViewModel(WalletRoute.Transfer.Route)
- val input by searchAddressViewModel.input.observeAsState()
- val contacts by searchAddressViewModel.contacts.observeAsState()
- val recent by searchAddressViewModel.recent.observeAsState()
- val ensData by searchAddressViewModel.ensData.observeAsState()
- val selectEnsData by searchAddressViewModel.selectEnsData.observeAsState()
- val canConfirm by searchAddressViewModel.canConfirm.observeAsState()
+ val input by searchAddressViewModel.input.collectAsState()
+ val contacts by searchAddressViewModel.contacts.collectAsState()
+ val recent by searchAddressViewModel.recent.collectAsState()
+ val ensData by searchAddressViewModel.ensData.collectAsState()
+ val selectEnsData by searchAddressViewModel.selectEnsData.collectAsState()
+ val canConfirm by searchAddressViewModel.canConfirm.collectAsState()
val clipboardManager = LocalClipboardManager.current
val inAppNotification = LocalInAppNotification.current
@@ -176,9 +175,9 @@ fun SendRoute(
parametersOf(tradableId)
}
- val arrives by gasFeeViewModel.arrives.observeAsState(initial = "")
- val gasUsdTotal by gasFeeViewModel.gasUsdTotal.observeAsState(initial = BigDecimal.ZERO)
- val gasTotal by gasFeeViewModel.gasTotal.observeAsState(initial = BigDecimal.ZERO)
+ val arrives by gasFeeViewModel.arrives.collectAsState(initial = "")
+ val gasUsdTotal by gasFeeViewModel.gasUsdTotal.collectAsState(initial = BigDecimal.ZERO)
+ val gasTotal by gasFeeViewModel.gasTotal.collectAsState(initial = BigDecimal.ZERO)
val selectTradable by transferDetailViewModel.selectedTradable.collectAsState(null)
@@ -186,14 +185,14 @@ fun SendRoute(
transferDetailViewModel.setAddress(address)
}
val biometricViewModel = getViewModel()
- val biometricEnabled by biometricViewModel.biometricEnabled.observeAsState()
- val addressData by transferDetailViewModel.addressData.observeAsState()
- val amount by transferDetailViewModel.amount.observeAsState()
- val password by transferDetailViewModel.password.observeAsState()
- val canConfirm by transferDetailViewModel.canConfirm.observeAsState()
- val balance by transferDetailViewModel.balance.observeAsState()
- val maxAmount by transferDetailViewModel.maxAmount.observeAsState()
- val isEnoughForGas by transferDetailViewModel.isEnoughForGas.observeAsState()
+ val biometricEnabled by biometricViewModel.biometricEnabled.collectAsState()
+ val addressData by transferDetailViewModel.addressData.collectAsState()
+ val amount by transferDetailViewModel.amount.collectAsState()
+ val password by transferDetailViewModel.password.collectAsState()
+ val canConfirm by transferDetailViewModel.canConfirm.collectAsState()
+ val balance by transferDetailViewModel.balance.collectAsState()
+ val maxAmount by transferDetailViewModel.maxAmount.collectAsState()
+ val isEnoughForGas by transferDetailViewModel.isEnoughForGas.collectAsState()
transferDetailViewModel.setGasTotal(gasTotal = gasTotal)
TransferDetailScene(
@@ -272,8 +271,8 @@ fun SearchTokenRoute(
}
val viewModel = getViewModel()
- val walletTokens by viewModel.walletTokens.observeAsState(emptyList())
- val query by viewModel.query.observeAsState()
+ val walletTokens by viewModel.walletTokens.collectAsState(emptyList())
+ val query by viewModel.query.collectAsState()
SearchTokenScene(
onBack = {
onBack.invoke()
@@ -310,7 +309,7 @@ fun SearchCollectiblesRoute(
val viewModel = getViewModel()
val walletCollectibleCollections =
viewModel.walletCollectibleCollections.collectAsLazyPagingItems()
- val query by viewModel.query.observeAsState()
+ val query by viewModel.query.collectAsState()
SearchCollectibleScene(
onBack = {
onBack.invoke()
@@ -342,15 +341,15 @@ fun EditGasFeeRoute(
parametersOf(21000.0)
}
- val gasLimit by gasFeeViewModel.gasLimit.observeAsState(initial = -1.0)
- val maxPriorityFee by gasFeeViewModel.maxPriorityFeePerGas.observeAsState(initial = -1.0)
- val maxFee by gasFeeViewModel.maxFeePerGas.observeAsState(initial = -1.0)
- val arrives by gasFeeViewModel.arrives.observeAsState(initial = "")
- val gasTotal by gasFeeViewModel.gasTotal.observeAsState(initial = BigDecimal.ZERO)
- val gasUsdTotal by gasFeeViewModel.gasUsdTotal.observeAsState(initial = BigDecimal.ZERO)
+ val gasLimit by gasFeeViewModel.gasLimit.collectAsState(initial = -1.0)
+ val maxPriorityFee by gasFeeViewModel.maxPriorityFeePerGas.collectAsState(initial = -1.0)
+ val maxFee by gasFeeViewModel.maxFeePerGas.collectAsState(initial = -1.0)
+ val arrives by gasFeeViewModel.arrives.collectAsState(initial = "")
+ val gasTotal by gasFeeViewModel.gasTotal.collectAsState(initial = BigDecimal.ZERO)
+ val gasUsdTotal by gasFeeViewModel.gasUsdTotal.collectAsState(initial = BigDecimal.ZERO)
val mode by gasFeeViewModel.gasPriceEditMode.collectAsState()
- val loading by gasFeeViewModel.loadingState.observeAsState()
- val gasFeeUnit by gasFeeViewModel.gasFeeUnit.observeAsState()
+ val loading by gasFeeViewModel.loadingState.collectAsState()
+ val gasFeeUnit by gasFeeViewModel.gasFeeUnit.collectAsState()
EditGasPriceSheet(
price = gasUsdTotal.humanizeDollar(),
costFee = gasTotal.humanizeToken(),
@@ -401,7 +400,7 @@ fun AddContactSheetRoute(
@Back onBack: () -> Unit,
) {
val viewModel = getViewModel()
- val name by viewModel.name.observeAsState(initial = "")
+ val name by viewModel.name.collectAsState(initial = "")
AddContactSheet(
avatarLabel = name,
address = address,
@@ -439,10 +438,10 @@ fun SendConfirmRoute(
parametersOf(tradableId)
}
- val gasLimit by gasFeeViewModel.gasLimit.observeAsState(initial = -1.0)
- val maxPriorityFee by gasFeeViewModel.maxPriorityFeePerGas.observeAsState(initial = -1.0)
- val maxFee by gasFeeViewModel.maxFeePerGas.observeAsState(initial = -1.0)
- val gasUsdTotal by gasFeeViewModel.gasUsdTotal.observeAsState(initial = BigDecimal.ZERO)
+ val gasLimit by gasFeeViewModel.gasLimit.collectAsState(initial = -1.0)
+ val maxPriorityFee by gasFeeViewModel.maxPriorityFeePerGas.collectAsState(initial = -1.0)
+ val maxFee by gasFeeViewModel.maxFeePerGas.collectAsState(initial = -1.0)
+ val gasUsdTotal by gasFeeViewModel.gasUsdTotal.collectAsState(initial = BigDecimal.ZERO)
val selectTradable by transferDetailViewModel.selectedTradable.collectAsState(null)
val amount = remember(amountString) { BigDecimal(amountString) }
@@ -450,9 +449,9 @@ fun SendConfirmRoute(
val viewModel = getViewModel {
parametersOf(address)
}
- val deeplink by viewModel.deepLink.observeAsState(initial = "")
- val addressData by viewModel.addressData.observeAsState(initial = null)
- val loading by viewModel.loadingState.observeAsState()
+ val deeplink by viewModel.deepLink.collectAsState(initial = "")
+ val addressData by viewModel.addressData.collectAsState(initial = null)
+ val loading by viewModel.loadingState.collectAsState()
val totalPrice = selectTradable?.let {
when (it) {
is WalletTokenData -> (amount * it.tokenData.price + gasUsdTotal).humanizeDollar()
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/walletconnect/WalletConnectModal.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/walletconnect/WalletConnectModal.kt
index 99948976..2f69f4dd 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/walletconnect/WalletConnectModal.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/scenes/wallets/walletconnect/WalletConnectModal.kt
@@ -55,6 +55,7 @@ import androidx.compose.material.TabRowDefaults
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -72,13 +73,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import androidx.navigation.NavType
-import androidx.navigation.compose.dialog
-import androidx.navigation.navArgument
-import androidx.navigation.navOptions
import coil.compose.rememberImagePainter
-import com.dimension.maskbook.common.ext.observeAsState
+import com.dimension.maskbook.common.ext.navOptions
+import com.dimension.maskbook.common.route.composable
import com.dimension.maskbook.common.ui.barcode.rememberBarcodeBitmap
import com.dimension.maskbook.common.ui.notification.StringResNotificationEvent.Companion.show
import com.dimension.maskbook.common.ui.widget.HorizontalScenePadding
@@ -96,12 +93,12 @@ import com.dimension.maskbook.wallet.route.WalletRoute
import com.dimension.maskbook.wallet.ui.scenes.wallets.management.supportedChainType
import com.dimension.maskbook.wallet.viewmodel.wallets.walletconnect.WalletConnectResult
import com.dimension.maskbook.wallet.viewmodel.wallets.walletconnect.WalletConnectViewModel
-import com.google.accompanist.navigation.animation.AnimatedNavHost
-import com.google.accompanist.navigation.animation.composable
-import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
+import moe.tlaster.precompose.navigation.NavController
+import moe.tlaster.precompose.navigation.NavHost
+import moe.tlaster.precompose.navigation.rememberNavController
import org.koin.core.parameter.parametersOf
enum class WalletConnectType {
@@ -112,7 +109,7 @@ enum class WalletConnectType {
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun WalletConnectModal(rootNavController: NavController) {
- val navController = rememberAnimatedNavController()
+ val navController = rememberNavController()
val scope = rememberCoroutineScope()
val onResult: (WalletConnectResult) -> Unit = { result ->
scope.launch(Dispatchers.Main) {
@@ -135,9 +132,9 @@ fun WalletConnectModal(rootNavController: NavController) {
val viewModel = getViewModel {
parametersOf(onResult)
}
- val wcUrl by viewModel.wcUrl.observeAsState(initial = "")
+ val wcUrl by viewModel.wcUrl.collectAsState(initial = "")
val context = LocalContext.current
- val currentSupportedWallets by viewModel.currentSupportedWallets.observeAsState(initial = emptyList())
+ val currentSupportedWallets by viewModel.currentSupportedWallets.collectAsState(initial = emptyList())
MaskModal {
val clipboardManager = LocalClipboardManager.current
val inAppNotification = LocalInAppNotification.current
@@ -151,9 +148,9 @@ fun WalletConnectModal(rootNavController: NavController) {
textAlign = TextAlign.Center,
)
- AnimatedNavHost(
+ NavHost(
navController = navController,
- startDestination = "WalletConnectTypeSelect"
+ initialRoute = "WalletConnectTypeSelect"
) {
composable("WalletConnectTypeSelect") {
TypeSelectScene(
@@ -201,9 +198,8 @@ fun WalletConnectModal(rootNavController: NavController) {
dialog(
"WalletConnectUnsupportedNetwork/{network}",
- listOf(navArgument("network") { type = NavType.StringType })
) {
- val network = it.arguments?.getString("network") ?: "unKnown"
+ val network: String = it.path("network")
WalletConnectUnsupportedNetwork(
onBack = {
viewModel.retry()
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/tab/WalletTabScreen.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/tab/WalletTabScreen.kt
index 51ff7187..ae2c0685 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/tab/WalletTabScreen.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/tab/WalletTabScreen.kt
@@ -21,19 +21,17 @@
package com.dimension.maskbook.wallet.ui.tab
import androidx.compose.runtime.Composable
-import androidx.navigation.NavController
import com.dimension.maskbook.common.route.CommonRoute
import com.dimension.maskbook.common.ui.tab.TabScreen
import com.dimension.maskbook.wallet.R
import com.dimension.maskbook.wallet.ui.scenes.wallets.intro.WalletIntroHost
-import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
+import moe.tlaster.precompose.navigation.NavController
class WalletTabScreen : TabScreen {
override val route = CommonRoute.Main.Tabs.Wallet
override val title: Int = R.string.tab_wallet
override val icon: Int = R.drawable.ic_wallet
- @OptIn(ExperimentalMaterialNavigationApi::class)
@Composable
override fun Content(navController: NavController, onBack: () -> Unit) {
WalletIntroHost(navController)
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/widget/CollectibleCollectionCard.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/widget/CollectibleCollectionCard.kt
index d0b5f2bc..9ad7667d 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/widget/CollectibleCollectionCard.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/ui/widget/CollectibleCollectionCard.kt
@@ -59,7 +59,7 @@ import com.dimension.maskbook.wallet.R
import com.dimension.maskbook.wallet.export.model.WalletCollectibleCollectionData
import com.dimension.maskbook.wallet.export.model.WalletCollectibleData
import com.dimension.maskbook.wallet.viewmodel.wallets.collectible.CollectiblesViewModel
-import org.koin.androidx.compose.getViewModel
+import moe.tlaster.koin.compose.getViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/WelcomeViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/WelcomeViewModel.kt
index a8d4ba07..20c8e642 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/WelcomeViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/WelcomeViewModel.kt
@@ -20,11 +20,11 @@
*/
package com.dimension.maskbook.wallet.viewmodel
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.persona.export.PersonaServices
import kotlinx.coroutines.flow.MutableStateFlow
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class WelcomeViewModel(
private val personaServices: PersonaServices,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/TokenDetailViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/TokenDetailViewModel.kt
index 61cd4ac8..c7a258a8 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/TokenDetailViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/TokenDetailViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.util.DateUtils
import com.dimension.maskbook.wallet.repository.ITokenRepository
@@ -33,6 +31,8 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class TokenDetailViewModel(
private val id: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/TouchIdEnableViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/TouchIdEnableViewModel.kt
index 1ef59f6a..9dcac7c8 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/TouchIdEnableViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/TouchIdEnableViewModel.kt
@@ -20,7 +20,7 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets
-import androidx.lifecycle.ViewModel
+import moe.tlaster.precompose.viewmodel.ViewModel
class TouchIdEnableViewModel : ViewModel() {
fun enable(onEnable: () -> Unit) {
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/UnlockWalletViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/UnlockWalletViewModel.kt
index dfdd2ce9..4fd6240b 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/UnlockWalletViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/UnlockWalletViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.util.BiometricAuthenticator
import com.dimension.maskbook.common.viewmodel.BiometricViewModel
import com.dimension.maskbook.setting.export.SettingServices
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.viewModelScope
class UnlockWalletViewModel(
settingsRepository: SettingServices,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletBalancesViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletBalancesViewModel.kt
index 0658eec1..538b1296 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletBalancesViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletBalancesViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.bigDecimal.BigDecimal
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.ext.humanizeDollar
@@ -47,6 +45,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class WalletBalancesViewModel(
private val repository: IWalletRepository,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletConnectManagementViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletConnectManagementViewModel.kt
index be0f9305..1c2eafef 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletConnectManagementViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletConnectManagementViewModel.kt
@@ -20,12 +20,12 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.wallet.export.model.WalletData
import com.dimension.maskbook.wallet.repository.IWalletRepository
import com.dimension.maskbook.wallet.walletconnect.WalletConnectClientManager
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class WalletConnectManagementViewModel(
private val manager: WalletConnectClientManager,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletManagementModalViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletManagementModalViewModel.kt
index 50e926b9..4bb419e5 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletManagementModalViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/WalletManagementModalViewModel.kt
@@ -20,10 +20,10 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.repository.IWalletRepository
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class WalletManagementModalViewModel(
private val repository: IWalletRepository,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/collectible/CollectibleDetailViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/collectible/CollectibleDetailViewModel.kt
index 8c224e85..7c0a391c 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/collectible/CollectibleDetailViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/collectible/CollectibleDetailViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.collectible
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.util.DateUtils
import com.dimension.maskbook.wallet.repository.ICollectibleRepository
@@ -32,6 +30,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class CollectibleDetailViewModel(
private val id: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/collectible/CollectiblesViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/collectible/CollectiblesViewModel.kt
index 5ffdaa30..0af6c869 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/collectible/CollectiblesViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/collectible/CollectiblesViewModel.kt
@@ -20,7 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.collectible
-import androidx.lifecycle.ViewModel
import androidx.paging.PagingData
import com.dimension.maskbook.wallet.export.model.WalletCollectibleData
import com.dimension.maskbook.wallet.repository.ICollectibleRepository
@@ -29,6 +28,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.mapNotNull
+import moe.tlaster.precompose.viewmodel.ViewModel
class CollectiblesViewModel(
private val repository: ICollectibleRepository,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/create/CreateWalletRecoveryKeyViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/create/CreateWalletRecoveryKeyViewModel.kt
index 1ecf5d20..1f17ba50 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/create/CreateWalletRecoveryKeyViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/create/CreateWalletRecoveryKeyViewModel.kt
@@ -20,7 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.create
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.viewmodel.BaseMnemonicPhraseViewModel
import com.dimension.maskbook.wallet.db.model.CoinPlatformType
@@ -29,6 +28,7 @@ import com.dimension.maskbook.wallet.repository.WalletCreateOrImportResult
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.viewModelScope
import java.util.UUID
class CreateWalletRecoveryKeyViewModel(
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletDerivationPathViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletDerivationPathViewModel.kt
index c96f1d7b..4deff364 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletDerivationPathViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletDerivationPathViewModel.kt
@@ -21,8 +21,6 @@
package com.dimension.maskbook.wallet.viewmodel.wallets.import
import androidx.compose.runtime.snapshots.SnapshotStateMap
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.db.model.CoinPlatformType
import com.dimension.maskbook.wallet.repository.IWalletRepository
@@ -37,6 +35,8 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class ImportWalletDerivationPathViewModel(
private val wallet: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletKeystoreViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletKeystoreViewModel.kt
index 8286c73f..6be9cc01 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletKeystoreViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletKeystoreViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.import
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.db.model.CoinPlatformType
import com.dimension.maskbook.wallet.repository.IWalletRepository
@@ -33,6 +31,8 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class ImportWalletKeystoreViewModel(
private val wallet: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletMnemonicViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletMnemonicViewModel.kt
index 3ee6d8d7..37f34174 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletMnemonicViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletMnemonicViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.import
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.export.model.WalletData
import com.dimension.maskbook.wallet.repository.IWalletRepository
@@ -30,6 +28,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
import java.util.UUID
class ImportWalletMnemonicViewModel(
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletPrivateKeyViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletPrivateKeyViewModel.kt
index 8388eacc..5571fdae 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletPrivateKeyViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/import/ImportWalletPrivateKeyViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.import
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.db.model.CoinPlatformType
import com.dimension.maskbook.wallet.repository.IWalletRepository
@@ -30,6 +28,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class ImportWalletPrivateKeyViewModel(
private val wallet: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletBackupViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletBackupViewModel.kt
index 0f2d3069..1c662224 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletBackupViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletBackupViewModel.kt
@@ -20,11 +20,11 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.management
-import androidx.lifecycle.ViewModel
import com.dimension.maskbook.setting.export.SettingServices
import com.dimension.maskbook.wallet.repository.IWalletRepository
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapNotNull
+import moe.tlaster.precompose.viewmodel.ViewModel
class WalletBackupViewModel(
private val repository: IWalletRepository,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletDeleteViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletDeleteViewModel.kt
index c6e5d770..6609043e 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletDeleteViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletDeleteViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.management
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.setting.export.SettingServices
import com.dimension.maskbook.wallet.repository.IWalletRepository
@@ -29,6 +27,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class WalletDeleteViewModel(
private val id: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletRenameViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletRenameViewModel.kt
index 91a822de..ca2f3331 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletRenameViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletRenameViewModel.kt
@@ -20,11 +20,11 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.management
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.repository.IWalletRepository
import kotlinx.coroutines.flow.MutableStateFlow
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class WalletRenameViewModel(
private val walletId: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletSwitchEditViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletSwitchEditViewModel.kt
index 535e0a25..11abb16b 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletSwitchEditViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletSwitchEditViewModel.kt
@@ -20,10 +20,10 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.management
-import androidx.lifecycle.ViewModel
import com.dimension.maskbook.wallet.repository.IWalletRepository
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
class WalletSwitchEditViewModel(
private val id: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletSwitchViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletSwitchViewModel.kt
index 9ba12703..281dc7d2 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletSwitchViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletSwitchViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.management
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.export.model.ChainType
import com.dimension.maskbook.wallet.export.model.WalletData
@@ -29,6 +27,8 @@ import com.dimension.maskbook.wallet.repository.IWalletRepository
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class WalletSwitchViewModel(
private val walletRepository: IWalletRepository
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletTransactionHistoryViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletTransactionHistoryViewModel.kt
index 711ca4c9..8f865108 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletTransactionHistoryViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/management/WalletTransactionHistoryViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.management
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.util.DateUtils
import com.dimension.maskbook.wallet.repository.ITransactionRepository
@@ -30,6 +28,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class WalletTransactionHistoryViewModel(
private val repository: IWalletRepository,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/AddContactViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/AddContactViewModel.kt
index 4606116c..0ccfcd90 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/AddContactViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/AddContactViewModel.kt
@@ -20,13 +20,13 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.send
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.ext.onFinished
import com.dimension.maskbook.wallet.usecase.AddContactUseCase
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class AddContactViewModel(
private val addContact: AddContactUseCase,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/GasFeeViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/GasFeeViewModel.kt
index c5e1d9c3..c3babc70 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/GasFeeViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/GasFeeViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.send
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.ext.humanizeMinutes
import com.dimension.maskbook.common.ext.onFinished
@@ -42,6 +40,8 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
import java.math.BigDecimal
class GasFeeViewModel(
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SearchAddressViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SearchAddressViewModel.kt
index f3d6d9e2..cf1f618a 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SearchAddressViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SearchAddressViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.send
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.Validator
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.export.model.ChainType
@@ -37,6 +35,8 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
sealed class EnsData {
object Loading : EnsData()
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SearchTradableViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SearchTradableViewModel.kt
index 3b861e1b..75769a5d 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SearchTradableViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SearchTradableViewModel.kt
@@ -20,14 +20,14 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.send
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.usecase.GetWalletCollectibleCollectionsUseCase
import com.dimension.maskbook.wallet.usecase.GetWalletTokensUseCase
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class SearchTradableViewModel(
getWalletTokens: GetWalletTokensUseCase,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SendConfirmViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SendConfirmViewModel.kt
index 253fa0c6..c621c1cd 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SendConfirmViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/SendConfirmViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.send
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.bigDecimal.BigDecimal
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.ext.onFinished
@@ -35,6 +33,8 @@ import com.dimension.maskbook.wallet.usecase.SendWalletCollectibleUseCase
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class SendConfirmViewModel(
private val toAddress: String,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/TransferDetailViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/TransferDetailViewModel.kt
index 9b037362..9f0e8c98 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/TransferDetailViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/TransferDetailViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.send
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.export.model.TradableData
import com.dimension.maskbook.wallet.export.model.WalletCollectibleData
@@ -38,6 +36,8 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
import java.math.BigDecimal
class TransferDetailViewModel(
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/Web3TransactionConfirmViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/Web3TransactionConfirmViewModel.kt
index 576bfcfa..fa35c409 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/Web3TransactionConfirmViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/send/Web3TransactionConfirmViewModel.kt
@@ -20,8 +20,6 @@
*/
package com.dimension.maskbook.wallet.viewmodel.wallets.send
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.bigDecimal.BigDecimal
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.common.ext.onFinished
@@ -43,6 +41,8 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
class Web3TransactionConfirmViewModel(
private val data: SendTransactionData,
diff --git a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/walletconnect/WalletConnectViewModel.kt b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/walletconnect/WalletConnectViewModel.kt
index 2e84409e..c580c6f4 100644
--- a/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/walletconnect/WalletConnectViewModel.kt
+++ b/wallet/src/androidMain/kotlin/com/dimension/maskbook/wallet/viewmodel/wallets/walletconnect/WalletConnectViewModel.kt
@@ -26,8 +26,6 @@ import android.content.pm.PackageManager
import android.content.pm.PackageManager.MATCH_DEFAULT_ONLY
import android.content.pm.ResolveInfo
import android.net.Uri
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.dimension.maskbook.common.ext.asStateIn
import com.dimension.maskbook.wallet.db.model.CoinPlatformType
import com.dimension.maskbook.wallet.export.model.ChainType
@@ -41,6 +39,8 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
+import moe.tlaster.precompose.viewmodel.ViewModel
+import moe.tlaster.precompose.viewmodel.viewModelScope
sealed class WalletConnectResult {
data class Success(val switchNetwork: Boolean = false) : WalletConnectResult()