Update readme and build dependency graph

pull/1902/head
Clara Fok 1 month ago
parent 9985a029e6
commit a059e42612

@ -85,7 +85,6 @@ dependencies {
implementation(projects.core.data) implementation(projects.core.data)
implementation(projects.core.model) implementation(projects.core.model)
implementation(projects.core.analytics) implementation(projects.core.analytics)
implementation(projects.core.navigation)
implementation(projects.sync.work) implementation(projects.sync.work)
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)

@ -41,9 +41,9 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourc
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone
import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackViewModel import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackViewModel
import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone
import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.NiaApp
import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState
import com.google.samples.apps.nowinandroid.util.isSystemInDarkTheme import com.google.samples.apps.nowinandroid.util.isSystemInDarkTheme
@ -160,7 +160,7 @@ class MainActivity : ComponentActivity() {
) { ) {
NiaApp( NiaApp(
appState, appState,
entryProviderBuilders entryProviderBuilders,
) )
} }
} }

@ -44,8 +44,8 @@ object BackStackProvider {
@Provides @Provides
@Singleton @Singleton
fun provideSerializersModule( fun provideSerializersModule(
polymorphicModuleBuilders: Set<@JvmSuppressWildcards PolymorphicModuleBuilder<NiaNavKey>.() -> Unit> polymorphicModuleBuilders: Set<@JvmSuppressWildcards PolymorphicModuleBuilder<NiaNavKey>.() -> Unit>,
) : SerializersModule = SerializersModule { ): SerializersModule = SerializersModule {
polymorphic(NiaNavKey::class) { polymorphic(NiaNavKey::class) {
polymorphicModuleBuilders.forEach { it() } polymorphicModuleBuilders.forEach { it() }
} }

@ -49,7 +49,7 @@ enum class TopLevelDestination(
@StringRes val iconTextId: Int, @StringRes val iconTextId: Int,
@StringRes val titleTextId: Int, @StringRes val titleTextId: Int,
val route: KClass<*>, val route: KClass<*>,
val key: NiaNavKey val key: NiaNavKey,
) { ) {
FOR_YOU( FOR_YOU(
selectedIcon = NiaIcons.Upcoming, selectedIcon = NiaIcons.Upcoming,
@ -57,7 +57,7 @@ enum class TopLevelDestination(
iconTextId = forYouR.string.feature_foryou_api_title, iconTextId = forYouR.string.feature_foryou_api_title,
titleTextId = R.string.app_name, titleTextId = R.string.app_name,
route = ForYouRoute::class, route = ForYouRoute::class,
key = ForYouRoute key = ForYouRoute,
), ),
BOOKMARKS( BOOKMARKS(
selectedIcon = NiaIcons.Bookmarks, selectedIcon = NiaIcons.Bookmarks,
@ -65,7 +65,7 @@ enum class TopLevelDestination(
iconTextId = bookmarksR.string.feature_bookmarks_api_title, iconTextId = bookmarksR.string.feature_bookmarks_api_title,
titleTextId = bookmarksR.string.feature_bookmarks_api_title, titleTextId = bookmarksR.string.feature_bookmarks_api_title,
route = BookmarksRoute::class, route = BookmarksRoute::class,
key = BookmarksRoute key = BookmarksRoute,
), ),
INTERESTS( INTERESTS(
selectedIcon = NiaIcons.Grid3x3, selectedIcon = NiaIcons.Grid3x3,
@ -73,7 +73,7 @@ enum class TopLevelDestination(
iconTextId = searchR.string.feature_search_api_interests, iconTextId = searchR.string.feature_search_api_interests,
titleTextId = searchR.string.feature_search_api_interests, titleTextId = searchR.string.feature_search_api_interests,
route = InterestsRoute::class, route = InterestsRoute::class,
key = InterestsRoute(null) key = InterestsRoute(null),
), ),
} }

@ -271,4 +271,4 @@ private fun Modifier.notificationDot(): Modifier =
), ),
) )
} }
} }

@ -18,7 +18,6 @@ package com.google.samples.apps.nowinandroid.ui
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
@ -125,6 +124,6 @@ private fun NavigationTrackingSideEffect(niaBackStack: NiaBackStack) {
val stack = niaBackStack.backStack.toList() val stack = niaBackStack.backStack.toList()
metricsHolder.state?.putState("Navigation", stack.lastOrNull().toString()) metricsHolder.state?.putState("Navigation", stack.lastOrNull().toString())
} }
onDispose { } onDispose { }
} }
} }

@ -125,7 +125,7 @@ class NiaAppScreenSizesScreenshotTests {
networkMonitor = networkMonitor, networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository, userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor, timeZoneMonitor = timeZoneMonitor,
niaBackStack = mockNiaBackStack() niaBackStack = mockNiaBackStack(),
) )
NiaApp( NiaApp(
fakeAppState, fakeAppState,
@ -230,4 +230,4 @@ class NiaAppScreenSizesScreenshotTests {
"expandedWidth_expandedHeight_showsNavigationRail", "expandedWidth_expandedHeight_showsNavigationRail",
) )
} }
} }

@ -145,4 +145,4 @@ class NiaAppStateTest {
state.currentTimeZone.value, state.currentTimeZone.value,
) )
} }
} }

@ -213,7 +213,7 @@ class SnackbarInsetsScreenshotTests {
CompositionLocalProvider( CompositionLocalProvider(
// Replaces images with placeholders // Replaces images with placeholders
LocalInspectionMode provides true, LocalInspectionMode provides true,
LocalSnackbarHostState provides snackbarHostState LocalSnackbarHostState provides snackbarHostState,
) { ) {
scope = rememberCoroutineScope() scope = rememberCoroutineScope()
@ -250,7 +250,7 @@ class SnackbarInsetsScreenshotTests {
networkMonitor = networkMonitor, networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository, userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor, timeZoneMonitor = timeZoneMonitor,
niaBackStack = mockNiaBackStack() niaBackStack = mockNiaBackStack(),
) )
NiaApp( NiaApp(
appState = appState, appState = appState,

@ -186,7 +186,7 @@ class SnackbarScreenshotTests {
CompositionLocalProvider( CompositionLocalProvider(
// Replaces images with placeholders // Replaces images with placeholders
LocalInspectionMode provides true, LocalInspectionMode provides true,
LocalSnackbarHostState provides snackbarHostState LocalSnackbarHostState provides snackbarHostState,
) { ) {
scope = rememberCoroutineScope() scope = rememberCoroutineScope()
@ -200,7 +200,7 @@ class SnackbarScreenshotTests {
networkMonitor = networkMonitor, networkMonitor = networkMonitor,
userNewsResourceRepository = userNewsResourceRepository, userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor, timeZoneMonitor = timeZoneMonitor,
niaBackStack = mockNiaBackStack() niaBackStack = mockNiaBackStack(),
) )
NiaApp( NiaApp(
appState = appState, appState = appState,

@ -26,12 +26,12 @@ import com.google.samples.apps.nowinandroid.feature.foryou.impl.ForYouScreen
val MockEntryProvider: Set<EntryProviderBuilder<NiaNavKey>.() -> Unit> = val MockEntryProvider: Set<EntryProviderBuilder<NiaNavKey>.() -> Unit> =
setOf( setOf(
{ {
entry<ForYouRoute>{ entry<ForYouRoute> {
ForYouScreen({}) ForYouScreen({})
} }
}, },
) )
private val startKey = ForYouRoute private val startKey = ForYouRoute
fun mockNiaBackStack() = NiaBackStack(startKey) fun mockNiaBackStack() = NiaBackStack(startKey)

@ -0,0 +1,3 @@
# :benchmarks module
## Dependency graph
![Dependency graph](../docs/images/graphs/dep_graph_benchmarks.svg)

@ -28,7 +28,6 @@ class AndroidFeatureApiConventionPlugin : Plugin<Project> {
dependencies { dependencies {
"api"(project(":core:navigation")) "api"(project(":core:navigation"))
"implementation"(project(":core:data"))
} }
} }
} }

@ -0,0 +1,3 @@
# :core:navigation module
## Dependency graph
![Dependency graph](../../docs/images/graphs/dep_graph_core_navigation.svg)

@ -1,3 +1,19 @@
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins { plugins {
alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library)
alias(libs.plugins.nowinandroid.hilt) alias(libs.plugins.nowinandroid.hilt)

@ -42,7 +42,6 @@ class NiaBackStackViewModelTest {
subclass(TestTopLevelKeySecond::class, TestTopLevelKeySecond.serializer()) subclass(TestTopLevelKeySecond::class, TestTopLevelKeySecond.serializer())
subclass(TestKeyFirst::class, TestKeyFirst.serializer()) subclass(TestKeyFirst::class, TestKeyFirst.serializer())
subclass(TestKeySecond::class, TestKeySecond.serializer()) subclass(TestKeySecond::class, TestKeySecond.serializer())
} }
} }
@ -57,7 +56,8 @@ class NiaBackStackViewModelTest {
rule.setContent { rule.setContent {
val viewModel = createViewModel() val viewModel = createViewModel()
assertThat(viewModel.backStackMap).containsEntry( assertThat(viewModel.backStackMap).containsEntry(
TestStartKey, mutableListOf(TestStartKey), TestStartKey,
mutableListOf(TestStartKey),
) )
} }
} }
@ -72,7 +72,8 @@ class NiaBackStackViewModelTest {
} }
assertThat(viewModel.backStackMap).containsEntry( assertThat(viewModel.backStackMap).containsEntry(
TestStartKey, mutableListOf(TestStartKey, TestKeyFirst), TestStartKey,
mutableListOf(TestStartKey, TestKeyFirst),
) )
} }
@ -87,8 +88,10 @@ class NiaBackStackViewModelTest {
} }
assertThat(viewModel.backStackMap).containsExactly( assertThat(viewModel.backStackMap).containsExactly(
TestStartKey, mutableListOf(TestStartKey, TestKeyFirst), TestStartKey,
TestTopLevelKeyFirst, mutableListOf(TestTopLevelKeyFirst), mutableListOf(TestStartKey, TestKeyFirst),
TestTopLevelKeyFirst,
mutableListOf(TestTopLevelKeyFirst),
).inOrder() ).inOrder()
} }
@ -102,8 +105,10 @@ class NiaBackStackViewModelTest {
} }
assertThat(viewModel.backStackMap).containsExactly( assertThat(viewModel.backStackMap).containsExactly(
TestStartKey, mutableListOf(TestStartKey, TestKeyFirst), TestStartKey,
TestTopLevelKeyFirst, mutableListOf(TestTopLevelKeyFirst, TestKeySecond), mutableListOf(TestStartKey, TestKeyFirst),
TestTopLevelKeyFirst,
mutableListOf(TestTopLevelKeyFirst, TestKeySecond),
).inOrder() ).inOrder()
} }
@ -115,12 +120,14 @@ class NiaBackStackViewModelTest {
backStack.navigate(TestKeyFirst) backStack.navigate(TestKeyFirst)
assertThat(viewModel.backStackMap).containsExactly( assertThat(viewModel.backStackMap).containsExactly(
TestStartKey, mutableListOf(TestStartKey, TestKeyFirst), TestStartKey,
mutableListOf(TestStartKey, TestKeyFirst),
) )
backStack.popLast() backStack.popLast()
assertThat(viewModel.backStackMap).containsExactly( assertThat(viewModel.backStackMap).containsExactly(
TestStartKey, mutableListOf(TestStartKey), TestStartKey,
mutableListOf(TestStartKey),
) )
} }
} }
@ -141,7 +148,8 @@ class NiaBackStackViewModelTest {
rule.runOnIdle { rule.runOnIdle {
scenario.viewModel.niaBackStack.navigate(TestKeyFirst) scenario.viewModel.niaBackStack.navigate(TestKeyFirst)
assertThat(scenario.viewModel.niaBackStack.backStack).containsExactly( assertThat(scenario.viewModel.niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst, TestStartKey,
TestKeyFirst,
).inOrder() ).inOrder()
} }
@ -149,7 +157,8 @@ class NiaBackStackViewModelTest {
rule.runOnIdle { rule.runOnIdle {
assertThat(scenario.viewModel.niaBackStack.backStack).containsExactly( assertThat(scenario.viewModel.niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst, TestStartKey,
TestKeyFirst,
).inOrder() ).inOrder()
} }
} }
@ -173,7 +182,10 @@ class NiaBackStackViewModelTest {
scenario.viewModel.niaBackStack.navigate(TestKeySecond) scenario.viewModel.niaBackStack.navigate(TestKeySecond)
assertThat(scenario.viewModel.niaBackStack.backStack).containsExactly( assertThat(scenario.viewModel.niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst, TestTopLevelKeyFirst, TestKeySecond, TestStartKey,
TestKeyFirst,
TestTopLevelKeyFirst,
TestKeySecond,
).inOrder() ).inOrder()
} }
@ -181,38 +193,41 @@ class NiaBackStackViewModelTest {
rule.runOnIdle { rule.runOnIdle {
assertThat(scenario.viewModel.niaBackStack.backStack).containsExactly( assertThat(scenario.viewModel.niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst, TestTopLevelKeyFirst, TestKeySecond, TestStartKey,
TestKeyFirst,
TestTopLevelKeyFirst,
TestKeySecond,
).inOrder() ).inOrder()
} }
} }
} }
@Serializable @Serializable
private object TestStartKey: NiaNavKey { private object TestStartKey : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }
@Serializable @Serializable
private object TestTopLevelKeyFirst: NiaNavKey { private object TestTopLevelKeyFirst : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }
@Serializable @Serializable
private object TestTopLevelKeySecond: NiaNavKey { private object TestTopLevelKeySecond : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }
@Serializable @Serializable
private object TestKeyFirst: NiaNavKey { private object TestKeyFirst : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = false get() = false
} }
@Serializable @Serializable
private object TestKeySecond: NiaNavKey { private object TestKeySecond : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = false get() = false
} }

@ -24,7 +24,6 @@ import androidx.compose.runtime.snapshots.SnapshotStateList
import org.jetbrains.annotations.VisibleForTesting import org.jetbrains.annotations.VisibleForTesting
import kotlin.collections.mutableListOf import kotlin.collections.mutableListOf
// TODO refine back behavior - perhaps take a lambda so that each screen / use site can customize back behavior? // TODO refine back behavior - perhaps take a lambda so that each screen / use site can customize back behavior?
// https://github.com/android/nowinandroid/issues/1934 // https://github.com/android/nowinandroid/issues/1934
class NiaBackStack( class NiaBackStack(
@ -32,10 +31,9 @@ class NiaBackStack(
) { ) {
internal var backStackMap: LinkedHashMap<NiaNavKey, MutableList<NiaNavKey>> = internal var backStackMap: LinkedHashMap<NiaNavKey, MutableList<NiaNavKey>> =
linkedMapOf( linkedMapOf(
startKey to mutableListOf(startKey) startKey to mutableListOf(startKey),
) )
@VisibleForTesting @VisibleForTesting
val backStack: SnapshotStateList<NiaNavKey> = mutableStateListOf(startKey) val backStack: SnapshotStateList<NiaNavKey> = mutableStateListOf(startKey)
@ -58,11 +56,10 @@ class NiaBackStack(
val tempStack = mapOf(startKey to backStackMap[startKey]!!) val tempStack = mapOf(startKey to backStackMap[startKey]!!)
backStackMap.clear() backStackMap.clear()
backStackMap.putAll(tempStack) backStackMap.putAll(tempStack)
// else either restore an existing substack or initiate new one // else either restore an existing substack or initiate new one
} else { } else {
backStackMap[key] = backStackMap.remove(key) ?: mutableListOf(key) backStackMap[key] = backStackMap.remove(key) ?: mutableListOf(key)
} }
} }
// not top level - add to current substack // not top level - add to current substack
else -> { else -> {
@ -73,7 +70,6 @@ class NiaBackStack(
} }
currentStack.add(key) currentStack.add(key)
} }
} }
updateBackStack() updateBackStack()
} }
@ -105,7 +101,7 @@ class NiaBackStack(
backStack.apply { backStack.apply {
clear() clear()
backStack.addAll( backStack.addAll(
backStackMap.flatMap { it.value } backStackMap.flatMap { it.value },
) )
} }
@ -128,4 +124,4 @@ private fun popErrorMessage(count: Int, lastPopped: NiaNavKey) =
""" """
Failed to pop $count entries. BackStack has been popped to an empty stack. Last Failed to pop $count entries. BackStack has been popped to an empty stack. Last
popped key is $lastPopped. popped key is $lastPopped.
""".trimIndent() """.trimIndent()

@ -23,20 +23,20 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.serialization.saved import androidx.lifecycle.serialization.saved
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.savedstate.serialization.SavedStateConfiguration import androidx.savedstate.serialization.SavedStateConfiguration
import javax.inject.Inject
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import javax.inject.Inject
@HiltViewModel @HiltViewModel
class NiaBackStackViewModel @Inject constructor( class NiaBackStackViewModel @Inject constructor(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
val niaBackStack: NiaBackStack, val niaBackStack: NiaBackStack,
serializersModules: SerializersModule, serializersModules: SerializersModule,
): ViewModel() { ) : ViewModel() {
private val config = SavedStateConfiguration { serializersModule = serializersModules } private val config = SavedStateConfiguration { serializersModule = serializersModules }
@ -53,11 +53,10 @@ class NiaBackStackViewModel @Inject constructor(
// Restore backstack from saved state handle if not emtpy // Restore backstack from saved state handle if not emtpy
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
niaBackStack.restore( niaBackStack.restore(
backStackMap as LinkedHashMap<NiaNavKey, MutableList<NiaNavKey>> backStackMap as LinkedHashMap<NiaNavKey, MutableList<NiaNavKey>>,
) )
} }
// Start observing changes to the backStack and save backStack whenever it updates // Start observing changes to the backStack and save backStack whenever it updates
viewModelScope.launch { viewModelScope.launch {
snapshotFlow { snapshotFlow {

@ -57,13 +57,15 @@ class NiaBackStackTest {
niaBackStack.navigate(TestKeyFirst) niaBackStack.navigate(TestKeyFirst)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst TestStartKey,
TestKeyFirst,
).inOrder() ).inOrder()
niaBackStack.navigate(TestKeyFirst) niaBackStack.navigate(TestKeyFirst)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst TestStartKey,
TestKeyFirst,
).inOrder() ).inOrder()
} }
@ -73,13 +75,16 @@ class NiaBackStackTest {
niaBackStack.navigate(TestKeyFirst) niaBackStack.navigate(TestKeyFirst)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestTopLevelKey, TestKeyFirst TestStartKey,
TestTopLevelKey,
TestKeyFirst,
).inOrder() ).inOrder()
niaBackStack.navigate(TestTopLevelKey) niaBackStack.navigate(TestTopLevelKey)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestTopLevelKey TestStartKey,
TestTopLevelKey,
).inOrder() ).inOrder()
} }
@ -130,12 +135,15 @@ class NiaBackStackTest {
niaBackStack.restore( niaBackStack.restore(
linkedMapOf( linkedMapOf(
TestStartKey to mutableListOf(TestStartKey, TestKeyFirst), TestStartKey to mutableListOf(TestStartKey, TestKeyFirst),
TestTopLevelKey to mutableListOf(TestTopLevelKey, TestKeySecond) TestTopLevelKey to mutableListOf(TestTopLevelKey, TestKeySecond),
) ),
) )
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst, TestTopLevelKey, TestKeySecond TestStartKey,
TestKeyFirst,
TestTopLevelKey,
TestKeySecond,
).inOrder() ).inOrder()
assertThat(niaBackStack.currentKey).isEqualTo(TestKeySecond) assertThat(niaBackStack.currentKey).isEqualTo(TestKeySecond)
@ -148,13 +156,16 @@ class NiaBackStackTest {
niaBackStack.navigate(TestKeySecond) niaBackStack.navigate(TestKeySecond)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst, TestKeySecond TestStartKey,
TestKeyFirst,
TestKeySecond,
).inOrder() ).inOrder()
niaBackStack.popLast() niaBackStack.popLast()
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst TestStartKey,
TestKeyFirst,
).inOrder() ).inOrder()
assertThat(niaBackStack.currentKey).isEqualTo(TestKeyFirst) assertThat(niaBackStack.currentKey).isEqualTo(TestKeyFirst)
@ -167,7 +178,9 @@ class NiaBackStackTest {
niaBackStack.navigate(TestTopLevelKey) niaBackStack.navigate(TestTopLevelKey)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst, TestTopLevelKey TestStartKey,
TestKeyFirst,
TestTopLevelKey,
).inOrder() ).inOrder()
assertThat(niaBackStack.currentKey).isEqualTo(TestTopLevelKey) assertThat(niaBackStack.currentKey).isEqualTo(TestTopLevelKey)
@ -177,7 +190,8 @@ class NiaBackStackTest {
niaBackStack.popLast() niaBackStack.popLast()
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst TestStartKey,
TestKeyFirst,
).inOrder() ).inOrder()
assertThat(niaBackStack.currentKey).isEqualTo(TestKeyFirst) assertThat(niaBackStack.currentKey).isEqualTo(TestKeyFirst)
@ -190,13 +204,15 @@ class NiaBackStackTest {
niaBackStack.navigate(TestKeySecond) niaBackStack.navigate(TestKeySecond)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestKeyFirst, TestKeySecond TestStartKey,
TestKeyFirst,
TestKeySecond,
).inOrder() ).inOrder()
niaBackStack.popLast(2) niaBackStack.popLast(2)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey TestStartKey,
).inOrder() ).inOrder()
assertThat(niaBackStack.currentKey).isEqualTo(TestStartKey) assertThat(niaBackStack.currentKey).isEqualTo(TestStartKey)
@ -205,7 +221,7 @@ class NiaBackStackTest {
@Test @Test
fun popMultipleTopLevel() { fun popMultipleTopLevel() {
val TestTopLevelKeyTwo = object: NiaNavKey { val testTopLevelKeyTwo = object : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }
@ -214,17 +230,21 @@ class NiaBackStackTest {
niaBackStack.navigate(TestTopLevelKey) niaBackStack.navigate(TestTopLevelKey)
niaBackStack.navigate(TestKeyFirst) niaBackStack.navigate(TestKeyFirst)
// third sub-stack // third sub-stack
niaBackStack.navigate(TestTopLevelKeyTwo) niaBackStack.navigate(testTopLevelKeyTwo)
niaBackStack.navigate(TestKeySecond) niaBackStack.navigate(TestKeySecond)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey, TestTopLevelKey, TestKeyFirst, TestTopLevelKeyTwo, TestKeySecond, TestStartKey,
TestTopLevelKey,
TestKeyFirst,
testTopLevelKeyTwo,
TestKeySecond,
).inOrder() ).inOrder()
niaBackStack.popLast(4) niaBackStack.popLast(4)
assertThat(niaBackStack.backStack).containsExactly( assertThat(niaBackStack.backStack).containsExactly(
TestStartKey TestStartKey,
).inOrder() ).inOrder()
assertThat(niaBackStack.currentKey).isEqualTo(TestStartKey) assertThat(niaBackStack.currentKey).isEqualTo(TestStartKey)
@ -239,23 +259,22 @@ class NiaBackStackTest {
} }
} }
private object TestStartKey: NiaNavKey { private object TestStartKey : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }
private object TestTopLevelKey: NiaNavKey { private object TestTopLevelKey : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }
private object TestKeyFirst: NiaNavKey { private object TestKeyFirst : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = false get() = false
} }
private object TestKeySecond: NiaNavKey { private object TestKeySecond : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = false get() = false
} }

@ -1,305 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1616" height="404pt" viewBox="0 0 1212.13 404">
<g class="graph">
<path fill="#fff" d="M0 404V0h1212.13v404z"/>
<g class="node" transform="translate(4 400)">
<ellipse cx="645.13" cy="-378" fill="none" stroke="#000" rx="27.66" ry="18"/>
<text x="645.13" y="-373.8" font-family="Times,serif" font-size="14" text-anchor="middle">:app</text>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="297.13" cy="-306" fill="none" stroke="#000" rx="75.39" ry="18"/>
<text x="297.13" y="-301.8" font-family="Times,serif" font-size="14" text-anchor="middle">:feature:interests</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M622.9 28.25C575.45 37.76 472.2 58.45 385.13 76c-5.51 1.11-11.23 2.26-16.96 3.42"/>
<path fill="red" d="m370.62 82.5-10.5-1.45 9.11-5.41z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="135.13" cy="-306" fill="none" stroke="#000" rx="68.95" ry="18"/>
<text x="135.13" y="-301.8" font-family="Times,serif" font-size="14" text-anchor="middle">:feature:foryou</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M621.71 25.48C553.19 31.85 369.1 50.17 217.13 76c-5.5.93-11.19 1.99-16.88 3.12"/>
<path d="m201.33 82.47-10.5-1.43 9.1-5.43z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="812.13" cy="-306" fill="none" stroke="#000" rx="86.12" ry="18"/>
<text x="812.13" y="-301.8" font-family="Times,serif" font-size="14" text-anchor="middle">:feature:bookmarks</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M672 32.59c24.72 10.36 64.82 27.17 96.45 40.43"/>
<path d="m769.56 69.68 7.87 7.1-10.58-.64z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="645.13" cy="-306" fill="none" stroke="#000" rx="62.51" ry="18"/>
<text x="645.13" y="-301.8" font-family="Times,serif" font-size="14" text-anchor="middle">:feature:topic</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M649.13 40.3v24.16"/>
<path d="m652.63 64.38-3.5 10-3.5-10z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="458.13" cy="-306" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="458.13" y="-301.8" font-family="Times,serif" font-size="14" text-anchor="middle">:feature:search</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M625.45 31.87C596.73 42.61 547.61 61 510.72 74.81"/>
<path d="m512.19 78-10.59.22 8.14-6.78z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="989.13" cy="-306" fill="none" stroke="#000" rx="72.71" ry="18"/>
<text x="989.13" y="-301.8" font-family="Times,serif" font-size="14" text-anchor="middle">:feature:settings</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M675.54 28.3c47.27 9.49 149.44 30.06 235.59 47.7 5.47 1.12 11.14 2.29 16.83 3.46"/>
<path d="m928.32 75.96 9.08 5.45-10.5 1.4z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="795.13" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="795.13" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M676.95 22.77c93.64-.29 395.91 2.79 476.18 53.23 39.37 24.74 55 42.5 55 89v74c0 44.13-8.03 63.44-44 89-52.76 37.49-225.24 24.92-289 36-5.22.91-10.62 1.93-16.03 3.02"/>
<path d="m860.04 370.4-10.51-1.38 9.08-5.47z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="553.13" cy="-234" fill="none" stroke="#000" rx="40.53" ry="18"/>
<text x="553.13" y="-229.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:ui</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M627.54 33.44C611.2 42.46 589.52 57.03 577.13 76c-11.72 17.95-16.64 41.79-18.67 60.29"/>
<path d="m561.96 136.38-4.33 9.66-2.64-10.26z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="774.13" cy="-162" fill="none" stroke="#000" rx="85.06" ry="18"/>
<text x="774.13" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:designsystem</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M677 24.37c90.47 4.71 371.67 21.68 398.13 51.63 10.59 11.99 8.85 22.67 0 36-14.26 21.47-161.89 76.8-243.49 106.12"/>
<path d="m832.85 221.41-10.6.07 8.24-6.66z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="584.13" cy="-162" fill="none" stroke="#000" rx="49.1" ry="18"/>
<text x="584.13" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:data</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M621.3 23.36C509.79 25.07 99.33 34.2 61.13 76-73.67 223.49 362.28 237.49 527.4 237.64"/>
<path d="m527.29 234.14 10 3.49-10 3.51z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="412.13" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="412.13" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M621.23 23.65C507.4 26.52 81.9 39.63 35.13 76c-33.07 25.71-31 47.11-31 89v74c0 72.85 233.17 116.46 348.68 133.59"/>
<path d="m353.06 369.09 9.39 4.9-10.4 2.02z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="1084.13" cy="-90" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="1084.13" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M676.84 24.37c96.52 5 414 23.46 449.29 51.63 38.51 30.74 36.4 60.07 25 108-8.61 36.2-29.46 73.82-44.83 98.2"/>
<path d="m1109.29 284.03-8.36 6.5 2.48-10.3z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="1084.13" cy="-234" fill="none" stroke="#000" rx="53.95" ry="18"/>
<text x="1084.13" y="-229.8" font-family="Times,serif" font-size="14" text-anchor="middle">:sync:work</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M676.8 24.09c94.84 4.04 402.48 19.53 431.33 51.91 15.09 16.94 7.03 42.79-3 62.27"/>
<path d="m1108.33 139.73-7.93 7.02 1.82-10.44z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M349.28 108.17c46.73 12.77 117.11 32.02 162.77 44.5"/>
<path d="m512.91 149.28 8.73 6.02-10.57.73z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M335.47 110.31c53.99 24.01 154.4 68.34 171.66 73.69 62.31 19.31 134.94 33.06 189.32 41.71"/>
<path d="m696.93 222.25 9.34 5-10.42 1.91z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M322.57 111.56c25.82 19.36 71.03 51.41 113.56 72.44 33.02 16.33 72.34 29.89 102.71 39.21"/>
<path d="m539.58 219.78 8.56 6.23-10.58.47z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="280.13" cy="-234" fill="none" stroke="#000" rx="61.99" ry="18"/>
<text x="280.13" y="-229.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:domain</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M296.93 112.3c-1.79 7.38-3.93 16.18-5.94 24.45"/>
<path fill="red" d="m294.76 136.06-5.76 8.89-1.04-10.54z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M189.81 106.51c9.08 1.93 18.46 3.84 27.32 5.49 102.76 19.19 223.91 36.91 290.28 46.2"/>
<path d="m507.84 154.73 9.42 4.84-10.39 2.09z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M189.66 106.67c58.87 13.72 150.08 35.36 165.47 41.33 31.15 12.08 34.32 25.38 66 36 25.5 8.55 177.39 29.47 274.66 42.32"/>
<path d="m696.12 222.84 9.45 4.77-10.37 2.17z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M148.05 112.02c11.87 20.76 34.89 55.14 65.08 71.98 52.8 29.45 223.23 43.94 315.06 49.76"/>
<path d="m528.25 230.26 9.77 4.11-10.2 2.88z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M170.95 110.36c21.25 10.26 49.41 23.85 72.31 34.91"/>
<path d="m244.53 142 7.48 7.5-10.52-1.2z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M764.96 108.83c-47.43 12.82-117.32 31.71-162.71 43.98"/>
<path d="m603.29 156.15-10.56-.77 8.74-5.99z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M811.47 112.41c-6.45 24.09-18.15 67.84-25.84 96.56"/>
<path d="m789.07 209.67-5.97 8.76-.8-10.57z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M787.58 111.42c-17.78 10.32-41.08 24.02-61.45 36.58-36.53 22.52-77.81 49.32-105.62 67.59"/>
<path d="m622.44 218.51-10.27 2.57 6.43-8.42z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M627.79 111.24c-12.51 9.51-28.47 21.66-42.05 31.99"/>
<path d="m588.06 145.86-10.07 3.27 5.83-8.84z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M664.43 111.85c22.5 24.76 64.67 71.18 90.84 99.99"/>
<path d="m757.69 209.3 4.14 9.75-9.32-5.04z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M641.77 112.13c-10.4 24.2-29.49 68.66-41.87 97.48"/>
<path d="m603.27 210.61-7.16 7.81.73-10.57z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M484.16 111.24c12.92 9.51 29.4 21.66 43.43 31.99"/>
<path d="m529.64 140.4 5.98 8.75-10.13-3.11z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M503.63 108.57c29.3 9.97 69.29 24.35 103.5 39.43 46.63 20.55 98.27 48.14 132.48 67.12"/>
<path d="m741.29 212.05 7.03 7.93-10.44-1.82z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M467.8 112.32c6.93 19.2 20.17 50.26 39.33 71.68 11.92 13.33 27.94 24.74 42.57 33.52"/>
<path d="m551.31 214.4 6.93 8.01-10.42-1.94z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M425.25 109.5c-27.48 10.81-65.2 25.64-94.72 37.25"/>
<path d="m332.05 149.92-10.59.4 8.03-6.92z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M939.84 106.5c-9.54 1.93-19.41 3.84-28.71 5.5-108 19.26-235.39 37.16-304.07 46.42"/>
<path d="m607.62 161.87-10.37-2.13 9.44-4.8z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M968.47 111.29c-38.36 25.33-112.4 74.23-155.97 103.01"/>
<path d="m814.7 217.04-10.27 2.59 6.42-8.43z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M952.44 109.27c-76.15 26.7-239.67 84.03-319.13 111.89"/>
<path d="m634.7 224.38-10.59.01 8.27-6.62z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="m589.26 177.18 131.95 41.79"/>
<path d="m722.19 215.61 8.47 6.36-10.59.31z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M522.04 175.53C471.55 189.9 380.2 224.25 341.13 292c-7.99 13.86-7.36 21.79 0 36 7.13 13.76 19.66 24.72 32.46 33.01"/>
<path d="m375.25 357.93 6.8 8.13-10.38-2.12z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M582.63 180.21c18.33 9.97 43.28 24.49 63.5 39.79 18.55 14.04 16.89 26.22 38 36 133.07 61.65 183.33 11.96 328 36 5.23.87 10.63 1.87 16.04 2.94"/>
<path d="m1028.66 291.47 9.08 5.45-10.5 1.4z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M626.36 249.67c8.15 2.17 16.73 4.39 24.77 6.33 74.65 18.01 120.68-23.69 169 36 14.16 17.49 6.2 42.9-3.73 62.09"/>
<path d="m819.51 355.71-7.95 7 1.85-10.43z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M625.21 250.09c8.46 2.23 17.45 4.36 25.92 5.91 158.59 29.1 201.81 10.35 361 36 5.31.86 10.8 1.86 16.3 2.93"/>
<path d="m1029.07 291.49 9.09 5.45-10.5 1.41z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="412.13" cy="-90" fill="none" stroke="#000" rx="66.26" ry="18"/>
<text x="412.13" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:database</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="m556.18 252-93.88 38.21"/>
<path fill="red" d="m465.15 292.83-10.58.53 7.94-7.02z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="931.13" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="931.13" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M625.98 249.79c8.25 2.19 16.97 4.38 25.15 6.21 88.99 19.9 112.45 18.74 202 36 6.31 1.22 12.89 2.52 19.45 3.84"/>
<path d="m872.97 292.35 9.1 5.43-10.49 1.43z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="742.13" cy="-90" fill="none" stroke="#000" rx="64.66" ry="18"/>
<text x="742.13" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:network</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M618.59 252.5c23.83 10.55 57.28 25.37 83.94 37.19"/>
<path d="m703.65 286.35 7.72 7.25-10.56-.85z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="578.13" cy="-90" fill="none" stroke="#000" rx="81.29" ry="18"/>
<text x="578.13" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:notifications</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M586.65 256.3c-.63 7.29-1.37 15.97-2.07 24.16"/>
<path d="m588.07 280.72-4.35 9.67-2.63-10.26z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M1037.64 172.89c-76.58 9.09-226.61 27.56-353.51 47.11-13.59 2.09-28.23 4.57-41.87 6.98"/>
<path d="m643.02 230.4-10.46-1.69 9.23-5.2z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M1088.13 184.41v96.17"/>
<path d="m1091.63 280.38-3.5 10-3.5-10z"/>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M331.63 177.94c55.58 12.79 147.28 33.91 204.66 47.12"/>
<path fill="red" d="m535.38 221.26 8.96 5.66-10.53 1.16z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M281.82 184.44c-3.23 31.81-5.55 99.99 26.31 143.56 13.06 17.87 33.81 30.02 53.65 38.13"/>
<path d="m362.8 362.78 8.12 6.81-10.59-.26z"/>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M416.13 328.3v24.16"/>
<path fill="red" d="m419.63 350.87-3.5 10-3.5-10z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M904.95 326.54c-19.28 9.92-44.51 22.9-65.5 33.71"/>
<path d="m841.07 363.35-10.5 1.47 7.29-7.69z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M877.24 319.75c-18.26 2.7-38.52 5.66-57.11 8.25-120.2 16.77-260.74 34.86-340.37 44.98"/>
<path d="m480.33 376.43-10.37-2.21 9.48-4.73z"/>
</g>
<g class="node" transform="translate(4 400)">
<ellipse cx="971.13" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="971.13" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M945.02 328.3c4.45 7.8 9.82 17.19 14.78 25.87"/>
<path d="m962.68 352.16 1.92 10.42-8-6.94z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M758.96 327.95c6.18 8.16 13.74 18.14 20.62 27.23"/>
<path d="M782.26 352.92 785.5 363l-8.82-5.86z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M697.6 322.31c-8.48 1.93-17.23 3.89-25.47 5.69-67.75 14.79-145.82 30.81-197.76 41.32"/>
<path d="m475.36 372.69-10.5-1.45 9.11-5.41z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M626.83 325.42c34.89 11.26 83.44 26.92 120.11 38.75"/>
<path d="m747.55 360.68 8.44 6.4-10.59.26z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M545.7 326.36c-25.25 10.65-59 24.88-85.72 36.15"/>
<path d="m461.34 365.73-10.57.66 7.85-7.11z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,45 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="616" height="188pt" viewBox="0 0 462.21 188">
<g class="graph">
<path fill="#fff" d="M0 188V0h462.21v188z"/>
<g class="node" transform="translate(4 184)">
<ellipse cx="169.06" cy="-162" fill="none" stroke="#000" rx="73.77" ry="18"/>
<text x="169.06" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:app-nia-catalog</text>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="85.06" cy="-18" fill="none" stroke="#000" rx="85.06" ry="18"/>
<text x="85.06" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:designsystem</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M162.93 40.13c-14.38 24.31-40.85 69.05-57.89 97.86"/>
<path d="m108.1 139.68-8.1 6.83 2.08-10.39z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="229.06" cy="-90" fill="none" stroke="#000" rx="40.53" ry="18"/>
<text x="229.06" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:ui</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M187.58 39.95c7.26 8.46 16.17 18.86 24.18 28.21"/>
<path fill="red" d="m213.26 64.52 3.85 9.87-9.17-5.31z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M206.31 108c-20.76 10.09-49.96 24.29-74.03 35.99"/>
<path d="m133.87 147.11-10.53 1.22 7.47-7.52z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="255.06" cy="-18" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="255.06" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M239.35 111.95c2.8 7.53 6.17 16.61 9.33 25.11"/>
<path fill="red" d="m251.39 134.29.2 10.6-6.77-8.16z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="397.06" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="397.06" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M261.94 107.03c26.15 10.9 65.07 27.11 95.09 39.63"/>
<path d="m358.37 143.42 7.89 7.08-10.58-.62z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="143pt" height="44pt" viewBox="0 0 142.68 44">
<g class="graph">
<path fill="#fff" d="M0 44V0h142.68v44z"/>
<g class="node" transform="translate(4 40)">
<ellipse cx="67.34" cy="-18" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="67.34" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 459 B

After

Width:  |  Height:  |  Size: 0 B

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="142pt" height="44pt" viewBox="0 0 141.63 44">
<g class="graph">
<path fill="#fff" d="M0 44V0h141.63v44z"/>
<g class="node" transform="translate(4 40)">
<ellipse cx="66.81" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="66.81" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 0 B

@ -1,97 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="791pt" height="188pt" viewBox="0 0 791.3 188">
<g class="graph">
<path fill="#fff" d="M0 188V0h791.3v188z"/>
<g class="node" transform="translate(4 184)">
<ellipse cx="327.95" cy="-162" fill="none" stroke="#000" rx="49.1" ry="18"/>
<text x="327.95" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:data</text>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="91.95" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="91.95" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M282.66 23.68C201.49 25.96 45.21 35.62 10.95 76c-10.35 12.2-7.76 22.01 0 36 8.1 14.58 22.05 25.67 36.34 33.84"/>
<path d="m48.61 142.58 7.22 7.76-10.48-1.56z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="563.95" cy="-90" fill="none" stroke="#000" rx="66.26" ry="18"/>
<text x="563.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:database</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M369.35 34.09c38.82 11.52 99.94 29.64 144.13 42.75"/>
<path fill="red" d="m512.83 73 8.59 6.2-10.58.51z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="411.95" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="411.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M351.02 38.88c10.72 8.94 24.32 20.27 36.3 30.26"/>
<path d="m389.49 66.39 5.44 9.09-9.92-3.71z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="80.95" cy="-90" fill="none" stroke="#000" rx="64.66" ry="18"/>
<text x="80.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:network</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M293.9 33.78C252.69 45.47 186.56 64.2 139.75 77.47"/>
<path d="m140.92 80.78-10.57-.64 8.66-6.1z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="715.95" cy="-90" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="715.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M376.63 29.91C437.15 39.44 548.48 57.53 642.95 76c5.19 1.01 10.56 2.11 15.95 3.24"/>
<path d="m659.38 75.76 9.05 5.51-10.51 1.34z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="244.95" cy="-90" fill="none" stroke="#000" rx="81.29" ry="18"/>
<text x="244.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:notifications</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M313.12 38.88c-10.52 8.88-23.84 20.11-35.62 30.04"/>
<path d="m279.93 71.46-9.9 3.77 5.39-9.13z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="244.95" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="244.95" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M518.77 106.31c-8.6 1.93-17.46 3.89-25.82 5.69-78.48 16.87-98.73 18.12-177 36-4.28.98-8.71 2.03-13.16 3.1"/>
<path fill="red" d="m305.26 154.1-10.55-1.01 8.88-5.79z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M365.01 106.27c-8.69 1.91-17.63 3.88-26.06 5.73-61.62 13.52-132.06 28.88-181.18 39.56"/>
<path d="m158.69 154.95-10.52-1.3 9.03-5.54z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M380.95 109.67c-25.67 10.76-60.7 25.44-88.22 36.98"/>
<path d="m294.18 149.84-10.57.64 7.87-7.1z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="411.95" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="411.95" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M415.95 112.3v24.16"/>
<path d="m419.45 136.38-3.5 10-3.5-10z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M87.67 112.3c1.16 7.38 2.55 16.18 3.85 24.45"/>
<path d="m94.94 135.98-1.91 10.42-5.01-9.33z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M119.33 109.67c25.13 10.73 59.38 25.35 86.37 36.87"/>
<path d="m206.8 143.2 7.83 7.15-10.57-.71z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M214.62 110.71c-22.33 10.22-51.64 23.63-75.54 34.56"/>
<path d="m140.79 148.34-10.55.97 7.64-7.34z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M248.95 112.3v24.16"/>
<path d="m252.45 136.38-3.5 10-3.5-10z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,105 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="791pt" height="260pt" viewBox="0 0 791.3 260">
<g class="graph">
<path fill="#fff" d="M0 260V0h791.3v260z"/>
<g class="node" transform="translate(4 256)">
<ellipse cx="327.95" cy="-234" fill="none" stroke="#000" rx="65.73" ry="18"/>
<text x="327.95" y="-229.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:data-test</text>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="327.95" cy="-162" fill="none" stroke="#000" rx="49.1" ry="18"/>
<text x="327.95" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:data</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M331.95 40.3v24.16"/>
<path fill="red" d="m335.45 62.87-3.5 10-3.5-10z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="91.95" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="91.95" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M282.66 95.68C201.49 97.96 45.21 107.62 10.95 148c-10.35 12.2-7.76 22.01 0 36 8.1 14.58 22.05 25.67 36.34 33.84"/>
<path d="m48.61 214.58 7.22 7.76-10.48-1.56z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="563.95" cy="-90" fill="none" stroke="#000" rx="66.26" ry="18"/>
<text x="563.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:database</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M369.35 106.09c38.82 11.52 99.94 29.64 144.13 42.75"/>
<path fill="red" d="m512.83 145 8.59 6.2-10.58.51z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="411.95" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="411.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M351.02 110.88c10.72 8.94 24.32 20.27 36.3 30.26"/>
<path d="m389.49 138.39 5.44 9.09-9.92-3.71z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="80.95" cy="-90" fill="none" stroke="#000" rx="64.66" ry="18"/>
<text x="80.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:network</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M293.9 105.78c-41.21 11.69-107.34 30.42-154.15 43.69"/>
<path d="m140.92 152.78-10.57-.64 8.66-6.1z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="715.95" cy="-90" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="715.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M376.63 101.91c60.52 9.53 171.85 27.62 266.32 46.09 5.19 1.01 10.56 2.11 15.95 3.24"/>
<path d="m659.38 147.76 9.05 5.51-10.51 1.34z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="244.95" cy="-90" fill="none" stroke="#000" rx="81.29" ry="18"/>
<text x="244.95" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:notifications</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M313.12 110.88c-10.52 8.88-23.84 20.11-35.62 30.04"/>
<path d="m279.93 143.46-9.9 3.77 5.39-9.13z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="244.95" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="244.95" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M518.77 178.31c-8.6 1.93-17.46 3.89-25.82 5.69-78.48 16.87-98.73 18.12-177 36-4.28.98-8.71 2.03-13.16 3.1"/>
<path fill="red" d="m305.26 226.1-10.55-1.01 8.88-5.79z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M365.01 178.27c-8.69 1.91-17.63 3.88-26.06 5.73-61.62 13.52-132.06 28.88-181.18 39.56"/>
<path d="m158.69 226.95-10.52-1.3 9.03-5.54z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M380.95 181.67c-25.67 10.76-60.7 25.44-88.22 36.98"/>
<path d="m294.18 221.84-10.57.64 7.87-7.1z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="411.95" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="411.95" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M415.95 184.3v24.16"/>
<path d="m419.45 208.38-3.5 10-3.5-10z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M87.67 184.3c1.16 7.38 2.55 16.18 3.85 24.45"/>
<path d="m94.94 207.98-1.91 10.42-5.01-9.33z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M119.33 181.67c25.13 10.73 59.38 25.35 86.37 36.87"/>
<path d="m206.8 215.2 7.83 7.15-10.57-.71z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M214.62 182.71c-22.33 10.22-51.64 23.63-75.54 34.56"/>
<path d="m140.79 220.34-10.55.97 7.64-7.34z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M248.95 184.3v24.16"/>
<path d="m252.45 208.38-3.5 10-3.5-10z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="188" height="116pt" viewBox="0 0 140.52 116">
<g class="graph">
<path fill="#fff" d="M0 116V0h140.52v116z"/>
<g class="node" transform="translate(4 112)">
<ellipse cx="66.26" cy="-90" fill="none" stroke="#000" rx="66.26" ry="18"/>
<text x="66.26" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:database</text>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="66.26" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="66.26" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M70.26 40.3v24.16"/>
<path fill="red" d="m73.76 62.87-3.5 10-3.5-10z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 920 B

After

Width:  |  Height:  |  Size: 0 B

@ -1,33 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="475pt" height="116pt" viewBox="0 0 475.29 116">
<g class="graph">
<path fill="#fff" d="M0 116V0h475.29v116z"/>
<g class="node" transform="translate(4 112)">
<ellipse cx="258.47" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="258.47" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="91.47" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="91.47" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M227.47 37.67c-24.41 10.23-57.26 24.01-84.09 35.25"/>
<path fill="red" d="m146.42 75.44-10.58.64 7.87-7.09z"/>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="258.47" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="258.47" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M262.47 40.3v24.16"/>
<path d="m265.97 64.38-3.5 10-3.5-10z"/>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="400.47" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="400.47" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M293.64 38.36c20.44 10.08 47.39 23.36 69.6 34.31"/>
<path d="m364.61 69.44 7.42 7.56-10.52-1.28z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="191pt" height="44pt" viewBox="0 0 190.95 44">
<g class="graph">
<path fill="#fff" d="M0 44V0h190.95v44z"/>
<g class="node" transform="translate(4 40)">
<ellipse cx="91.47" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="91.47" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 0 B

@ -1,45 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="475pt" height="188pt" viewBox="0 0 474.97 188">
<g class="graph">
<path fill="#fff" d="M0 188V0h474.97v188z"/>
<g class="node" transform="translate(4 184)">
<ellipse cx="154.81" cy="-162" fill="none" stroke="#000" rx="84.5" ry="18"/>
<text x="154.81" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-test</text>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="66.81" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="66.81" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M148.2 40.13c-15.13 24.41-43.04 69.44-60.88 98.23"/>
<path d="m90.51 139.87-8.25 6.65 2.3-10.34z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="222.81" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="222.81" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M175.27 39.95c8.19 8.42 18.24 18.77 27.29 28.08"/>
<path fill="red" d="m203.94 64.43 4.46 9.61-9.48-4.73z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M193.35 110.02c-23.03 10.33-53.85 24.16-78.82 35.37"/>
<path d="m116.16 148.49-10.55.9 7.69-7.29z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="242.81" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="242.81" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M231.76 112.3c2.13 7.46 4.68 16.38 7.06 24.73"/>
<path fill="red" d="m241.71 134.41-.61 10.58-6.12-8.66z"/>
</g>
<g class="node" transform="translate(4 184)">
<ellipse cx="409.81" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="409.81" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M264.66 109.17c29.59 11.07 70.88 26.53 102.52 38.37"/>
<path d="m368.38 144.25 8.13 6.79-10.59-.23z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="178pt" height="44pt" viewBox="0 0 178.11 44">
<g class="graph">
<path fill="#fff" d="M0 44V0h178.11v44z"/>
<g class="node" transform="translate(4 40)">
<ellipse cx="85.06" cy="-18" fill="none" stroke="#000" rx="85.06" ry="18"/>
<text x="85.06" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:designsystem</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 0 B

@ -1,109 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="857pt" height="260pt" viewBox="0 0 857.47 260">
<g class="graph">
<path fill="#fff" d="M0 260V0h857.47v260z"/>
<g class="node" transform="translate(4 256)">
<ellipse cx="644.47" cy="-234" fill="none" stroke="#000" rx="61.99" ry="18"/>
<text x="644.47" y="-229.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:domain</text>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="432.47" cy="-162" fill="none" stroke="#000" rx="49.1" ry="18"/>
<text x="432.47" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:data</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M608.6 36.17c-35.81 11.82-88.38 29.18-125.99 41.6"/>
<path fill="red" d="m485.41 80.53-10.59-.19 8.4-6.46z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="596.47" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="596.47" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M688.75 36.01C759.51 60.94 893.9 118.79 841.47 184c-21.58 26.85-111.18 40.88-174.8 47.61"/>
<path d="m667.32 235.06-10.31-2.47 9.6-4.5z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="350.47" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="350.47" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M388.98 99.32c-59.59 6.51-156.66 21.12-178.51 48.68-9.93 12.54-9.15 22.88 0 36 16.41 23.52 44.49 36.64 71.52 43.93"/>
<path d="m282.71 224.51 8.89 5.76-10.55 1.04z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="762.47" cy="-90" fill="none" stroke="#000" rx="66.26" ry="18"/>
<text x="762.47" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:database</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M478.76 103.63c50.44 10.34 137.3 28.29 211.71 44.37 4.89 1.06 9.96 2.16 15.04 3.28"/>
<path fill="red" d="m704.66 147.51 9.01 5.58-10.52 1.25z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="91.47" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="91.47" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M394.3 103.6c-51.27 10.45-140.4 28.66-216.83 44.4-6.2 1.28-12.66 2.61-19.13 3.95"/>
<path d="m159.51 155.28-10.5-1.4 9.08-5.45z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="432.47" cy="-90" fill="none" stroke="#000" rx="64.66" ry="18"/>
<text x="432.47" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:network</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M436.47 112.3v24.16"/>
<path d="m439.97 136.38-3.5 10-3.5-10z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="282.47" cy="-90" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="282.47" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M406.84 108.83c-22.19 10.35-52.86 24.67-77.69 36.26"/>
<path d="m330.82 148.17-10.54 1.05 7.58-7.4z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="596.47" cy="-90" fill="none" stroke="#000" rx="81.29" ry="18"/>
<text x="596.47" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:notifications</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M467.71 108.33c24.28 10.37 58.36 24.91 85.91 36.67"/>
<path d="m555 141.78 7.82 7.15-10.57-.71z"/>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M731.68 181.67c-25.43 10.73-60.1 25.35-87.42 36.87"/>
<path fill="red" d="m647.18 221.11-10.58.66 7.86-7.11z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M153.37 175.69c18.26 2.71 38.52 5.67 57.1 8.31 115.27 16.35 249.85 34.41 326.98 44.67"/>
<path d="m537.45 225.14 9.45 4.78-10.37 2.15z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M141.78 179.51c43.64 11.8 109.15 29.5 155.92 42.15"/>
<path d="m298.37 218.21 8.74 5.99-10.57.77z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="91.47" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="91.47" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M95.47 184.3v24.16"/>
<path d="m98.97 208.38-3.5 10-3.5-10z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M470.85 181.67c25.13 10.73 59.38 25.35 86.37 36.87"/>
<path d="m558.32 215.2 7.83 7.15-10.57-.71z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M417.04 183.59c-10.31 8.8-23.17 19.78-34.55 29.49"/>
<path d="m384.8 215.72-9.88 3.83 5.33-9.16z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M600.47 184.3v24.16"/>
<path d="m603.97 208.38-3.5 10-3.5-10z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M551.87 180.83c-40.74 11.59-98.94 28.15-141.58 40.29"/>
<path d="m411.28 224.47-10.58-.62 8.66-6.11z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="122pt" height="44pt" viewBox="0 0 122.31 44">
<g class="graph">
<path fill="#fff" d="M0 44V0h122.31v44z"/>
<g class="node" transform="translate(4 40)">
<ellipse cx="57.16" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="57.16" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 455 B

After

Width:  |  Height:  |  Size: 0 B

@ -1,25 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="274pt" height="116pt" viewBox="0 0 273.97 116">
<g class="graph">
<path fill="#fff" d="M0 116V0h273.97v116z"/>
<g class="node" transform="translate(4 112)">
<ellipse cx="137.81" cy="-90" fill="none" stroke="#000" rx="64.66" ry="18"/>
<text x="137.81" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:network</text>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="66.81" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="66.81" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M124.99 39.59c-8.76 8.63-19.63 19.35-29.33 28.91"/>
<path fill="red" d="m99.25 69.88-9.58 4.52 4.67-9.51z"/>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="208.81" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="208.81" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M158.64 39.59c8.75 8.63 19.63 19.35 29.33 28.91"/>
<path d="m190.37 65.95 4.66 9.52-9.58-4.53z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,25 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="274pt" height="116pt" viewBox="0 0 273.97 116">
<g class="graph">
<path fill="#fff" d="M0 116V0h273.97v116z"/>
<g class="node" transform="translate(4 112)">
<ellipse cx="128.16" cy="-90" fill="none" stroke="#000" rx="81.29" ry="18"/>
<text x="128.16" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:notifications</text>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="57.16" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="57.16" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M114.97 39.95c-8.76 8.63-19.57 19.29-29.2 28.78"/>
<path fill="red" d="m89.43 70.04-9.58 4.53 4.67-9.52z"/>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="199.16" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="199.16" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M149.34 39.95c8.64 8.51 19.26 18.98 28.78 28.37"/>
<path d="m180.39 65.64 4.66 9.51-9.58-4.53z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="284" height="116pt" viewBox="0 0 213.49 116">
<g class="graph">
<path fill="#fff" d="M0 116V0h213.49v116z"/>
<g class="node" transform="translate(4 112)">
<ellipse cx="102.74" cy="-90" fill="none" stroke="#000" rx="102.74" ry="18"/>
<text x="102.74" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:screenshot-testing</text>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="102.74" cy="-18" fill="none" stroke="#000" rx="85.06" ry="18"/>
<text x="102.74" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:designsystem</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M106.74 40.3v24.16"/>
<path fill="red" d="m110.24 62.87-3.5 10-3.5-10z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 944 B

After

Width:  |  Height:  |  Size: 0 B

@ -1,121 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="860pt" height="260pt" viewBox="0 0 859.61 260">
<g class="graph">
<path fill="#fff" d="M0 260V0h859.61v260z"/>
<g class="node" transform="translate(4 256)">
<ellipse cx="359.72" cy="-234" fill="none" stroke="#000" rx="58.77" ry="18"/>
<text x="359.72" y="-229.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:testing</text>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="93.72" cy="-90" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="93.72" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M335.26 38.2c-47.85 25.54-144.45 77.11-198.56 105.99"/>
<path d="m138.6 147.15-10.47 1.62 7.17-7.8z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="359.72" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="359.72" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M308.51 28.66C191.4 42.71-64.42 85.41 21.72 184c39.25 44.92 207.29 25.48 266 36 5.22.93 10.62 1.98 16.02 3.08"/>
<path d="m304.24 219.61 9.07 5.49-10.51 1.36z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="359.72" cy="-162" fill="none" stroke="#000" rx="49.1" ry="18"/>
<text x="359.72" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:data</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M363.72 40.3v24.16"/>
<path fill="red" d="m367.22 62.87-3.5 10-3.5-10z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="584.72" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="584.72" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M418.59 28.91C533.39 42.54 792.65 80.29 844.72 148c51.56 67.05-95.5 83.82-187.67 87.86"/>
<path d="m657.46 239.35-10.13-3.11 9.85-3.89z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="750.72" cy="-90" fill="none" stroke="#000" rx="81.29" ry="18"/>
<text x="750.72" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:notifications</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M400.51 36.36c70.04 25.44 222.72 80.88 302.82 109.97"/>
<path d="m704.04 142.87 8.2 6.7-10.59-.12z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M324.76 105.25c-44.63 11.75-118.2 31.11-169.45 44.6"/>
<path d="m156.44 153.17-10.56-.84 8.78-5.93z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M315.28 97.72c-35.07 4.8-80.25 17.47-103.56 50.28-9.26 13.04-9.26 22.96 0 36 11.01 15.5 53.26 29.5 90.43 39.21"/>
<path d="m302.68 219.73 8.83 5.85-10.56.94z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M407.47 102.59c58.44 10.17 165.19 28.86 256.25 45.41 6.39 1.16 13.02 2.38 19.66 3.6"/>
<path d="m683.85 148.13 9.19 5.26-10.47 1.62z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="584.72" cy="-90" fill="none" stroke="#000" rx="66.26" ry="18"/>
<text x="584.72" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:database</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M400.37 106.4c36.7 11.42 93.54 29.1 135.19 42.06"/>
<path fill="red" d="m534.93 144.6 8.51 6.31-10.58.37z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="284.72" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="284.72" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M346.33 111.24c-9.33 8.7-21.02 19.61-31.43 29.33"/>
<path d="m317.35 143.07-9.7 4.26 4.92-9.38z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="435.72" cy="-90" fill="none" stroke="#000" rx="64.66" ry="18"/>
<text x="435.72" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:network</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M381.35 111.24c9.55 8.79 21.54 19.83 32.16 29.62"/>
<path d="m415.57 137.99 4.98 9.35-9.73-4.2z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M694.53 178.33c-10.27 1.91-20.84 3.86-30.81 5.67-80.46 14.62-173.17 30.92-233.98 41.53"/>
<path d="m430.71 228.91-10.45-1.73 9.25-5.16z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M718.29 182.36c-25.24 10.65-58.99 24.88-85.72 36.15"/>
<path d="m633.94 221.73-10.58.66 7.86-7.11z"/>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M588.72 184.3v24.16"/>
<path fill="red" d="m592.22 206.87-3.5 10-3.5-10z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M306.5 183.59c9.27 8.65 20.79 19.41 31.06 28.99"/>
<path d="m339.79 209.87 4.92 9.38-9.7-4.26z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M338.6 178.64c53.46 12.47 138.16 32.24 193.94 45.25"/>
<path d="m533.22 220.46 8.95 5.68-10.54 1.13z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="183.72" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="183.72" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M265.3 183.24c-13.2 9.14-29.89 20.71-44.41 30.77"/>
<path d="m223.18 216.68-10.21 2.82 6.22-8.57z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M421.71 183.59c-9.39 8.65-21.07 19.41-31.48 28.99"/>
<path d="m392.71 215.06-9.73 4.2 4.99-9.35z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M471.69 182.02c22.2 10.42 51.97 24.41 75.93 35.67"/>
<path d="m548.9 214.42 7.56 7.42-10.54-1.08z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,33 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="616" height="116pt" viewBox="0 0 462.5 116">
<g class="graph">
<path fill="#fff" d="M0 116V0h462.5v116z"/>
<g class="node" transform="translate(4 112)">
<ellipse cx="237.34" cy="-90" fill="none" stroke="#000" rx="40.53" ry="18"/>
<text x="237.34" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:ui</text>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="67.34" cy="-18" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="67.34" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M212.5 34.87c-26.1 10.75-64.98 26.76-95.34 39.26"/>
<path fill="red" d="m120.2 76.67-10.58.57 7.91-7.04z"/>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="237.34" cy="-18" fill="none" stroke="#000" rx="85.06" ry="18"/>
<text x="237.34" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:designsystem</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M241.34 40.3v24.16"/>
<path d="m244.84 64.38-3.5 10-3.5-10z"/>
</g>
<g class="node" transform="translate(4 112)">
<ellipse cx="397.34" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="397.34" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M269.58 35.35c24.66 10.79 60.82 26.61 89.02 38.95"/>
<path d="m359.84 71.02 7.76 7.22-10.57-.81z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,121 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="791pt" height="332pt" viewBox="0 0 791.39 332">
<g class="graph">
<path fill="#fff" d="M0 332V0h791.39v332z"/>
<g class="node" transform="translate(4 328)">
<ellipse cx="453.04" cy="-306" fill="none" stroke="#000" rx="68.43" ry="18"/>
<text x="453.04" y="-301.8" font-family="Times,serif" font-size="14" text-anchor="middle">:sync:sync-test</text>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="412.04" cy="-162" fill="none" stroke="#000" rx="49.1" ry="18"/>
<text x="412.04" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:data</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M448.89 40.37c-4.55 10.2-10.05 23.45-13.85 35.63-6.18 19.86-11.03 42.82-14.28 60.45"/>
<path d="m424.22 136.97-5.19 9.24-1.7-10.46z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="494.04" cy="-234" fill="none" stroke="#000" rx="53.95" ry="18"/>
<text x="494.04" y="-229.8" font-family="Times,serif" font-size="14" text-anchor="middle">:sync:work</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M466.97 39.95c4.63 7.91 10.26 17.51 15.44 26.36"/>
<path fill="red" d="m484.65 63.2 2.03 10.4-8.07-6.86z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="92.04" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="92.04" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M367.83 169.79C265.55 176.21 34.29 193.52 11.04 220c-10.55 12.03-7.76 22.01 0 36 8.09 14.58 22.05 25.67 36.34 33.84"/>
<path d="m48.7 286.58 7.21 7.76-10.47-1.56z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="564.04" cy="-90" fill="none" stroke="#000" rx="66.26" ry="18"/>
<text x="564.04" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:database</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M445.71 180.66c22.53 10.38 53.84 24.8 79.14 36.45"/>
<path fill="red" d="m524.92 213.29 7.62 7.36-10.55-1z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="412.04" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="412.04" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M416.04 184.3v24.16"/>
<path d="m419.54 208.38-3.5 10-3.5-10z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="81.04" cy="-90" fill="none" stroke="#000" rx="64.66" ry="18"/>
<text x="81.04" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:network</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M373.69 175.43c-50.99 10.22-139.2 28.13-214.65 44.57-4.63 1.01-9.43 2.07-14.25 3.15"/>
<path d="m145.57 226.56-10.52-1.21 8.98-5.61z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="716.04" cy="-90" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="716.04" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M456.73 176.37c52.01 11.97 142.52 32.82 202.68 46.67"/>
<path d="m659.84 219.55 8.96 5.65-10.53 1.17z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="245.04" cy="-90" fill="none" stroke="#000" rx="81.29" ry="18"/>
<text x="245.04" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:notifications</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M384.63 180.17c-24.88 10.43-60.04 25.16-88.32 37.02"/>
<path d="m297.82 220.35-10.58.64 7.87-7.09z"/>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M479.03 111.24c-10.64 9.07-24.07 20.54-35.79 30.55"/>
<path fill="red" d="m446.86 143.3-9.88 3.83 5.33-9.16z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M522.22 110.47c39.47 25.24 117.88 75.4 163.23 104.4"/>
<path d="m687.06 211.75 6.54 8.33-10.31-2.44z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="245.04" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="245.04" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M518.86 250.31c-8.6 1.93-17.46 3.89-25.82 5.69-78.48 16.87-98.73 18.12-177 36-4.28.98-8.72 2.03-13.16 3.1"/>
<path fill="red" d="m305.35 298.1-10.55-1.01 8.87-5.79z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M365.1 250.27c-8.69 1.91-17.63 3.88-26.06 5.73-61.62 13.52-132.06 28.88-181.18 39.56"/>
<path d="m158.78 298.95-10.52-1.3 9.03-5.54z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M381.03 253.67c-25.66 10.76-60.69 25.44-88.21 36.98"/>
<path d="m294.27 293.84-10.57.64 7.87-7.1z"/>
</g>
<g class="node" transform="translate(4 328)">
<ellipse cx="412.04" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="412.04" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M416.04 256.3v24.16"/>
<path d="m419.54 280.38-3.5 10-3.5-10z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M87.76 256.3c1.16 7.38 2.54 16.18 3.84 24.45"/>
<path d="m95.03 279.98-1.91 10.42-5.01-9.33z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M119.42 253.67c25.13 10.73 59.38 25.35 86.36 36.87"/>
<path d="m206.89 287.2 7.83 7.15-10.58-.71z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M214.71 254.71c-22.34 10.22-51.64 23.63-75.54 34.56"/>
<path d="m140.88 292.34-10.55.97 7.64-7.34z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M249.04 256.3v24.16"/>
<path d="m252.54 280.38-3.5 10-3.5-10z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -1,109 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1084" height="260pt" viewBox="0 0 812.6 260">
<g class="graph">
<path fill="#fff" d="M0 260V0h812.6v260z"/>
<g class="node" transform="translate(4 256)">
<ellipse cx="161.34" cy="-234" fill="none" stroke="#000" rx="53.95" ry="18"/>
<text x="161.34" y="-229.8" font-family="Times,serif" font-size="14" text-anchor="middle">:sync:work</text>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="67.34" cy="-90" fill="none" stroke="#000" rx="67.34" ry="18"/>
<text x="67.34" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:analytics</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="m154.19 39.85-65.45 98.86"/>
<path d="m91.75 140.51-8.43 6.4 2.6-10.27z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="255.34" cy="-162" fill="none" stroke="#000" rx="49.1" ry="18"/>
<text x="255.34" y="-157.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:data</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M186.67 38.88c12.59 9.38 28.72 21.39 42.58 31.71"/>
<path fill="red" d="m230.06 66.83 5.93 8.78-10.11-3.16z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M225.73 107.51c-29.45 10.97-72.61 27.04-105.81 39.4"/>
<path d="m121.53 150.05-10.6.21 8.15-6.77z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="266.34" cy="-18" fill="none" stroke="#000" rx="66.81" ry="18"/>
<text x="266.34" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:common</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M228.51 108.42c-16 8.75-34.07 21.87-43.17 39.58-7.31 14.23-7.76 22.01 0 36 8.09 14.58 22.04 25.67 36.33 33.84"/>
<path d="m222.99 214.58 7.22 7.76-10.48-1.56z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="738.34" cy="-90" fill="none" stroke="#000" rx="66.26" ry="18"/>
<text x="738.34" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:database</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M306.37 99.66c76.16 7.87 231.05 25.23 360.97 48.34 5.15.92 10.48 1.95 15.82 3.04"/>
<path fill="red" d="m682.05 147.23 9.05 5.5-10.51 1.35z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="586.34" cy="-90" fill="none" stroke="#000" rx="67.87" ry="18"/>
<text x="586.34" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M301.62 103.67c50.44 10.37 137.29 28.35 211.72 44.33 4.88 1.05 9.92 2.14 14.99 3.24"/>
<path d="m528.92 147.79 9.02 5.56-10.52 1.28z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="255.34" cy="-90" fill="none" stroke="#000" rx="64.66" ry="18"/>
<text x="255.34" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:network</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M259.34 112.3v24.16"/>
<path d="m262.84 136.38-3.5 10-3.5-10z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="419.34" cy="-90" fill="none" stroke="#000" rx="81.29" ry="18"/>
<text x="419.34" y="-85.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:notifications</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M290.58 108.33c24.28 10.37 58.36 24.91 85.91 36.67"/>
<path d="m377.86 141.78 7.83 7.15-10.57-.71z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="419.34" cy="-18" fill="none" stroke="#000" rx="57.16" ry="18"/>
<text x="419.34" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:model</text>
</g>
<g stroke="red" stroke-width="2" class="edge">
<path fill="none" d="M693.16 178.31c-8.6 1.93-17.46 3.89-25.82 5.69-78.48 16.87-98.74 18.12-177 36-4.28.98-8.72 2.03-13.16 3.1"/>
<path fill="red" d="m479.65 226.1-10.55-1.01 8.87-5.79z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M539.4 178.27c-8.69 1.91-17.63 3.88-26.06 5.73-61.62 13.52-132.07 28.88-181.18 39.56"/>
<path d="m333.07 226.95-10.51-1.3 9.03-5.54z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M555.33 181.67c-25.67 10.76-60.69 25.44-88.21 36.98"/>
<path d="m468.57 221.84-10.58.64 7.87-7.1z"/>
</g>
<g class="node" transform="translate(4 256)">
<ellipse cx="586.34" cy="-18" fill="none" stroke="#000" rx="91.47" ry="18"/>
<text x="586.34" y="-13.8" font-family="Times,serif" font-size="14" text-anchor="middle">:core:datastore-proto</text>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M590.34 184.3v24.16"/>
<path d="m593.84 208.38-3.5 10-3.5-10z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M262.06 184.3c1.16 7.38 2.54 16.18 3.84 24.45"/>
<path d="m269.32 207.98-1.9 10.42-5.01-9.33z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M293.72 181.67c25.13 10.73 59.38 25.35 86.36 36.87"/>
<path d="m381.19 215.2 7.82 7.15-10.57-.71z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M389.01 182.71c-22.34 10.22-51.64 23.63-75.54 34.56"/>
<path d="m315.18 220.34-10.55.97 7.64-7.34z"/>
</g>
<g stroke="#000" class="edge">
<path fill="none" d="M423.34 184.3v24.16"/>
<path d="m426.84 208.38-3.5 10-3.5-10z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 0 B

@ -0,0 +1,3 @@
# :feature:bookmarks:api module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_bookmarks_api.svg)

@ -20,7 +20,7 @@ import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
object BookmarksRoute: NiaNavKey{ object BookmarksRoute : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }

@ -0,0 +1,3 @@
# :feature:bookmarks:impl module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_bookmarks_impl.svg)

@ -35,7 +35,7 @@ import dagger.multibindings.IntoSet
@Module @Module
@InstallIn(ActivityComponent::class) @InstallIn(ActivityComponent::class)
object BookmarksModule { object BookmarksEntryProvider {
@Provides @Provides
@IntoSet @IntoSet
@ -52,7 +52,7 @@ object BookmarksModule {
actionLabel = action, actionLabel = action,
duration = Short, duration = Short,
) == ActionPerformed ) == ActionPerformed
} },
) )
} }
} }

@ -34,7 +34,7 @@ import kotlinx.serialization.modules.PolymorphicModuleBuilder
object BookmarksSerializerModule { object BookmarksSerializerModule {
@Provides @Provides
@IntoSet @IntoSet
fun provideSearchPolymorphicModuleBuilder(): PolymorphicModuleBuilder<@JvmSuppressWildcards NiaNavKey>.() -> Unit = { fun provideBookmarksPolymorphicModuleBuilder(): PolymorphicModuleBuilder<@JvmSuppressWildcards NiaNavKey>.() -> Unit = {
subclass(BookmarksRoute::class, BookmarksRoute.serializer()) subclass(BookmarksRoute::class, BookmarksRoute.serializer())
} }
} }

@ -0,0 +1,3 @@
# :feature:foryou:api module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_foryou_api.svg)

@ -20,7 +20,7 @@ import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
object ForYouRoute: NiaNavKey{ // route to ForYou screen object ForYouRoute : NiaNavKey { // route to ForYou screen
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }

@ -0,0 +1,3 @@
# :feature:foryou:impl module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_foryou_impl.svg)

@ -26,7 +26,6 @@ android {
dependencies { dependencies {
implementation(libs.accompanist.permissions) implementation(libs.accompanist.permissions)
implementation(projects.core.data)
implementation(projects.core.domain) implementation(projects.core.domain)
implementation(projects.core.notifications) implementation(projects.core.notifications)
implementation(projects.feature.foryou.api) implementation(projects.feature.foryou.api)

@ -154,12 +154,12 @@ class ForYouScreenTest {
ForYouScreen( ForYouScreen(
isSyncing = false, isSyncing = false,
onboardingUiState = onboardingUiState =
OnboardingUiState.Shown( OnboardingUiState.Shown(
// Follow one topic // Follow one topic
topics = followableTopicTestData.mapIndexed { index, testTopic -> topics = followableTopicTestData.mapIndexed { index, testTopic ->
testTopic.copy(isFollowed = index == 1) testTopic.copy(isFollowed = index == 1)
}, },
), ),
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = emptyList(), feed = emptyList(),
), ),
@ -201,9 +201,9 @@ class ForYouScreenTest {
ForYouScreen( ForYouScreen(
isSyncing = false, isSyncing = false,
onboardingUiState = onboardingUiState =
OnboardingUiState.Shown( OnboardingUiState.Shown(
topics = followableTopicTestData topics = followableTopicTestData,
), ),
feedState = NewsFeedUiState.Loading, feedState = NewsFeedUiState.Loading,
deepLinkedUserNewsResource = null, deepLinkedUserNewsResource = null,
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },

@ -31,7 +31,7 @@ import dagger.multibindings.IntoSet
@Module @Module
@InstallIn(ActivityComponent::class) @InstallIn(ActivityComponent::class)
object ForYouModule { object ForYouEntryProvider {
/** /**
* The ForYou composable for the app. It can also display information about topics. * The ForYou composable for the app. It can also display information about topics.
* This should be supplied from a separate module. * This should be supplied from a separate module.
@ -43,8 +43,8 @@ object ForYouModule {
): EntryProviderBuilder<NiaNavKey>.() -> Unit = { ): EntryProviderBuilder<NiaNavKey>.() -> Unit = {
entry<ForYouRoute> { entry<ForYouRoute> {
ForYouScreen( ForYouScreen(
onTopicClick = backStack::navigateToTopic onTopicClick = backStack::navigateToTopic,
) )
} }
} }
} }

@ -34,7 +34,7 @@ import kotlinx.serialization.modules.PolymorphicModuleBuilder
object ForYouRouteSerializerModule { object ForYouRouteSerializerModule {
@Provides @Provides
@IntoSet @IntoSet
fun provideSearchPolymorphicModuleBuilder(): PolymorphicModuleBuilder<@JvmSuppressWildcards NiaNavKey>.() -> Unit = { fun provideForYouPolymorphicModuleBuilder(): PolymorphicModuleBuilder<@JvmSuppressWildcards NiaNavKey>.() -> Unit = {
subclass(ForYouRoute::class, ForYouRoute.serializer()) subclass(ForYouRoute::class, ForYouRoute.serializer())
} }
} }

@ -0,0 +1,3 @@
# :feature:interests:api module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_interests_api.svg)

@ -23,7 +23,7 @@ import kotlinx.serialization.Serializable
data class InterestsRoute( data class InterestsRoute(
// The ID of the topic which will be initially selected at this destination // The ID of the topic which will be initially selected at this destination
val initialTopicId: String? = null, val initialTopicId: String? = null,
): NiaNavKey { ) : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = true get() = true
} }

@ -6,14 +6,14 @@
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp" android:width="64dp"
android:height="64dp" android:height="64dp"

@ -18,4 +18,5 @@
<string name="feature_interests_api_title">Interests</string> <string name="feature_interests_api_title">Interests</string>
<string name="feature_interests_api_loading">Loading data</string> <string name="feature_interests_api_loading">Loading data</string>
<string name="feature_interests_api_empty_header">"No available data"</string> <string name="feature_interests_api_empty_header">"No available data"</string>
<string name="feature_interests_api_select_an_interest">Select an Interest</string>
</resources> </resources>

@ -0,0 +1,3 @@
# :feature:interests:impl module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_interests_impl.svg)

@ -24,7 +24,6 @@ android {
} }
dependencies { dependencies {
implementation(projects.core.data)
implementation(projects.core.domain) implementation(projects.core.domain)
implementation(projects.feature.topic.api) implementation(projects.feature.topic.api)
implementation(projects.feature.interests.api) implementation(projects.feature.interests.api)

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.feature.topic.api package com.google.samples.apps.nowinandroid.feature.interests.impl
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -33,9 +33,10 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.feature.interests.api.R
@Composable @Composable
fun TopicDetailPlaceholder(modifier: Modifier = Modifier) { fun InterestsDetailPlaceholder(modifier: Modifier = Modifier) {
Card( Card(
modifier = modifier, modifier = modifier,
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
@ -50,12 +51,12 @@ fun TopicDetailPlaceholder(modifier: Modifier = Modifier) {
), ),
) { ) {
Icon( Icon(
painter = painterResource(id = R.drawable.feature_topic_api_ic_topic_placeholder), painter = painterResource(id = R.drawable.feature_interests_api_ic_detail_placeholder),
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colorScheme.primary, tint = MaterialTheme.colorScheme.primary,
) )
Text( Text(
text = stringResource(id = R.string.feature_topic_api_select_an_interest), text = stringResource(id = R.string.feature_interests_api_select_an_interest),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
) )
} }
@ -66,6 +67,6 @@ fun TopicDetailPlaceholder(modifier: Modifier = Modifier) {
@Composable @Composable
fun TopicDetailPlaceholderPreview() { fun TopicDetailPlaceholderPreview() {
NiaTheme { NiaTheme {
TopicDetailPlaceholder() InterestsDetailPlaceholder()
} }
} }

@ -71,7 +71,7 @@ class InterestsViewModel @AssistedInject constructor(
} }
@AssistedFactory @AssistedFactory
interface Factory{ interface Factory {
fun create(key: InterestsRoute): InterestsViewModel fun create(key: InterestsRoute): InterestsViewModel
} }
} }

@ -16,8 +16,6 @@
package com.google.samples.apps.nowinandroid.feature.interests.impl package com.google.samples.apps.nowinandroid.feature.interests.impl
import android.graphics.Color.RED
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@ -32,11 +30,9 @@ import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.DraggableScrollbar import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.DraggableScrollbar
@ -108,4 +104,4 @@ fun TopicsTabContent(
} }
} }
val LIST_PANE_TEST_TAG = "interests:topics" val LIST_PANE_TEST_TAG = "interests:topics"

@ -24,9 +24,9 @@ import androidx.navigation3.runtime.entry
import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack
import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsDetailPlaceholder
import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsScreen import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsScreen
import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsViewModel import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsViewModel
import com.google.samples.apps.nowinandroid.feature.topic.api.TopicDetailPlaceholder
import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@ -36,18 +36,18 @@ import dagger.multibindings.IntoSet
@Module @Module
@InstallIn(ActivityComponent::class) @InstallIn(ActivityComponent::class)
object InterestsModule { object InterestsEntryProvider {
@OptIn(ExperimentalMaterial3AdaptiveApi::class) @OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Provides @Provides
@IntoSet @IntoSet
fun provideInterestsEntryProviderBuilder( fun provideInterestsEntryProviderBuilder(
backStack: NiaBackStack backStack: NiaBackStack,
): EntryProviderBuilder<NiaNavKey>.() -> Unit = { ): EntryProviderBuilder<NiaNavKey>.() -> Unit = {
entry<InterestsRoute>( entry<InterestsRoute>(
metadata = ListDetailSceneStrategy.listPane { metadata = ListDetailSceneStrategy.listPane {
TopicDetailPlaceholder() InterestsDetailPlaceholder()
} },
) { key -> ) { key ->
val viewModel = hiltViewModel<InterestsViewModel, InterestsViewModel.Factory> { val viewModel = hiltViewModel<InterestsViewModel, InterestsViewModel.Factory> {
it.create(key) it.create(key)
@ -55,8 +55,8 @@ object InterestsModule {
InterestsScreen( InterestsScreen(
onTopicClick = backStack::navigateToTopic, onTopicClick = backStack::navigateToTopic,
shouldHighlightSelectedTopic = false, shouldHighlightSelectedTopic = false,
viewModel = viewModel viewModel = viewModel,
) )
} }
} }
} }

@ -34,7 +34,7 @@ import kotlinx.serialization.modules.PolymorphicModuleBuilder
object InterestsSerializerModule { object InterestsSerializerModule {
@Provides @Provides
@IntoSet @IntoSet
fun provideSearchPolymorphicModuleBuilder(): PolymorphicModuleBuilder<@JvmSuppressWildcards NiaNavKey>.() -> Unit = { fun provideInterestsPolymorphicModuleBuilder(): PolymorphicModuleBuilder<@JvmSuppressWildcards NiaNavKey>.() -> Unit = {
subclass(InterestsRoute::class, InterestsRoute.serializer()) subclass(InterestsRoute::class, InterestsRoute.serializer())
} }
} }

@ -37,8 +37,9 @@ import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepositor
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack
import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackViewModel import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackViewModel
import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import com.google.samples.apps.nowinandroid.feature.interests.api.R
import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.interests.impl.LIST_PANE_TEST_TAG import com.google.samples.apps.nowinandroid.feature.interests.impl.LIST_PANE_TEST_TAG
import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity
@ -47,6 +48,7 @@ import dagger.Provides
import dagger.hilt.EntryPoint import dagger.hilt.EntryPoint
import dagger.hilt.EntryPoints import dagger.hilt.EntryPoints
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication import dagger.hilt.android.testing.HiltTestApplication
@ -64,10 +66,8 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.properties.ReadOnlyProperty
import com.google.samples.apps.nowinandroid.feature.topic.api.R as FeatureTopicR
import dagger.hilt.android.components.ActivityComponent
import kotlin.getValue import kotlin.getValue
import kotlin.properties.ReadOnlyProperty
private const val EXPANDED_WIDTH = "w1200dp-h840dp" private const val EXPANDED_WIDTH = "w1200dp-h840dp"
private const val COMPACT_WIDTH = "w412dp-h915dp" private const val COMPACT_WIDTH = "w412dp-h915dp"
@ -87,7 +87,7 @@ class InterestsListDetailScreenTest {
@EntryPoint @EntryPoint
@InstallIn(ActivityComponent::class) @InstallIn(ActivityComponent::class)
interface EntryProvidersEntryPoint { interface EntryProvidersEntryPoint {
fun getEntryProviders() : Set<@JvmSuppressWildcards EntryProviderBuilder<NiaNavKey>.() -> Unit> fun getEntryProviders(): Set<@JvmSuppressWildcards EntryProviderBuilder<NiaNavKey>.() -> Unit>
} }
@Inject @Inject
@ -99,7 +99,7 @@ class InterestsListDetailScreenTest {
} }
// The strings used for matching in these tests. // The strings used for matching in these tests.
private val placeholderText by composeTestRule.stringResource(FeatureTopicR.string.feature_topic_api_select_an_interest) private val placeholderText by composeTestRule.stringResource(R.string.feature_interests_api_select_an_interest)
private val Topic.testTag private val Topic.testTag
get() = "topic:${this.id}" get() = "topic:${this.id}"
@ -257,10 +257,10 @@ object BackStackProvider {
@Provides @Provides
@Singleton @Singleton
fun provideSerializersModule( fun provideSerializersModule(
polymorphicModuleBuilders: Set<@JvmSuppressWildcards PolymorphicModuleBuilder<NiaNavKey>.() -> Unit> polymorphicModuleBuilders: Set<@JvmSuppressWildcards PolymorphicModuleBuilder<NiaNavKey>.() -> Unit>,
) : SerializersModule = SerializersModule { ): SerializersModule = SerializersModule {
polymorphic(NiaNavKey::class) { polymorphic(NiaNavKey::class) {
polymorphicModuleBuilders.forEach { it() } polymorphicModuleBuilders.forEach { it() }
} }
} }
} }

@ -37,7 +37,6 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import kotlin.intArrayOf
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -73,7 +72,7 @@ class InterestsViewModelTest {
), ),
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
getFollowableTopics = getFollowableTopicsUseCase, getFollowableTopics = getFollowableTopicsUseCase,
InterestsRoute(initialTopicId = testInputTopics[0].topic.id) InterestsRoute(initialTopicId = testInputTopics[0].topic.id),
) )
} }

@ -0,0 +1,3 @@
# :feature:search:api module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_search_api.svg)

@ -21,11 +21,11 @@ import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
object SearchRoute: NiaNavKey { object SearchRoute : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = false get() = false
} }
fun NiaBackStack.navigateToSearch() { fun NiaBackStack.navigateToSearch() {
navigate(SearchRoute) navigate(SearchRoute)
} }

@ -0,0 +1,3 @@
# :feature:search:impl module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_search_impl.svg)

@ -25,7 +25,6 @@ android {
} }
dependencies { dependencies {
implementation(projects.core.data)
implementation(projects.core.domain) implementation(projects.core.domain)
implementation(projects.feature.interests.api) implementation(projects.feature.interests.api)
implementation(projects.feature.search.api) implementation(projects.feature.search.api)

@ -32,7 +32,7 @@ import dagger.multibindings.IntoSet
@Module @Module
@InstallIn(ActivityComponent::class) @InstallIn(ActivityComponent::class)
object SearchModule { object SearchEntryProvider {
@Provides @Provides
@IntoSet @IntoSet
@ -47,4 +47,4 @@ object SearchModule {
) )
} }
} }
} }

@ -0,0 +1,3 @@
# :feature:settings:api module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_settings_api.svg)

@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
~ Copyright 2022 The Android Open Source Project Copyright 2022 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and See the License for the specific language governing permissions and
~ limitations under the License. limitations under the License.
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application> <application>
<activity <activity

@ -0,0 +1,3 @@
# :feature:topic:api module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_topic_api.svg)

@ -22,12 +22,4 @@ plugins {
android { android {
namespace = "com.google.samples.apps.nowinandroid.feature.topic.api" namespace = "com.google.samples.apps.nowinandroid.feature.topic.api"
}
dependencies {
testImplementation(projects.core.testing)
testImplementation(libs.robolectric)
androidTestImplementation(libs.bundles.androidx.compose.ui.test)
androidTestImplementation(projects.core.testing)
} }

@ -21,7 +21,7 @@ import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class TopicRoute(val id: String): NiaNavKey{ data class TopicRoute(val id: String) : NiaNavKey {
override val isTopLevel: Boolean override val isTopLevel: Boolean
get() = false get() = false
} }
@ -30,4 +30,4 @@ fun NiaBackStack.navigateToTopic(
topicId: String, topicId: String,
) { ) {
navigate(TopicRoute(topicId)) navigate(TopicRoute(topicId))
} }

@ -33,7 +33,7 @@ import kotlinx.serialization.modules.PolymorphicModuleBuilder
object TopicSerializerModule { object TopicSerializerModule {
@Provides @Provides
@IntoSet @IntoSet
fun provideSearchPolymorphicModuleBuilder(): PolymorphicModuleBuilder<@JvmSuppressWildcards NiaNavKey>.() -> Unit = { fun provideTopicPolymorphicModuleBuilder(): PolymorphicModuleBuilder<@JvmSuppressWildcards NiaNavKey>.() -> Unit = {
subclass(TopicRoute::class, TopicRoute.serializer()) subclass(TopicRoute::class, TopicRoute.serializer())
} }
} }

@ -16,5 +16,4 @@
--> -->
<resources> <resources>
<string name="feature_topic_api_loading">Loading topic</string> <string name="feature_topic_api_loading">Loading topic</string>
<string name="feature_topic_api_select_an_interest">Select an Interest</string>
</resources> </resources>

@ -0,0 +1,3 @@
# :feature:topic:impl module
## Dependency graph
![Dependency graph](../../../docs/images/graphs/dep_graph_feature_topic_impl.svg)

@ -1,18 +1,18 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* https://www.apache.org/licenses/LICENSE-2.0 * https://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
plugins { plugins {
alias(libs.plugins.nowinandroid.android.feature.impl) alias(libs.plugins.nowinandroid.android.feature.impl)

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.feature.topic.api package com.google.samples.apps.nowinandroid.feature.topic.impl
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.ui.test.hasScrollToNodeAction import androidx.compose.ui.test.hasScrollToNodeAction
@ -26,6 +26,7 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performScrollToNode import androidx.compose.ui.test.performScrollToNode
import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData
import com.google.samples.apps.nowinandroid.core.testing.data.userNewsResourcesTestData import com.google.samples.apps.nowinandroid.core.testing.data.userNewsResourcesTestData
import com.google.samples.apps.nowinandroid.feature.topic.api.R
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.feature.topic.api package com.google.samples.apps.nowinandroid.feature.topic.impl
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation
@ -64,11 +64,12 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
import com.google.samples.apps.nowinandroid.core.ui.R as UiR
import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank
import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider
import com.google.samples.apps.nowinandroid.core.ui.userNewsResourceCardItems import com.google.samples.apps.nowinandroid.core.ui.userNewsResourceCardItems
import com.google.samples.apps.nowinandroid.feature.topic.api.R.string import com.google.samples.apps.nowinandroid.feature.topic.api.R as TopicR
@Composable @Composable
fun TopicScreen( fun TopicScreen(
@ -124,7 +125,7 @@ internal fun TopicScreen(
TopicUiState.Loading -> item { TopicUiState.Loading -> item {
NiaLoadingWheel( NiaLoadingWheel(
modifier = modifier, modifier = modifier,
contentDesc = stringResource(id = string.feature_topic_api_loading), contentDesc = stringResource(id = TopicR.string.feature_topic_api_loading),
) )
} }
@ -292,7 +293,7 @@ private fun TopicToolbar(
Icon( Icon(
imageVector = NiaIcons.ArrowBack, imageVector = NiaIcons.ArrowBack,
contentDescription = stringResource( contentDescription = stringResource(
id = com.google.samples.apps.nowinandroid.core.ui.R.string.core_ui_back, id = UiR.string.core_ui_back,
), ),
) )
} }

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.feature.topic.api package com.google.samples.apps.nowinandroid.feature.topic.impl
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope

@ -25,9 +25,9 @@ import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack
import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey
import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.TopicRoute import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.TopicRoute
import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic
import com.google.samples.apps.nowinandroid.feature.topic.api.TopicScreen import com.google.samples.apps.nowinandroid.feature.topic.impl.TopicScreen
import com.google.samples.apps.nowinandroid.feature.topic.api.TopicViewModel import com.google.samples.apps.nowinandroid.feature.topic.impl.TopicViewModel
import com.google.samples.apps.nowinandroid.feature.topic.api.TopicViewModel.Factory import com.google.samples.apps.nowinandroid.feature.topic.impl.TopicViewModel.Factory
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -36,7 +36,7 @@ import dagger.multibindings.IntoSet
@Module @Module
@InstallIn(ActivityComponent::class) @InstallIn(ActivityComponent::class)
object TopicModule { object TopicEntryProvider {
@OptIn(ExperimentalMaterial3AdaptiveApi::class) @OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Provides @Provides
@ -45,7 +45,7 @@ object TopicModule {
backStack: NiaBackStack, backStack: NiaBackStack,
): EntryProviderBuilder<NiaNavKey>.() -> Unit = { ): EntryProviderBuilder<NiaNavKey>.() -> Unit = {
entry<TopicRoute>( entry<TopicRoute>(
metadata = ListDetailSceneStrategy.detailPane() metadata = ListDetailSceneStrategy.detailPane(),
) { key -> ) { key ->
val id = key.id val id = key.id
TopicScreen( TopicScreen(
@ -60,4 +60,4 @@ object TopicModule {
) )
} }
} }
} }

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.feature.topic.api package com.google.samples.apps.nowinandroid.feature.topic.impl
import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic

@ -0,0 +1,3 @@
# :lint module
## Dependency graph
![Dependency graph](../docs/images/graphs/dep_graph_lint.svg)

@ -0,0 +1,3 @@
# :ui-test-hilt-manifest module
## Dependency graph
![Dependency graph](../docs/images/graphs/dep_graph_ui_test_hilt_manifest.svg)
Loading…
Cancel
Save