Add news resource cards to topics screen

Change-Id: Iaab014904460988296ea93cc2088c8da93ed17f1
pull/2/head
Adetunji Dahunsi 3 years ago committed by Don Turner
parent 635abb4dc2
commit 4d72950f94

@ -16,5 +16,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.apps.nowinandroid.core.network">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

@ -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 <T> LazyListScope.newsResourceCardItems(
items: List<T>,
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
)
},
)

@ -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)
)
}
}

@ -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()))
}
}
}

Loading…
Cancel
Save