refactor: switch from metro to koin
Honestly metro looks too overcomplicated and I still don't know how to use it properly. Switching to koin for now as I'm more comfortable with it.
This commit is contained in:
parent
53abaccd21
commit
2725342c3f
23 changed files with 165 additions and 199 deletions
|
|
@ -4,23 +4,23 @@ import android.os.Bundle
|
|||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import moe.lava.neon.ui.App
|
||||
import moe.lava.neon.ui.di.initKoin
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.android.ext.koin.androidLogger
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
initKoin {
|
||||
androidContext(this@MainActivity)
|
||||
androidLogger()
|
||||
}
|
||||
|
||||
setContent {
|
||||
App()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AppAndroidPreview() {
|
||||
App()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import androidx.compose.material3.MaterialExpressiveTheme
|
|||
import androidx.compose.material3.MotionScheme
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
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
|
||||
|
|
@ -14,12 +13,10 @@ import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
|||
import androidx.navigation3.ui.NavDisplay
|
||||
import androidx.savedstate.serialization.SavedStateConfiguration
|
||||
import co.touchlab.kermit.Logger
|
||||
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.ui.di.AppUiGraph
|
||||
import moe.lava.neon.core.repository.AuthRepository
|
||||
import moe.lava.neon.ui.screens.Login
|
||||
import moe.lava.neon.ui.screens.Sample
|
||||
import moe.lava.neon.ui.screens.chat.Chat
|
||||
|
|
@ -29,6 +26,7 @@ import moe.lava.neon.ui.screens.navigator.NavigatorModel
|
|||
import moe.lava.neon.ui.screens.navigator.NavigatorPreviewProvider
|
||||
import moe.lava.neon.ui.util.ThreePaneSceneStrategy
|
||||
import moe.lava.neon.ui.util.rememberThreePaneSceneStrategy
|
||||
import org.koin.compose.koinInject
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
object Route {
|
||||
|
|
@ -68,81 +66,78 @@ fun App() {
|
|||
exitProcess(1)
|
||||
}
|
||||
|
||||
val uiGraph = createGraph<AppUiGraph>()
|
||||
val graph = uiGraph.core
|
||||
CaptchaBinder(graph.api)
|
||||
CompositionLocalProvider(LocalMetroViewModelFactory provides uiGraph.metroViewModelFactory) {
|
||||
MaterialExpressiveTheme(
|
||||
colorScheme = getColorScheme(),
|
||||
motionScheme = MotionScheme.expressive(),
|
||||
) {
|
||||
val init = if (graph.auth.token != null) Route.Sample else Route.Login
|
||||
val backStack = rememberNavBackStack(config, Route.Sample)
|
||||
val threePaneStrategy = rememberThreePaneSceneStrategy<NavKey>()
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
entryDecorators = listOf(
|
||||
rememberSaveableStateHolderNavEntryDecorator(),
|
||||
rememberViewModelStoreNavEntryDecorator(),
|
||||
),
|
||||
onBack = { backStack.removeLastOrNull() },
|
||||
sceneStrategy = threePaneStrategy,
|
||||
entryProvider = entryProvider {
|
||||
entry<Route.Login> {
|
||||
Login(
|
||||
onSuccess = {
|
||||
backStack.clear()
|
||||
backStack.add(Route.Sample)
|
||||
}
|
||||
)
|
||||
}
|
||||
entry<Route.Sample> {
|
||||
Sample(
|
||||
navTest = {
|
||||
backStack.add(Route.Navigator(it))
|
||||
backStack.add(Route.Chat)
|
||||
backStack.add(Route.MembersList)
|
||||
},
|
||||
onRequestLogout = {
|
||||
backStack.clear()
|
||||
backStack.add(Route.Login)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
entry<Route.Navigator>(
|
||||
metadata = ThreePaneSceneStrategy.listPane()
|
||||
) { key ->
|
||||
if (key.left) {
|
||||
Navigator(
|
||||
NavigatorPreviewProvider.base2.copy(
|
||||
guildNavPosition = NavigatorModel.GuildNavPosition.LeftSidebar
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Navigator(
|
||||
NavigatorPreviewProvider.base2.copy(
|
||||
guildNavPosition = NavigatorModel.GuildNavPosition.BottomSheet
|
||||
)
|
||||
)
|
||||
val auth: AuthRepository = koinInject()
|
||||
CaptchaBinder(koinInject())
|
||||
MaterialExpressiveTheme(
|
||||
colorScheme = getColorScheme(),
|
||||
motionScheme = MotionScheme.expressive(),
|
||||
) {
|
||||
val init = if (auth.token != null) Route.Sample else Route.Login
|
||||
val backStack = rememberNavBackStack(config, Route.Sample)
|
||||
val threePaneStrategy = rememberThreePaneSceneStrategy<NavKey>()
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
entryDecorators = listOf(
|
||||
rememberSaveableStateHolderNavEntryDecorator(),
|
||||
rememberViewModelStoreNavEntryDecorator(),
|
||||
),
|
||||
onBack = { backStack.removeLastOrNull() },
|
||||
sceneStrategy = threePaneStrategy,
|
||||
entryProvider = entryProvider {
|
||||
entry<Route.Login> {
|
||||
Login(
|
||||
onSuccess = {
|
||||
backStack.clear()
|
||||
backStack.add(Route.Sample)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
entry<Route.Sample> {
|
||||
Sample(
|
||||
navTest = {
|
||||
backStack.add(Route.Navigator(it))
|
||||
backStack.add(Route.Chat)
|
||||
backStack.add(Route.MembersList)
|
||||
},
|
||||
onRequestLogout = {
|
||||
backStack.clear()
|
||||
backStack.add(Route.Login)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
entry<Route.Chat>(
|
||||
metadata = ThreePaneSceneStrategy.detailPane()
|
||||
) {
|
||||
Chat(
|
||||
onOpenMembers = { backStack.add(Route.MembersList) }
|
||||
entry<Route.Navigator>(
|
||||
metadata = ThreePaneSceneStrategy.listPane()
|
||||
) { key ->
|
||||
if (key.left) {
|
||||
Navigator(
|
||||
NavigatorPreviewProvider.base2.copy(
|
||||
guildNavPosition = NavigatorModel.GuildNavPosition.LeftSidebar
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Navigator(
|
||||
NavigatorPreviewProvider.base2.copy(
|
||||
guildNavPosition = NavigatorModel.GuildNavPosition.BottomSheet
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
entry<Route.MembersList>(
|
||||
metadata = ThreePaneSceneStrategy.extraPane()
|
||||
) {
|
||||
MembersList()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
entry<Route.Chat>(
|
||||
metadata = ThreePaneSceneStrategy.detailPane()
|
||||
) {
|
||||
Chat(
|
||||
onOpenMembers = { backStack.add(Route.MembersList) }
|
||||
)
|
||||
}
|
||||
|
||||
entry<Route.MembersList>(
|
||||
metadata = ThreePaneSceneStrategy.extraPane()
|
||||
) {
|
||||
MembersList()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
package moe.lava.neon.ui.di
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.DependencyGraph
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metro.Provider
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import dev.zacsweers.metrox.viewmodel.ManualViewModelAssistedFactory
|
||||
import dev.zacsweers.metrox.viewmodel.MetroViewModelFactory
|
||||
import dev.zacsweers.metrox.viewmodel.ViewModelAssistedFactory
|
||||
import dev.zacsweers.metrox.viewmodel.ViewModelGraph
|
||||
import moe.lava.neon.core.di.AppGraph
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@DependencyGraph(AppScope::class)
|
||||
interface AppUiGraph : ViewModelGraph {
|
||||
val core: AppGraph
|
||||
}
|
||||
|
||||
@Inject
|
||||
@ContributesBinding(AppScope::class)
|
||||
@SingleIn(AppScope::class)
|
||||
class AppViewModelFactory(
|
||||
override val viewModelProviders: Map<KClass<out ViewModel>, Provider<ViewModel>>,
|
||||
override val assistedFactoryProviders: Map<KClass<out ViewModel>, Provider<ViewModelAssistedFactory>>,
|
||||
override val manualAssistedFactoryProviders: Map<KClass<out ManualViewModelAssistedFactory>, Provider<ManualViewModelAssistedFactory>>,
|
||||
) : MetroViewModelFactory()
|
||||
13
ui/src/commonMain/kotlin/moe/lava/neon/ui/di/KoinInit.kt
Normal file
13
ui/src/commonMain/kotlin/moe/lava/neon/ui/di/KoinInit.kt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package moe.lava.neon.ui.di
|
||||
|
||||
import org.koin.core.KoinApplication
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.KoinAppDeclaration
|
||||
import org.koin.dsl.includes
|
||||
|
||||
fun initKoin(config: KoinAppDeclaration? = null): KoinApplication {
|
||||
return startKoin {
|
||||
includes(config)
|
||||
modules(uiModule)
|
||||
}
|
||||
}
|
||||
13
ui/src/commonMain/kotlin/moe/lava/neon/ui/di/UiModule.kt
Normal file
13
ui/src/commonMain/kotlin/moe/lava/neon/ui/di/UiModule.kt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package moe.lava.neon.ui.di
|
||||
|
||||
import moe.lava.neon.core.di.coreModule
|
||||
import moe.lava.neon.ui.screens.LoginViewModel
|
||||
import moe.lava.neon.ui.screens.SampleViewModel
|
||||
import org.koin.dsl.module
|
||||
import org.koin.plugin.module.dsl.viewModel
|
||||
|
||||
val uiModule = module {
|
||||
includes(coreModule)
|
||||
viewModel<LoginViewModel>()
|
||||
viewModel<SampleViewModel>()
|
||||
}
|
||||
|
|
@ -23,11 +23,6 @@ import androidx.compose.ui.text.input.VisualTransformation
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import co.touchlab.kermit.Logger
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesIntoMap
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metrox.viewmodel.ViewModelKey
|
||||
import dev.zacsweers.metrox.viewmodel.metroViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.lava.neon.core.repository.AuthRepository
|
||||
import moe.lava.neon.core.repository.AuthResponse
|
||||
|
|
@ -35,12 +30,13 @@ import moe.lava.neon.resources.Res
|
|||
import moe.lava.neon.resources.visibility
|
||||
import moe.lava.neon.resources.visibility_off
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
fun Login(
|
||||
onSuccess: () -> Unit,
|
||||
) {
|
||||
val viewModel: LoginViewModel = metroViewModel()
|
||||
val viewModel: LoginViewModel = koinViewModel()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
|
|
@ -113,9 +109,6 @@ fun Login(
|
|||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
@ViewModelKey(LoginViewModel::class)
|
||||
@ContributesIntoMap(AppScope::class)
|
||||
class LoginViewModel(
|
||||
private val auth: AuthRepository
|
||||
) : ViewModel() {
|
||||
|
|
|
|||
|
|
@ -20,11 +20,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesIntoMap
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metrox.viewmodel.ViewModelKey
|
||||
import dev.zacsweers.metrox.viewmodel.metroViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.lava.neon.core.api.gateway.GatewayHandler
|
||||
import moe.lava.neon.core.repository.AuthRepository
|
||||
|
|
@ -32,13 +27,14 @@ import moe.lava.neon.resources.Res
|
|||
import moe.lava.neon.resources.compose_multiplatform
|
||||
import moe.lava.neon.ui.Greeting
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
fun Sample(
|
||||
navTest: (Boolean) -> Unit,
|
||||
onRequestLogout: () -> Unit,
|
||||
) {
|
||||
val viewModel: SampleViewModel = metroViewModel()
|
||||
val viewModel: SampleViewModel = koinViewModel()
|
||||
var showContent by remember { mutableStateOf(false) }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -86,9 +82,6 @@ fun Sample(
|
|||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
@ViewModelKey(SampleViewModel::class)
|
||||
@ContributesIntoMap(AppScope::class)
|
||||
class SampleViewModel(
|
||||
private val auth: AuthRepository,
|
||||
private val gateway: GatewayHandler,
|
||||
|
|
|
|||
|
|
@ -6,12 +6,16 @@ import androidx.compose.ui.unit.Density
|
|||
import androidx.compose.ui.window.Window
|
||||
import androidx.compose.ui.window.application
|
||||
import moe.lava.neon.ui.App
|
||||
import moe.lava.neon.ui.di.initKoin
|
||||
|
||||
// The UI is designed with touchscreens in mind; on desktop elements may look gigantic
|
||||
// So scale them down a bit
|
||||
const val scaleFactor = 0.75f
|
||||
|
||||
fun main() = application {
|
||||
initKoin {
|
||||
printLogger()
|
||||
}
|
||||
Window(
|
||||
onCloseRequest = ::exitApplication,
|
||||
title = "Neon",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue