@ -44,12 +44,15 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy
@ -67,6 +70,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.icon.Icon.ImageVec
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors
import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.feature.settings.SettingsDialog
import com.google.samples.apps.nowinandroid.navigation.NiaNavHost
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination
@ -85,6 +89,7 @@ fun NiaApp(
networkMonitor = networkMonitor ,
windowSizeClass = windowSizeClass ,
) ,
userNewsResourceRepository : UserNewsResourceRepository ,
) {
val shouldShowGradientBackground =
appState . currentTopLevelDestination == TopLevelDestination . FOR _YOU
@ -128,8 +133,17 @@ fun NiaApp(
snackbarHost = { SnackbarHost ( snackbarHostState ) } ,
bottomBar = {
if ( appState . shouldShowBottomBar ) {
val forYouNewsResources by userNewsResourceRepository . getUserNewsResourcesForFollowedTopics ( )
. collectAsStateWithLifecycle ( emptyList ( ) )
val unreadDestinations =
when {
forYouNewsResources . all { it . isViewed } -> emptySet ( )
else -> setOf ( TopLevelDestination . FOR _YOU )
}
NiaBottomBar (
destinations = appState . topLevelDestinations ,
destinationsWithUnreadResources = unreadDestinations ,
onNavigateToDestination = appState :: navigateToTopLevelDestination ,
currentDestination = appState . currentDestination ,
modifier = Modifier . testTag ( " NiaBottomBar " ) ,
@ -211,6 +225,7 @@ private fun NiaNavRail(
imageVector = icon . imageVector ,
contentDescription = null ,
)
is DrawableResourceIcon -> Icon (
painter = painterResource ( id = icon . id ) ,
contentDescription = null ,
@ -218,6 +233,7 @@ private fun NiaNavRail(
}
} ,
label = { Text ( stringResource ( destination . iconTextId ) ) } ,
)
}
}
@ -226,6 +242,7 @@ private fun NiaNavRail(
@Composable
private fun NiaBottomBar (
destinations : List < TopLevelDestination > ,
destinationsWithUnreadResources : Set < TopLevelDestination > ,
onNavigateToDestination : ( TopLevelDestination ) -> Unit ,
currentDestination : NavDestination ? ,
modifier : Modifier = Modifier ,
@ -234,6 +251,7 @@ private fun NiaBottomBar(
modifier = modifier ,
) {
destinations . forEach { destination ->
val hasUnread = destinationsWithUnreadResources . contains ( destination )
val selected = currentDestination . isTopLevelDestinationInHierarchy ( destination )
NiaNavigationBarItem (
selected = selected ,
@ -257,6 +275,25 @@ private fun NiaBottomBar(
}
} ,
label = { Text ( stringResource ( destination . iconTextId ) ) } ,
modifier = if ( hasUnread ) {
val tertiaryColor = MaterialTheme . colorScheme . tertiary
Modifier . drawWithContent {
drawContent ( )
drawCircle (
tertiaryColor ,
radius = 5. dp . toPx ( ) ,
// This is based on the dimensions of the NavigationBar's "indicator pill";
// however, its parameters are private, so we must depend on them implicitly
// (NavigationBarTokens.ActiveIndicatorWidth = 64.dp)
center = center + Offset (
64. dp . toPx ( ) * . 45f ,
32. dp . toPx ( ) * - . 45f - 6. dp . toPx ( ) ,
) ,
)
}
} else {
Modifier
} ,
)
}
}