feat: basic repo + di
This commit is contained in:
parent
1b3b465112
commit
4d8872db9c
10 changed files with 185 additions and 22 deletions
|
|
@ -40,6 +40,10 @@ kotlin {
|
|||
implementation(libs.androidx.nav3.ui)
|
||||
implementation(libs.compose.material3.adaptive)
|
||||
implementation(libs.compose.material3.adaptive.nav3)
|
||||
|
||||
implementation(libs.metrox.viewmodel.compose)
|
||||
|
||||
implementation(libs.kermit)
|
||||
}
|
||||
commonTest.dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,12 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.navigation3.ui.defaultPredictivePopTransitionSpec
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metrox.viewmodel.LocalMetroViewModelFactory
|
||||
import dev.zacsweers.metrox.viewmodel.MetroViewModelFactory
|
||||
import moe.lava.neon.ui.App
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,21 @@ package moe.lava.neon.ui
|
|||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
import androidx.navigation3.runtime.rememberNavBackStack
|
||||
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
||||
import androidx.navigation3.ui.NavDisplay
|
||||
import androidx.savedstate.serialization.SavedStateConfiguration
|
||||
import dev.zacsweers.metro.createGraph
|
||||
import dev.zacsweers.metrox.viewmodel.LocalMetroViewModelFactory
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
import moe.lava.neon.core.di.AppGraph
|
||||
import moe.lava.neon.ui.di.AppUiGraph
|
||||
import moe.lava.neon.ui.screens.Login
|
||||
import moe.lava.neon.ui.screens.Sample
|
||||
|
||||
|
|
@ -18,7 +25,7 @@ private object Route {
|
|||
data object Login : NavKey
|
||||
|
||||
@Serializable
|
||||
data class Sample(val token: String) : NavKey
|
||||
data object Sample : NavKey
|
||||
}
|
||||
|
||||
private val config = SavedStateConfiguration {
|
||||
|
|
@ -30,25 +37,36 @@ private val config = SavedStateConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun App() {
|
||||
val uiGraph = createGraph<AppUiGraph>()
|
||||
val graph = uiGraph.core
|
||||
CompositionLocalProvider(LocalMetroViewModelFactory provides uiGraph.metroViewModelFactory) {
|
||||
MaterialTheme {
|
||||
val backStack = rememberNavBackStack(config, Route.Login)
|
||||
val init = if (graph.auth.token != null) Route.Sample else Route.Login
|
||||
val backStack = rememberNavBackStack(config, init)
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
entryDecorators = listOf(
|
||||
rememberSaveableStateHolderNavEntryDecorator(),
|
||||
rememberViewModelStoreNavEntryDecorator(),
|
||||
),
|
||||
onBack = { backStack.removeLastOrNull() },
|
||||
entryProvider = entryProvider {
|
||||
entry<Route.Login> {
|
||||
Login(
|
||||
onSuccess = { token ->
|
||||
backStack.add(Route.Sample(token))
|
||||
onSuccess = {
|
||||
backStack.clear()
|
||||
backStack.add(Route.Sample)
|
||||
}
|
||||
)
|
||||
}
|
||||
entry<Route.Sample> { key ->
|
||||
Sample(key.token)
|
||||
Sample()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package moe.lava.neon.ui.di
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.DependencyGraph
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metro.Provider
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import dev.zacsweers.metrox.viewmodel.ManualViewModelAssistedFactory
|
||||
import dev.zacsweers.metrox.viewmodel.MetroViewModelFactory
|
||||
import dev.zacsweers.metrox.viewmodel.ViewModelAssistedFactory
|
||||
import dev.zacsweers.metrox.viewmodel.ViewModelGraph
|
||||
import moe.lava.neon.core.di.AppGraph
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@DependencyGraph(AppScope::class)
|
||||
interface AppUiGraph : ViewModelGraph {
|
||||
val core: AppGraph
|
||||
}
|
||||
|
||||
@Inject
|
||||
@ContributesBinding(AppScope::class)
|
||||
@SingleIn(AppScope::class)
|
||||
class AppViewModelFactory(
|
||||
override val viewModelProviders: Map<KClass<out ViewModel>, Provider<ViewModel>>,
|
||||
override val assistedFactoryProviders: Map<KClass<out ViewModel>, Provider<ViewModelAssistedFactory>>,
|
||||
override val manualAssistedFactoryProviders: Map<KClass<out ManualViewModelAssistedFactory>, Provider<ManualViewModelAssistedFactory>>,
|
||||
) : MetroViewModelFactory()
|
||||
|
|
@ -9,14 +9,28 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.ViewModel
|
||||
import co.touchlab.kermit.Logger
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesIntoMap
|
||||
import dev.zacsweers.metro.ContributesTo
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metrox.viewmodel.ViewModelKey
|
||||
import dev.zacsweers.metrox.viewmodel.metroViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.lava.neon.core.repository.AuthRepository
|
||||
|
||||
@Composable
|
||||
fun Login(
|
||||
onSuccess: (token: String) -> Unit,
|
||||
onSuccess: () -> Unit,
|
||||
) {
|
||||
val viewModel: LoginViewModel = metroViewModel()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
// .background(MaterialTheme.colorScheme.primaryContainer)
|
||||
|
|
@ -31,8 +45,37 @@ fun Login(
|
|||
onValueChange = { token = it },
|
||||
label = { Text("Enter token") },
|
||||
)
|
||||
Button(onClick = { onSuccess(token) }) {
|
||||
Button(onClick = {
|
||||
scope.launch {
|
||||
val res = viewModel.login(token)
|
||||
when (res) {
|
||||
LoginViewModel.LoginResult.Failed -> {}
|
||||
LoginViewModel.LoginResult.Success -> onSuccess()
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text("Submit")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
@ViewModelKey(LoginViewModel::class)
|
||||
@ContributesIntoMap(AppScope::class)
|
||||
class LoginViewModel(
|
||||
private val auth: AuthRepository
|
||||
) : ViewModel() {
|
||||
sealed interface LoginResult {
|
||||
data object Failed : LoginResult
|
||||
data object Success : LoginResult
|
||||
}
|
||||
|
||||
suspend fun login(token: String): LoginResult {
|
||||
return try {
|
||||
auth.login(token)
|
||||
LoginResult.Success
|
||||
} catch(_: Throwable) {
|
||||
LoginResult.Failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,23 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.ViewModel
|
||||
import co.touchlab.kermit.Logger
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesIntoMap
|
||||
import dev.zacsweers.metro.ContributesTo
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metrox.viewmodel.ViewModelKey
|
||||
import dev.zacsweers.metrox.viewmodel.metroViewModel
|
||||
import moe.lava.neon.core.repository.AuthRepository
|
||||
import moe.lava.neon.resources.Res
|
||||
import moe.lava.neon.resources.compose_multiplatform
|
||||
import moe.lava.neon.ui.Greeting
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
|
||||
@Composable
|
||||
fun Sample(token: String) {
|
||||
fun Sample() {
|
||||
val viewModel: SampleViewModel = metroViewModel()
|
||||
var showContent by remember { mutableStateOf(false) }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -43,8 +53,17 @@ fun Sample(token: String) {
|
|||
) {
|
||||
Image(painterResource(Res.drawable.compose_multiplatform), null)
|
||||
Text("Compose: $greeting")
|
||||
Text("Passed token: $token")
|
||||
Text("Passed token: ${viewModel.token}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
@ViewModelKey(SampleViewModel::class)
|
||||
@ContributesIntoMap(AppScope::class)
|
||||
class SampleViewModel(
|
||||
private val auth: AuthRepository
|
||||
) : ViewModel() {
|
||||
val token get() = auth.token
|
||||
}
|
||||
|
|
|
|||
14
core/src/commonMain/kotlin/moe/lava/neon/core/di/AppGraph.kt
Normal file
14
core/src/commonMain/kotlin/moe/lava/neon/core/di/AppGraph.kt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package moe.lava.neon.core.di
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.DependencyGraph
|
||||
import dev.zacsweers.metro.GraphExtension
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import moe.lava.neon.core.repository.AuthRepository
|
||||
import moe.lava.neon.core.repository.UserRepository
|
||||
|
||||
@GraphExtension
|
||||
interface AppGraph {
|
||||
val auth: AuthRepository
|
||||
val users: UserRepository
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package moe.lava.neon.core.repository
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
|
||||
@Inject
|
||||
@SingleIn(AppScope::class)
|
||||
class AuthRepository {
|
||||
var token: String? = null
|
||||
private set
|
||||
|
||||
fun login(username: String, password: String) {
|
||||
// api.login(username, password)
|
||||
}
|
||||
suspend fun login(token: String) {
|
||||
this.token = token
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package moe.lava.neon.core.repository
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
|
||||
@Inject
|
||||
@SingleIn(AppScope::class)
|
||||
class UserRepository {
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ androidx-testExt = "1.3.0"
|
|||
composeHotReload = "1.0.0"
|
||||
composeMultiplatform = "1.10.0"
|
||||
junit = "4.13.2"
|
||||
kermit = "2.0.8"
|
||||
kotlin = "2.3.0"
|
||||
kotlinx-coroutines = "1.10.2"
|
||||
material3 = "1.10.0-alpha05"
|
||||
|
|
@ -21,6 +22,7 @@ material3-adaptive = "1.3.0-alpha03"
|
|||
metro = "0.10.0"
|
||||
|
||||
[libraries]
|
||||
kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
||||
junit = { module = "junit:junit", version.ref = "junit" }
|
||||
|
|
@ -43,6 +45,7 @@ compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMul
|
|||
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" }
|
||||
compose-uiToolingPreview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
|
||||
kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
|
||||
metrox-viewmodel-compose = { module = "dev.zacsweers.metro:metrox-viewmodel-compose", version.ref = "metro" }
|
||||
|
||||
[plugins]
|
||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue