refactor(folio): replace AppComponent with Metro AppGraph in App.kt

App now takes a suspend graph builder; the platform constructs
LedgerDatabase off the suspend DriverFactory.create() before invoking
the Metro graph factory. Routes resolve VMs through LocalAppGraph
instead of the hand-rolled LocalAppComponent.

Drops the loading-state placeholder comment (the empty Box is enough).
This commit is contained in:
pj committed 2026-04-26 16:05:43 +07:00
1 parent 2fb208bda9
commit 9cadf27301
3 files changed
+22 -42

No files matched your search

@@ -16,36 +16,25 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import app.folio.core.data.DriverFactory
import app.folio.core.data.Repository
import app.folio.core.data.SqlLedgerStore
import app.folio.db.LedgerDatabase
import app.folio.di.AppGraph
import app.folio.di.LocalAppGraph
import app.folio.feature.account.AddAccountRoute
import app.folio.feature.auth.LoginRoute
import app.folio.feature.home.HomeRoute
import app.folio.feature.ledger.AddTransactionRoute
import app.folio.feature.ledger.LedgerRoute
import app.folio.navigation.Navigator
import app.folio.navigation.Route
import app.folio.ui.testTagsAsResourceId
import app.folio.ui.theme.LedgerTheme
import app.folio.ui.theme.LocalTokens
import app.folio.ui.theme.Tokens
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun App(driverFactory: DriverFactory) {
var component by remember { mutableStateOf<AppComponent?>(null) }
fun App(graphBuilder: suspend () -> AppGraph) {
var graph by remember { mutableStateOf<AppGraph?>(null) }
LaunchedEffect(Unit) {
val driver = driverFactory.create()
val db = LedgerDatabase(driver)
val store = SqlLedgerStore(db)
component = AppComponent(repository = Repository(store), navigator = Navigator(initial = Route.Home))
}
LaunchedEffect(Unit) { graph = graphBuilder() }
LedgerTheme {
val t = Tokens()
@@ -55,14 +44,12 @@ fun App(driverFactory: DriverFactory) {
.fillMaxSize()
.background(t.bg)
.windowInsetsPadding(WindowInsets.safeDrawing)
.semantics { testTagsAsResourceId = true },
.testTagsAsResourceId(),
contentAlignment = Alignment.Center,
) {
val c = component
if (c == null) {
// Loading: keep blank to avoid surprising the spec runner.
} else {
CompositionLocalProvider(LocalAppComponent provides c) {
val g = graph
if (g != null) {
CompositionLocalProvider(LocalAppGraph provides g) {
AppContent()
}
}
@@ -73,15 +60,15 @@ fun App(driverFactory: DriverFactory) {
@Composable
private fun AppContent() {
val component = LocalAppComponent.current
val session by component.repository.session.collectAsState()
val route by component.navigator.current.collectAsState()
val graph = LocalAppGraph.current
val session by graph.repository.session.collectAsState()
val route by graph.navigator.current.collectAsState()
LaunchedEffect(session, route) {
if (session == null && route !is Route.Login) {
component.navigator.replace(Route.Login)
graph.navigator.replace(Route.Login)
} else if (session != null && route is Route.Login) {
component.navigator.replace(Route.Home)
graph.navigator.replace(Route.Home)
}
}
@@ -1,14 +0,0 @@
package app.folio
import androidx.compose.runtime.staticCompositionLocalOf
import app.folio.core.data.Repository
import app.folio.navigation.Navigator
class AppComponent(
val repository: Repository,
val navigator: Navigator,
)
val LocalAppComponent = staticCompositionLocalOf<AppComponent> {
error("AppComponent not provided. Wrap content in CompositionLocalProvider(LocalAppComponent provides ...).")
}
@@ -0,0 +1,7 @@
package app.folio.di
import androidx.compose.runtime.staticCompositionLocalOf
val LocalAppGraph = staticCompositionLocalOf<AppGraph> {
error("AppGraph not provided. Wrap content in CompositionLocalProvider(LocalAppGraph provides ...).")
}