Merge pull request #540 from android/mb/topic-chip-nav

Remove topic chip popup menus, route topic click events up to NiaNavHost
pull/559/head
Don Turner 2 years ago committed by GitHub
commit 959a0d27e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -38,7 +38,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.google.accompanist.flowlayout.FlowRow
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaDropdownMenuButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaFilterChip
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBar
@ -167,23 +166,6 @@ fun NiaCatalog() {
}
}
item { Text("Dropdown menus", Modifier.padding(top = 16.dp)) }
item {
FlowRow(mainAxisSpacing = 16.dp) {
NiaDropdownMenuButton(
text = { Text("Enabled") },
items = listOf("Item 1", "Item 2", "Item 3"),
onItemClick = {},
itemText = { item -> Text(item) },
)
NiaDropdownMenuButton(
text = { Text("Disabled") },
items = listOf("Item 1", "Item 2", "Item 3"),
onItemClick = {},
itemText = { item -> Text(item) },
enabled = false,
)
}
}
item { Text("Chips", Modifier.padding(top = 16.dp)) }
item {
FlowRow(mainAxisSpacing = 16.dp) {
@ -315,45 +297,19 @@ fun NiaCatalog() {
item { Text("Tags", Modifier.padding(top = 16.dp)) }
item {
FlowRow(mainAxisSpacing = 16.dp) {
var expandedTopicId by remember { mutableStateOf<String?>(null) }
var firstFollowed by remember { mutableStateOf(false) }
NiaTopicTag(
expanded = expandedTopicId == "Topic 1",
followed = firstFollowed,
onDropdownMenuToggle = { show ->
expandedTopicId = if (show) "Topic 1" else null
},
onFollowClick = { firstFollowed = true },
onUnfollowClick = { firstFollowed = false },
onBrowseClick = {},
followed = true,
onClick = {},
text = { Text(text = "Topic 1".uppercase()) },
followText = { Text(text = "Follow") },
unFollowText = { Text(text = "Unfollow") },
browseText = { Text(text = "Browse topic") },
)
var secondFollowed by remember { mutableStateOf(true) }
NiaTopicTag(
expanded = expandedTopicId == "Topic 2",
followed = secondFollowed,
onDropdownMenuToggle = { show ->
expandedTopicId = if (show) "Topic 2" else null
},
onFollowClick = { secondFollowed = true },
onUnfollowClick = { secondFollowed = false },
onBrowseClick = {},
followed = false,
onClick = {},
text = { Text(text = "Topic 2".uppercase()) },
followText = { Text(text = "Follow") },
unFollowText = { Text(text = "Unfollow") },
browseText = { Text(text = "Browse topic") },
)
NiaTopicTag(
expanded = false,
followed = false,
onDropdownMenuToggle = {},
onFollowClick = {},
onUnfollowClick = {},
onBrowseClick = {},
onClick = {},
text = { Text(text = "Disabled".uppercase()) },
enabled = false,
)

@ -37,7 +37,6 @@ import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen
@Composable
fun NiaNavHost(
navController: NavHostController,
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
startDestination: String = forYouNavigationRoute,
) {
@ -46,14 +45,18 @@ fun NiaNavHost(
startDestination = startDestination,
modifier = modifier,
) {
forYouScreen()
bookmarksScreen()
// TODO: handle topic clicks from each top level destination
forYouScreen(onTopicClick = {})
bookmarksScreen(onTopicClick = {})
interestsGraph(
navigateToTopic = { topicId ->
onTopicClick = { topicId ->
navController.navigateToTopic(topicId)
},
nestedGraphs = {
topicScreen(onBackClick)
topicScreen(
onBackClick = navController::popBackStack,
onTopicClick = {},
)
},
)
}

@ -178,10 +178,7 @@ fun NiaApp(
)
}
NiaNavHost(
navController = appState.navController,
onBackClick = appState::onBackClick,
)
NiaNavHost(appState.navController)
}
// TODO: We may want to add padding or spacer when the snackbar is shown so that

@ -136,10 +136,6 @@ class NiaAppState(
}
}
fun onBackClick() {
navController.popBackStack()
}
fun setShowSettingsDialog(shouldShow: Boolean) {
shouldShowSettingsDialog = shouldShow
}

@ -1,218 +0,0 @@
/*
* Copyright 2022 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.
*/
package com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
/**
* Now in Android dropdown menu button with included trailing icon as well as text label and item
* content slots.
*
* @param items The list of items to display in the menu.
* @param onItemClick Called when the user clicks on a menu item.
* @param modifier Modifier to be applied to the button.
* @param enabled Controls the enabled state of the button. When `false`, this button will not be
* clickable and will appear disabled to accessibility services.
* @param dismissOnItemClick Whether the menu should be dismissed when an item is clicked.
* @param itemText The text label content for a given item.
* @param itemLeadingIcon The leading icon content for a given item.
* @param itemTrailingIcon The trailing icon content for a given item.
*/
@Composable
fun <T> NiaDropdownMenuButton(
items: List<T>,
onItemClick: (item: T) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
dismissOnItemClick: Boolean = true,
text: @Composable () -> Unit,
itemText: @Composable (item: T) -> Unit,
itemLeadingIcon: @Composable ((item: T) -> Unit)? = null,
itemTrailingIcon: @Composable ((item: T) -> Unit)? = null,
) {
var expanded by remember { mutableStateOf(false) }
Box(modifier = modifier) {
OutlinedButton(
onClick = { expanded = true },
enabled = enabled,
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colorScheme.onBackground,
),
border = BorderStroke(
width = NiaDropdownMenuDefaults.DropdownMenuButtonBorderWidth,
color = if (enabled) {
MaterialTheme.colorScheme.outline
} else {
MaterialTheme.colorScheme.onSurface.copy(
alpha = NiaDropdownMenuDefaults.DisabledDropdownMenuButtonBorderAlpha,
)
},
),
contentPadding = NiaDropdownMenuDefaults.DropdownMenuButtonContentPadding,
) {
NiaDropdownMenuButtonContent(
text = text,
trailingIcon = {
Icon(
imageVector = if (expanded) {
NiaIcons.ArrowDropUp
} else {
NiaIcons.ArrowDropDown
},
contentDescription = null,
)
},
)
}
NiaDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
items = items,
onItemClick = onItemClick,
dismissOnItemClick = dismissOnItemClick,
itemText = itemText,
itemLeadingIcon = itemLeadingIcon,
itemTrailingIcon = itemTrailingIcon,
)
}
}
/**
* Internal Now in Android dropdown menu button content layout for arranging the text label and
* trailing icon.
*
* @param text The button text label content.
* @param trailingIcon The button trailing icon content. Default is `null` for no trailing icon.
*/
@Composable
private fun NiaDropdownMenuButtonContent(
text: @Composable () -> Unit,
trailingIcon: @Composable (() -> Unit)? = null,
) {
Box(
Modifier
.padding(
end = if (trailingIcon != null) {
ButtonDefaults.IconSpacing
} else {
0.dp
},
),
) {
ProvideTextStyle(value = MaterialTheme.typography.labelSmall) {
text()
}
}
if (trailingIcon != null) {
Box(Modifier.sizeIn(maxHeight = ButtonDefaults.IconSize)) {
trailingIcon()
}
}
}
/**
* Now in Android dropdown menu with item content slots. Wraps Material 3 [DropdownMenu] and
* [DropdownMenuItem].
*
* @param expanded Whether the menu is currently open and visible to the user.
* @param onDismissRequest Called when the user requests to dismiss the menu, such as by
* tapping outside the menu's bounds.
* @param items The list of items to display in the menu.
* @param onItemClick Called when the user clicks on a menu item.
* @param dismissOnItemClick Whether the menu should be dismissed when an item is clicked.
* @param itemText The text label content for a given item.
* @param itemLeadingIcon The leading icon content for a given item.
* @param itemTrailingIcon The trailing icon content for a given item.
*/
@Composable
fun <T> NiaDropdownMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
items: List<T>,
onItemClick: (item: T) -> Unit,
dismissOnItemClick: Boolean = true,
itemText: @Composable (item: T) -> Unit,
itemLeadingIcon: @Composable ((item: T) -> Unit)? = null,
itemTrailingIcon: @Composable ((item: T) -> Unit)? = null,
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
) {
items.forEach { item ->
DropdownMenuItem(
text = { itemText(item) },
onClick = {
onItemClick(item)
if (dismissOnItemClick) onDismissRequest()
},
leadingIcon = if (itemLeadingIcon != null) {
{ itemLeadingIcon(item) }
} else {
null
},
trailingIcon = if (itemTrailingIcon != null) {
{ itemTrailingIcon(item) }
} else {
null
},
)
}
}
}
/**
* Now in Android dropdown menu default values.
*/
object NiaDropdownMenuDefaults {
// TODO: File bug
// OutlinedButton border color doesn't respect disabled state by default
const val DisabledDropdownMenuButtonBorderAlpha = 0.12f
// TODO: File bug
// OutlinedButton default border width isn't exposed via ButtonDefaults
val DropdownMenuButtonBorderWidth = 1.dp
// TODO: File bug
// Various default button padding values aren't exposed via ButtonDefaults
val DropdownMenuButtonContentPadding =
PaddingValues(
start = 24.dp,
top = 8.dp,
end = 16.dp,
bottom = 8.dp,
)
}

@ -20,28 +20,18 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.google.samples.apps.nowinandroid.core.designsystem.R
@Composable
fun NiaTopicTag(
modifier: Modifier = Modifier,
expanded: Boolean = false,
followed: Boolean,
onDropdownMenuToggle: (show: Boolean) -> Unit = {},
onFollowClick: () -> Unit,
onUnfollowClick: () -> Unit,
onBrowseClick: () -> Unit,
onClick: () -> Unit,
enabled: Boolean = true,
text: @Composable () -> Unit,
followText: @Composable () -> Unit = { Text(stringResource(R.string.follow)) },
unFollowText: @Composable () -> Unit = { Text(stringResource(R.string.unfollow)) },
browseText: @Composable () -> Unit = { Text(stringResource(R.string.browse_topic)) },
) {
Box(modifier = modifier) {
val containerColor = if (followed) {
@ -52,7 +42,7 @@ fun NiaTopicTag(
)
}
TextButton(
onClick = { onDropdownMenuToggle(true) },
onClick = onClick,
enabled = enabled,
colors = ButtonDefaults.textButtonColors(
containerColor = containerColor,
@ -66,25 +56,6 @@ fun NiaTopicTag(
text()
}
}
NiaDropdownMenu(
expanded = expanded,
onDismissRequest = { onDropdownMenuToggle(false) },
items = if (followed) listOf(UNFOLLOW, BROWSE) else listOf(FOLLOW, BROWSE),
onItemClick = { item ->
when (item) {
FOLLOW -> onFollowClick()
UNFOLLOW -> onUnfollowClick()
BROWSE -> onBrowseClick()
}
},
itemText = { item ->
when (item) {
FOLLOW -> followText()
UNFOLLOW -> unFollowText()
BROWSE -> browseText()
}
},
)
}
}
@ -98,7 +69,3 @@ object NiaTagDefaults {
// Button disabled container alpha value not exposed by ButtonDefaults
const val DisabledTopicTagContainerAlpha = 0.12f
}
private const val FOLLOW = 1
private const val UNFOLLOW = 2
private const val BROWSE = 3

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2022 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
http://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.
-->
<resources>
<string name="follow">Follow</string>
<string name="unfollow">Unfollow</string>
<string name="browse_topic">Browse topic</string>
</resources>

@ -41,6 +41,7 @@ class NewsResourceCardTest {
isBookmarked = false,
onToggleBookmark = {},
onClick = {},
onTopicClick = {},
)
dateFormatted = dateFormatted(publishDate = newsWithKnownResourceType.publishDate)
@ -68,6 +69,7 @@ class NewsResourceCardTest {
isBookmarked = false,
onToggleBookmark = {},
onClick = {},
onTopicClick = {},
)
dateFormatted = dateFormatted(publishDate = newsWithUnknownResourceType.publishDate)
@ -81,7 +83,10 @@ class NewsResourceCardTest {
@Test
fun testTopicsChipColorBackground_matchesFollowedState() {
composeTestRule.setContent {
NewsResourceTopics(topics = followableTopicTestData)
NewsResourceTopics(
topics = followableTopicTestData,
onTopicClick = {},
)
}
for (followableTopic in followableTopicTestData) {

@ -47,6 +47,7 @@ import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
fun LazyGridScope.newsFeed(
feedState: NewsFeedUiState,
onNewsResourcesCheckedChanged: (String, Boolean) -> Unit,
onTopicClick: (String) -> Unit,
) {
when (feedState) {
NewsFeedUiState.Loading -> Unit
@ -68,6 +69,7 @@ fun LazyGridScope.newsFeed(
!userNewsResource.isSaved,
)
},
onTopicClick = onTopicClick,
)
}
}
@ -112,6 +114,7 @@ private fun NewsFeedLoadingPreview() {
newsFeed(
feedState = NewsFeedUiState.Loading,
onNewsResourcesCheckedChanged = { _, _ -> },
onTopicClick = {},
)
}
}
@ -129,6 +132,7 @@ private fun NewsFeedContentPreview(
newsFeed(
feedState = NewsFeedUiState.Success(userNewsResources),
onNewsResourcesCheckedChanged = { _, _ -> },
onTopicClick = {},
)
}
}

@ -79,6 +79,7 @@ fun NewsResourceCardExpanded(
isBookmarked: Boolean,
onToggleBookmark: () -> Unit,
onClick: () -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
val clickActionLabel = stringResource(R.string.card_tap_action)
@ -116,7 +117,10 @@ fun NewsResourceCardExpanded(
Spacer(modifier = Modifier.height(12.dp))
NewsResourceShortDescription(userNewsResource.content)
Spacer(modifier = Modifier.height(12.dp))
NewsResourceTopics(userNewsResource.followableTopics)
NewsResourceTopics(
topics = userNewsResource.followableTopics,
onTopicClick = onTopicClick,
)
}
}
}
@ -231,26 +235,17 @@ fun NewsResourceShortDescription(
@Composable
fun NewsResourceTopics(
topics: List<FollowableTopic>,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
// Store the ID of the Topic which has its "following" menu expanded, if any.
// To avoid UI confusion, only one topic can have an expanded menu at a time.
var expandedTopicId by remember { mutableStateOf<String?>(null) }
Row(
modifier = modifier.horizontalScroll(rememberScrollState()), // causes narrow chips
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
for (followableTopic in topics) {
NiaTopicTag(
expanded = expandedTopicId == followableTopic.topic.id,
followed = followableTopic.isFollowed,
onDropdownMenuToggle = { show ->
expandedTopicId = if (show) followableTopic.topic.id else null
},
onFollowClick = { }, // ToDo
onUnfollowClick = { }, // ToDo
onBrowseClick = { }, // ToDo
onClick = { onTopicClick(followableTopic.topic.id) },
text = {
val contentDescription = if (followableTopic.isFollowed) {
stringResource(
@ -308,6 +303,7 @@ private fun ExpandedNewsResourcePreview(
isBookmarked = true,
onToggleBookmark = {},
onClick = {},
onTopicClick = {},
)
}
}

@ -37,6 +37,7 @@ fun LazyListScope.userNewsResourceCardItems(
items: List<UserNewsResource>,
onToggleBookmark: (item: UserNewsResource) -> Unit,
onItemClick: ((item: UserNewsResource) -> Unit)? = null,
onTopicClick: (String) -> Unit,
itemModifier: Modifier = Modifier,
) = items(
items = items,
@ -56,6 +57,7 @@ fun LazyListScope.userNewsResourceCardItems(
else -> onItemClick(userNewsResource)
}
},
onTopicClick = onTopicClick,
modifier = itemModifier,
)
},

@ -50,7 +50,8 @@ class BookmarksScreenTest {
composeTestRule.setContent {
BookmarksScreen(
feedState = NewsFeedUiState.Loading,
removeFromBookmarks = { },
removeFromBookmarks = {},
onTopicClick = {},
)
}
@ -68,7 +69,8 @@ class BookmarksScreenTest {
feedState = NewsFeedUiState.Success(
userNewsResourcesTestData.take(2),
),
removeFromBookmarks = { },
removeFromBookmarks = {},
onTopicClick = {},
)
}
@ -110,6 +112,7 @@ class BookmarksScreenTest {
assertEquals(userNewsResourcesTestData[0].id, newsResourceId)
removeFromBookmarksCalled = true
},
onTopicClick = {},
)
}
@ -138,7 +141,8 @@ class BookmarksScreenTest {
composeTestRule.setContent {
BookmarksScreen(
feedState = NewsFeedUiState.Success(emptyList()),
removeFromBookmarks = { },
removeFromBookmarks = {},
onTopicClick = {},
)
}

@ -66,6 +66,7 @@ import com.google.samples.apps.nowinandroid.core.ui.newsFeed
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
internal fun BookmarksRoute(
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: BookmarksViewModel = hiltViewModel(),
) {
@ -73,6 +74,7 @@ internal fun BookmarksRoute(
BookmarksScreen(
feedState = feedState,
removeFromBookmarks = viewModel::removeFromSavedResources,
onTopicClick = onTopicClick,
modifier = modifier,
)
}
@ -85,12 +87,13 @@ internal fun BookmarksRoute(
internal fun BookmarksScreen(
feedState: NewsFeedUiState,
removeFromBookmarks: (String) -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
when (feedState) {
Loading -> LoadingState(modifier)
is Success -> if (feedState.feed.isNotEmpty()) {
BookmarksGrid(feedState, removeFromBookmarks, modifier)
BookmarksGrid(feedState, removeFromBookmarks, onTopicClick, modifier)
} else {
EmptyState(modifier)
}
@ -112,6 +115,7 @@ private fun LoadingState(modifier: Modifier = Modifier) {
private fun BookmarksGrid(
feedState: NewsFeedUiState,
removeFromBookmarks: (String) -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
val scrollableState = rememberLazyGridState()
@ -129,6 +133,7 @@ private fun BookmarksGrid(
newsFeed(
feedState = feedState,
onNewsResourcesCheckedChanged = { id, _ -> removeFromBookmarks(id) },
onTopicClick = onTopicClick,
)
item(span = { GridItemSpan(maxLineSpan) }) {
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
@ -193,6 +198,7 @@ private fun BookmarksGridPreview(
BookmarksGrid(
feedState = Success(userNewsResources),
removeFromBookmarks = {},
onTopicClick = {},
)
}
}

@ -28,8 +28,8 @@ fun NavController.navigateToBookmarks(navOptions: NavOptions? = null) {
this.navigate(bookmarksRoute, navOptions)
}
fun NavGraphBuilder.bookmarksScreen() {
fun NavGraphBuilder.bookmarksScreen(onTopicClick: (String) -> Unit) {
composable(route = bookmarksRoute) {
BookmarksRoute()
BookmarksRoute(onTopicClick)
}
}

@ -53,6 +53,7 @@ class ForYouScreenTest {
onboardingUiState = OnboardingUiState.Loading,
feedState = NewsFeedUiState.Loading,
onTopicCheckedChanged = { _, _ -> },
onTopicClick = {},
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
)
@ -75,6 +76,7 @@ class ForYouScreenTest {
onboardingUiState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Success(emptyList()),
onTopicCheckedChanged = { _, _ -> },
onTopicClick = {},
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
)
@ -103,6 +105,7 @@ class ForYouScreenTest {
feed = emptyList(),
),
onTopicCheckedChanged = { _, _ -> },
onTopicClick = {},
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
)
@ -146,6 +149,7 @@ class ForYouScreenTest {
feed = emptyList(),
),
onTopicCheckedChanged = { _, _ -> },
onTopicClick = {},
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
)
@ -182,6 +186,7 @@ class ForYouScreenTest {
OnboardingUiState.Shown(topics = followableTopicTestData),
feedState = NewsFeedUiState.Loading,
onTopicCheckedChanged = { _, _ -> },
onTopicClick = {},
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
)
@ -204,6 +209,7 @@ class ForYouScreenTest {
onboardingUiState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Loading,
onTopicCheckedChanged = { _, _ -> },
onTopicClick = {},
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
)
@ -227,6 +233,7 @@ class ForYouScreenTest {
feed = userNewsResourcesTestData,
),
onTopicCheckedChanged = { _, _ -> },
onTopicClick = {},
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
)

@ -92,6 +92,7 @@ import com.google.samples.apps.nowinandroid.core.ui.newsFeed
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
internal fun ForYouRoute(
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: ForYouViewModel = hiltViewModel(),
) {
@ -104,6 +105,7 @@ internal fun ForYouRoute(
onboardingUiState = onboardingUiState,
feedState = feedState,
onTopicCheckedChanged = viewModel::updateTopicSelection,
onTopicClick = onTopicClick,
saveFollowedTopics = viewModel::dismissOnboarding,
onNewsResourcesCheckedChanged = viewModel::updateNewsResourceSaved,
modifier = modifier,
@ -116,6 +118,7 @@ internal fun ForYouScreen(
onboardingUiState: OnboardingUiState,
feedState: NewsFeedUiState,
onTopicCheckedChanged: (String, Boolean) -> Unit,
onTopicClick: (String) -> Unit,
saveFollowedTopics: () -> Unit,
onNewsResourcesCheckedChanged: (String, Boolean) -> Unit,
modifier: Modifier = Modifier,
@ -175,6 +178,7 @@ internal fun ForYouScreen(
newsFeed(
feedState = feedState,
onNewsResourcesCheckedChanged = onNewsResourcesCheckedChanged,
onTopicClick = onTopicClick,
)
item(span = { GridItemSpan(maxLineSpan) }) {
@ -407,6 +411,7 @@ fun ForYouScreenPopulatedFeed(
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
onTopicClick = {},
)
}
}
@ -429,6 +434,7 @@ fun ForYouScreenOfflinePopulatedFeed(
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
onTopicClick = {},
)
}
}
@ -453,6 +459,7 @@ fun ForYouScreenTopicSelection(
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
onTopicClick = {},
)
}
}
@ -470,6 +477,7 @@ fun ForYouScreenLoading() {
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
onTopicClick = {},
)
}
}
@ -492,6 +500,7 @@ fun ForYouScreenPopulatedAndLoading(
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> },
onTopicClick = {},
)
}
}

@ -28,8 +28,8 @@ fun NavController.navigateToForYou(navOptions: NavOptions? = null) {
this.navigate(forYouNavigationRoute, navOptions)
}
fun NavGraphBuilder.forYouScreen() {
fun NavGraphBuilder.forYouScreen(onTopicClick: (String) -> Unit) {
composable(route = forYouNavigationRoute) {
ForYouRoute()
ForYouRoute(onTopicClick)
}
}

@ -109,7 +109,7 @@ class InterestsScreenTest {
InterestsScreen(
uiState = uiState,
followTopic = { _, _ -> },
navigateToTopic = {},
onTopicClick = {},
)
}
}

@ -37,7 +37,7 @@ import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParame
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
internal fun InterestsRoute(
navigateToTopic: (String) -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: InterestsViewModel = hiltViewModel(),
) {
@ -46,7 +46,7 @@ internal fun InterestsRoute(
InterestsScreen(
uiState = uiState,
followTopic = viewModel::followTopic,
navigateToTopic = navigateToTopic,
onTopicClick = onTopicClick,
modifier = modifier,
)
}
@ -55,7 +55,7 @@ internal fun InterestsRoute(
internal fun InterestsScreen(
uiState: InterestsUiState,
followTopic: (String, Boolean) -> Unit,
navigateToTopic: (String) -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
@ -71,7 +71,7 @@ internal fun InterestsScreen(
is InterestsUiState.Interests ->
TopicsTabContent(
topics = uiState.topics,
onTopicClick = navigateToTopic,
onTopicClick = onTopicClick,
onFollowButtonClick = followTopic,
modifier = modifier,
)
@ -98,7 +98,7 @@ fun InterestsScreenPopulated(
topics = followableTopics,
),
followTopic = { _, _ -> },
navigateToTopic = {},
onTopicClick = {},
)
}
}
@ -112,7 +112,7 @@ fun InterestsScreenLoading() {
InterestsScreen(
uiState = InterestsUiState.Loading,
followTopic = { _, _ -> },
navigateToTopic = {},
onTopicClick = {},
)
}
}
@ -126,7 +126,7 @@ fun InterestsScreenEmpty() {
InterestsScreen(
uiState = InterestsUiState.Empty,
followTopic = { _, _ -> },
navigateToTopic = {},
onTopicClick = {},
)
}
}

@ -31,7 +31,7 @@ fun NavController.navigateToInterestsGraph(navOptions: NavOptions? = null) {
}
fun NavGraphBuilder.interestsGraph(
navigateToTopic: (String) -> Unit,
onTopicClick: (String) -> Unit,
nestedGraphs: NavGraphBuilder.() -> Unit,
) {
navigation(
@ -39,9 +39,7 @@ fun NavGraphBuilder.interestsGraph(
startDestination = interestsRoute,
) {
composable(route = interestsRoute) {
InterestsRoute(
navigateToTopic = navigateToTopic,
)
InterestsRoute(onTopicClick)
}
nestedGraphs()
}

@ -55,8 +55,9 @@ class TopicScreenTest {
TopicScreen(
topicUiState = TopicUiState.Loading,
newsUiState = NewsUiState.Loading,
onBackClick = { },
onFollowClick = { },
onBackClick = {},
onFollowClick = {},
onTopicClick = {},
onBookmarkChanged = { _, _ -> },
)
}
@ -73,8 +74,9 @@ class TopicScreenTest {
TopicScreen(
topicUiState = TopicUiState.Success(testTopic),
newsUiState = NewsUiState.Loading,
onBackClick = { },
onFollowClick = { },
onBackClick = {},
onFollowClick = {},
onTopicClick = {},
onBookmarkChanged = { _, _ -> },
)
}
@ -96,8 +98,9 @@ class TopicScreenTest {
TopicScreen(
topicUiState = TopicUiState.Loading,
newsUiState = NewsUiState.Success(userNewsResourcesTestData),
onBackClick = { },
onFollowClick = { },
onBackClick = {},
onFollowClick = {},
onTopicClick = {},
onBookmarkChanged = { _, _ -> },
)
}
@ -117,8 +120,9 @@ class TopicScreenTest {
newsUiState = NewsUiState.Success(
userNewsResourcesTestData,
),
onBackClick = { },
onFollowClick = { },
onBackClick = {},
onFollowClick = {},
onTopicClick = {},
onBookmarkChanged = { _, _ -> },
)
}

@ -65,6 +65,7 @@ import com.google.samples.apps.nowinandroid.feature.topic.TopicUiState.Loading
@Composable
internal fun TopicRoute(
onBackClick: () -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: TopicViewModel = hiltViewModel(),
) {
@ -78,6 +79,7 @@ internal fun TopicRoute(
onBackClick = onBackClick,
onFollowClick = viewModel::followTopicToggle,
onBookmarkChanged = viewModel::bookmarkNews,
onTopicClick = onTopicClick,
)
}
@ -88,6 +90,7 @@ internal fun TopicScreen(
newsUiState: NewsUiState,
onBackClick: () -> Unit,
onFollowClick: (Boolean) -> Unit,
onTopicClick: (String) -> Unit,
onBookmarkChanged: (String, Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
@ -124,6 +127,7 @@ internal fun TopicScreen(
news = newsUiState,
imageUrl = topicUiState.followableTopic.topic.imageUrl,
onBookmarkChanged = onBookmarkChanged,
onTopicClick = onTopicClick,
)
}
}
@ -139,13 +143,14 @@ private fun LazyListScope.TopicBody(
news: NewsUiState,
imageUrl: String,
onBookmarkChanged: (String, Boolean) -> Unit,
onTopicClick: (String) -> Unit,
) {
// TODO: Show icon if available
item {
TopicHeader(name, description, imageUrl)
}
userNewsResourceCards(news, onBookmarkChanged)
userNewsResourceCards(news, onBookmarkChanged, onTopicClick)
}
@Composable
@ -176,12 +181,14 @@ private fun TopicHeader(name: String, description: String, imageUrl: String) {
private fun LazyListScope.userNewsResourceCards(
news: NewsUiState,
onBookmarkChanged: (String, Boolean) -> Unit,
onTopicClick: (String) -> Unit,
) {
when (news) {
is NewsUiState.Success -> {
userNewsResourceCardItems(
items = news.news,
onToggleBookmark = { onBookmarkChanged(it.id, !it.isSaved) },
onTopicClick = onTopicClick,
itemModifier = Modifier.padding(24.dp),
)
}
@ -202,11 +209,12 @@ private fun TopicBodyPreview() {
NiaTheme {
LazyColumn {
TopicBody(
"Jetpack Compose",
"Lorem ipsum maximum",
NewsUiState.Success(emptyList()),
"",
{ _, _ -> },
name = "Jetpack Compose",
description = "Lorem ipsum maximum",
news = NewsUiState.Success(emptyList()),
imageUrl = "",
onBookmarkChanged = { _, _ -> },
onTopicClick = {},
)
}
}
@ -263,6 +271,7 @@ fun TopicScreenPopulated(
onBackClick = {},
onFollowClick = {},
onBookmarkChanged = { _, _ -> },
onTopicClick = {},
)
}
}
@ -279,6 +288,7 @@ fun TopicScreenLoading() {
onBackClick = {},
onFollowClick = {},
onBookmarkChanged = { _, _ -> },
onTopicClick = {},
)
}
}

@ -42,6 +42,7 @@ fun NavController.navigateToTopic(topicId: String) {
fun NavGraphBuilder.topicScreen(
onBackClick: () -> Unit,
onTopicClick: (String) -> Unit,
) {
composable(
route = "topic_route/{$topicIdArg}",
@ -49,6 +50,6 @@ fun NavGraphBuilder.topicScreen(
navArgument(topicIdArg) { type = NavType.StringType },
),
) {
TopicRoute(onBackClick = onBackClick)
TopicRoute(onBackClick = onBackClick, onTopicClick = onTopicClick)
}
}

@ -85,7 +85,6 @@ class DesignSystemDetector : Detector(), Detector.UastScanner {
"TextButton" to "NiaTextButton",
"FilterChip" to "NiaFilterChip",
"ElevatedFilterChip" to "NiaFilterChip",
"DropdownMenu" to "NiaDropdownMenu",
"NavigationBar" to "NiaNavigationBar",
"NavigationBarItem" to "NiaNavigationBarItem",
"NavigationRail" to "NiaNavigationRail",

Loading…
Cancel
Save