diff --git a/core-network/src/main/AndroidManifest.xml b/core-network/src/main/AndroidManifest.xml
index dfab2494f..0d9ff8a0f 100644
--- a/core-network/src/main/AndroidManifest.xml
+++ b/core-network/src/main/AndroidManifest.xml
@@ -16,5 +16,6 @@
-->
+
\ No newline at end of file
diff --git a/core-ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt b/core-ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt
new file mode 100644
index 000000000..25a7bd2dc
--- /dev/null
+++ b/core-ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.ui
+
+import android.content.Intent
+import android.net.Uri
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.items
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.core.content.ContextCompat
+import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
+
+/**
+ * Extension function for displaying a [List] of [NewsResourceCardExpanded] backed by a generic
+ * [List] [T].
+ *
+ * [newsResourceMapper] maps type [T] to a [NewsResource]
+ * [isBookmarkedMapper] maps type [T] to whether the [NewsResource] is bookmarked
+ * [onToggleBookmark] defines the action invoked when a user wishes to bookmark an item
+ * [onItemClick] optional parameter for action to be performed when the card is clicked. The
+ * default action launches an intent matching the card.
+ */
+fun LazyListScope.newsResourceCardItems(
+ items: List,
+ newsResourceMapper: (item: T) -> NewsResource,
+ isBookmarkedMapper: (item: T) -> Boolean,
+ onToggleBookmark: (item: T) -> Unit,
+ onItemClick: ((item: T) -> Unit)? = null,
+ itemModifier: Modifier = Modifier,
+) = items(
+ items = items,
+ key = { newsResourceMapper(it).id },
+ itemContent = { item ->
+ val newsResource = newsResourceMapper(item)
+ val launchResourceIntent =
+ Intent(Intent.ACTION_VIEW, Uri.parse(newsResource.url))
+ val context = LocalContext.current
+
+ NewsResourceCardExpanded(
+ newsResource = newsResource,
+ isBookmarked = isBookmarkedMapper(item),
+ onToggleBookmark = { onToggleBookmark(item) },
+ onClick = {
+ when (onItemClick) {
+ null -> ContextCompat.startActivity(context, launchResourceIntent, null)
+ else -> onItemClick(item)
+ }
+ },
+ modifier = itemModifier
+ )
+ },
+)
diff --git a/feature-foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature-foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt
index cc2650112..a7a3e2cc5 100644
--- a/feature-foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt
+++ b/feature-foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt
@@ -16,8 +16,6 @@
package com.google.samples.apps.nowinandroid.feature.foryou
-import android.content.Intent
-import android.net.Uri
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
@@ -38,7 +36,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells.Fixed
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.items
-import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
@@ -55,7 +52,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -64,7 +60,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import androidx.compose.ui.unit.sp
-import androidx.core.content.ContextCompat.startActivity
import androidx.hilt.navigation.compose.hiltViewModel
import coil.compose.AsyncImage
import com.google.samples.apps.nowinandroid.core.model.data.Author
@@ -75,10 +70,10 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Vid
import com.google.samples.apps.nowinandroid.core.model.data.SaveableNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.ui.LoadingWheel
-import com.google.samples.apps.nowinandroid.core.ui.NewsResourceCardExpanded
import com.google.samples.apps.nowinandroid.core.ui.component.NiaToggleButton
import com.google.samples.apps.nowinandroid.core.ui.component.NiaTopAppBar
import com.google.samples.apps.nowinandroid.core.ui.icon.NiaIcons
+import com.google.samples.apps.nowinandroid.core.ui.newsResourceCardItems
import com.google.samples.apps.nowinandroid.core.ui.theme.NiaTypography
import kotlinx.datetime.Instant
@@ -220,21 +215,18 @@ fun ForYouScreen(
}
}
is ForYouFeedState.Success -> {
- items(feedState.feed) { (newsResource: NewsResource, isBookmarked: Boolean) ->
- val launchResourceIntent =
- Intent(Intent.ACTION_VIEW, Uri.parse(newsResource.url))
- val context = LocalContext.current
-
- NewsResourceCardExpanded(
- newsResource = newsResource,
- isBookmarked = isBookmarked,
- onClick = { startActivity(context, launchResourceIntent, null) },
- onToggleBookmark = {
- onNewsResourcesCheckedChanged(newsResource.id, !isBookmarked)
- },
- modifier = Modifier.padding(24.dp)
- )
- }
+ newsResourceCardItems(
+ items = feedState.feed,
+ newsResourceMapper = SaveableNewsResource::newsResource,
+ isBookmarkedMapper = SaveableNewsResource::isSaved,
+ onToggleBookmark = { saveableNewsResource ->
+ onNewsResourcesCheckedChanged(
+ saveableNewsResource.newsResource.id,
+ !saveableNewsResource.isSaved
+ )
+ },
+ itemModifier = Modifier.padding(24.dp)
+ )
}
}
diff --git a/feature-topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt b/feature-topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt
index bc940ba18..3f7316db1 100644
--- a/feature-topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt
+++ b/feature-topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt
@@ -17,6 +17,7 @@
package com.google.samples.apps.nowinandroid.feature.topic
import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -32,6 +33,7 @@ import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material.icons.Icons.Filled
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.Icon
@@ -52,6 +54,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.ui.LoadingWheel
import com.google.samples.apps.nowinandroid.core.ui.component.NiaFilterChip
+import com.google.samples.apps.nowinandroid.core.ui.newsResourceCardItems
import com.google.samples.apps.nowinandroid.feature.topic.R.string
import com.google.samples.apps.nowinandroid.feature.topic.TopicUiState.Loading
@@ -72,6 +75,7 @@ fun TopicRoute(
)
}
+@OptIn(ExperimentalFoundationApi::class)
@VisibleForTesting
@Composable
internal fun TopicScreen(
@@ -81,31 +85,35 @@ internal fun TopicScreen(
onFollowClick: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
- Column(
+ LazyColumn(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
- Spacer(
- // TODO: Replace with windowInsetsTopHeight after
- // https://issuetracker.google.com/issues/230383055
- Modifier.windowInsetsPadding(
- WindowInsets.safeDrawing.only(WindowInsetsSides.Top)
+ item {
+ Spacer(
+ // TODO: Replace with windowInsetsTopHeight after
+ // https://issuetracker.google.com/issues/230383055
+ Modifier.windowInsetsPadding(
+ WindowInsets.safeDrawing.only(WindowInsetsSides.Top)
+ )
)
- )
-
+ }
when (topicState) {
- Loading ->
+ Loading -> item {
LoadingWheel(
modifier = modifier,
contentDesc = stringResource(id = string.topic_loading),
)
+ }
TopicUiState.Error -> TODO()
is TopicUiState.Success -> {
- TopicToolbar(
- onBackClick = onBackClick,
- onFollowClick = onFollowClick,
- uiState = topicState.followableTopic
- )
+ item {
+ TopicToolbar(
+ onBackClick = onBackClick,
+ onFollowClick = onFollowClick,
+ uiState = topicState.followableTopic,
+ )
+ }
TopicBody(
name = topicState.followableTopic.topic.name,
description = topicState.followableTopic.topic.longDescription,
@@ -113,13 +121,36 @@ internal fun TopicScreen(
)
}
}
+ item {
+ Spacer(
+ // TODO: Replace with windowInsetsBottomHeight after
+ // https://issuetracker.google.com/issues/230383055
+ Modifier.windowInsetsPadding(
+ WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom)
+ )
+ )
+ }
}
}
+private fun LazyListScope.TopicBody(
+ name: String,
+ description: String,
+ news: NewsUiState
+) {
+ // TODO: Show icon if available
+ item {
+ TopicHeader(name, description)
+ }
+
+ TopicCards(news)
+}
+
@Composable
-private fun TopicBody(name: String, description: String, news: NewsUiState) {
- Column(modifier = Modifier.padding(horizontal = 24.dp)) {
- // TODO: Show icon if available
+private fun TopicHeader(name: String, description: String) {
+ Column(
+ modifier = Modifier.padding(horizontal = 24.dp)
+ ) {
Box(
modifier = Modifier
.size(216.dp)
@@ -139,32 +170,24 @@ private fun TopicBody(name: String, description: String, news: NewsUiState) {
style = MaterialTheme.typography.bodyLarge
)
}
- TopicList(news, Modifier.padding(top = 24.dp))
-
- Spacer(
- // TODO: Replace with windowInsetsBottomHeight after
- // https://issuetracker.google.com/issues/230383055
- Modifier.windowInsetsPadding(
- WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom)
- )
- )
}
}
-@Composable
-private fun TopicList(news: NewsUiState, modifier: Modifier = Modifier) {
+private fun LazyListScope.TopicCards(news: NewsUiState) {
when (news) {
is NewsUiState.Success -> {
- LazyColumn(modifier = modifier) {
- items(news.news.size) { index ->
- Text(news.news[index].title)
- }
- }
+ newsResourceCardItems(
+ items = news.news,
+ newsResourceMapper = { it },
+ isBookmarkedMapper = { /* TODO */ false },
+ onToggleBookmark = { /* TODO */ },
+ itemModifier = Modifier.padding(24.dp)
+ )
}
- is NewsUiState.Loading -> {
+ is NewsUiState.Loading -> item {
LoadingWheel(contentDesc = "Loading news") // TODO
}
- else -> {
+ else -> item {
Text("Error") // TODO
}
}
@@ -174,7 +197,9 @@ private fun TopicList(news: NewsUiState, modifier: Modifier = Modifier) {
@Composable
private fun TopicBodyPreview() {
MaterialTheme {
- TopicBody("Jetpack Compose", "Lorem ipsum maximum", NewsUiState.Success(emptyList()))
+ LazyColumn {
+ TopicBody("Jetpack Compose", "Lorem ipsum maximum", NewsUiState.Success(emptyList()))
+ }
}
}