Renamed SaveableNewsResource to UserNewsResource

pull/507/head
magicalmeghan 2 years ago
parent a3f768a252
commit 464f28a07f

@ -18,29 +18,25 @@ package com.google.samples.apps.nowinandroid.core.domain
import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.UserData
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.map
/** /**
* A use case responsible for obtaining news resources with their associated bookmarked (also known * A use case responsible for obtaining news resources with their associated bookmarked (also known
* as "saved") state. * as "saved") state.
*/ */
class GetSaveableNewsResourcesUseCase @Inject constructor( class GetUserNewsResourcesUseCase @Inject constructor(
private val newsRepository: NewsRepository, private val newsRepository: NewsRepository,
userDataRepository: UserDataRepository private val userDataRepository: UserDataRepository
) { ) {
private val bookmarkedNewsResources = userDataRepository.userData.map {
it.bookmarkedNewsResources
}
/** /**
* Returns a list of SaveableNewsResources which match the supplied set of topic ids. * Returns a list of UserNewsResources which match the supplied set of topic ids.
* *
* @param filterTopicIds - A set of topic ids used to filter the list of news resources. If * @param filterTopicIds - A set of topic ids used to filter the list of news resources. If
* this is empty the list of news resources will not be filtered. * this is empty the list of news resources will not be filtered.
@ -52,18 +48,30 @@ class GetSaveableNewsResourcesUseCase @Inject constructor(
newsRepository.getNewsResources() newsRepository.getNewsResources()
} else { } else {
newsRepository.getNewsResources(filterTopicIds = filterTopicIds) newsRepository.getNewsResources(filterTopicIds = filterTopicIds)
}.mapToSaveableNewsResources(bookmarkedNewsResources) }.mapToSaveableNewsResources(userDataRepository.userData)
} }
private fun Flow<List<NewsResource>>.mapToSaveableNewsResources( private fun Flow<List<NewsResource>>.mapToSaveableNewsResources(
savedNewsResourceIds: Flow<Set<String>> userData: Flow<UserData>
): Flow<List<UserNewsResource>> = ): Flow<List<UserNewsResource>> =
filterNot { it.isEmpty() } filterNot { it.isEmpty() }
.combine(savedNewsResourceIds) { newsResources, savedNewsResourceIds -> .combine(userData) { newsResources, userData ->
newsResources.map { newsResource -> newsResources.map { newsResource ->
UserNewsResource( UserNewsResource(
newsResource = newsResource, id = newsResource.id,
isSaved = savedNewsResourceIds.contains(newsResource.id) title = newsResource.title,
content = newsResource.content,
url = newsResource.url,
headerImageUrl = newsResource.headerImageUrl,
publishDate = newsResource.publishDate,
type = newsResource.type,
topics = newsResource.topics.map { topic ->
FollowableTopic(
topic = topic,
isFollowed = userData.followedTopics.contains(topic.id)
)
},
isSaved = userData.bookmarkedNewsResources.contains(newsResource.id)
) )
} }
} }

@ -17,11 +17,27 @@
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.domain.model
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.model.data.previewTopics
/** /**
* A [topic] with the additional information for whether or not it is followed. * A [topic] with the additional information for whether or not it is followed.
*/ */
data class FollowableTopic( data class FollowableTopic( //TODO consider changing to UserTopic and flattening
val topic: Topic, val topic: Topic,
val isFollowed: Boolean val isFollowed: Boolean
) )
val previewFollowableTopics = listOf(
FollowableTopic(
previewTopics[0],
isFollowed = false
),
FollowableTopic(
previewTopics[1],
isFollowed = true
),
FollowableTopic(
previewTopics[2],
isFollowed = false
)
)

@ -17,11 +17,89 @@
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.domain.model
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Codelab
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Unknown
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
import com.google.samples.apps.nowinandroid.core.model.data.previewTopics
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
/** /**
* A [NewsResource] with the additional information for whether it is saved. * A [NewsResource] with the additional user information.
*/ */
data class UserNewsResource( data class UserNewsResource(
val newsResource: NewsResource, val id: String,
val isSaved: Boolean, val title: String,
val content: String,
val url: String,
val headerImageUrl: String?,
val publishDate: Instant,
val type: NewsResourceType,
val topics: List<FollowableTopic>,
val isSaved: Boolean
)
val previewUserNewsResources = listOf(
UserNewsResource(
id = "1",
title = "Android Basics with Compose",
content = "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. Youll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Androids modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey",
url = "https://android-developers.googleblog.com/2022/05/new-android-basics-with-compose-course.html",
headerImageUrl = "https://developer.android.com/images/hero-assets/android-basics-compose.svg",
publishDate = LocalDateTime(
year = 2022,
monthNumber = 5,
dayOfMonth = 4,
hour = 23,
minute = 0,
second = 0,
nanosecond = 0
).toInstant(TimeZone.UTC),
type = Codelab,
topics = listOf(previewFollowableTopics[1]),
isSaved = true
),
UserNewsResource(
id = "2",
title = "Thanks for helping us reach 1M YouTube Subscribers",
content = "Thank you everyone for following the Now in Android series and everything the " +
"Android Developers YouTube channel has to offer. During the Android Developer " +
"Summit, our YouTube channel reached 1 million subscribers! Heres a small video to " +
"thank you all.",
url = "https://youtu.be/-fJ6poHQrjM",
headerImageUrl = "https://i.ytimg.com/vi/-fJ6poHQrjM/maxresdefault.jpg",
publishDate = Instant.parse("2021-11-09T00:00:00.000Z"),
type = Video,
topics = listOf(previewFollowableTopics[0], previewFollowableTopics[1]),
isSaved = false
),
UserNewsResource(
id = "3",
title = "Transformations and customisations in the Paging Library",
content = "A demonstration of different operations that can be performed " +
"with Paging. Transformations like inserting separators, when to " +
"create a new pager, and customisation options for consuming " +
"PagingData.",
url = "https://youtu.be/ZARz0pjm5YM",
headerImageUrl = "https://i.ytimg.com/vi/ZARz0pjm5YM/maxresdefault.jpg",
publishDate = Instant.parse("2021-11-01T00:00:00.000Z"),
type = Video,
topics = listOf(previewFollowableTopics[2]),
isSaved = false
),
UserNewsResource(
id = "4",
title = "New Jetpack Release",
content = "New Jetpack release includes updates to libraries such as CameraX, Benchmark, and" +
"more!",
url = "https://developer.android.com/jetpack/androidx/versions/all-channel",
headerImageUrl = "",
publishDate = Instant.parse("2022-10-01T00:00:00.000Z"),
type = Unknown,
topics = listOf(previewFollowableTopics[2]),
isSaved = true
)
) )

@ -16,6 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain package com.google.samples.apps.nowinandroid.core.domain
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
@ -38,7 +39,7 @@ class GetUserNewsResourcesUseCaseTest {
private val newsRepository = TestNewsRepository() private val newsRepository = TestNewsRepository()
private val userDataRepository = TestUserDataRepository() private val userDataRepository = TestUserDataRepository()
val useCase = GetSaveableNewsResourcesUseCase(newsRepository, userDataRepository) val useCase = GetUserNewsResourcesUseCase(newsRepository, userDataRepository)
@Test @Test
fun whenNoFilters_allNewsResourcesAreReturned() = runTest { fun whenNoFilters_allNewsResourcesAreReturned() = runTest {
@ -55,9 +56,51 @@ class GetUserNewsResourcesUseCaseTest {
// Check that the correct news resources are returned with their bookmarked state. // Check that the correct news resources are returned with their bookmarked state.
assertEquals( assertEquals(
listOf( listOf(
UserNewsResource(sampleNewsResources[0], true), UserNewsResource(
UserNewsResource(sampleNewsResources[1], false), sampleNewsResources[0].id,
UserNewsResource(sampleNewsResources[2], true) sampleNewsResources[0].title,
sampleNewsResources[0].content,
sampleNewsResources[0].url,
sampleNewsResources[0].headerImageUrl,
sampleNewsResources[0].publishDate,
sampleNewsResources[0].type,
sampleNewsResources[0].topics.map { topic ->
FollowableTopic(
topic = topic,
isFollowed = false
)
},
true),
UserNewsResource(
sampleNewsResources[1].id,
sampleNewsResources[1].title,
sampleNewsResources[1].content,
sampleNewsResources[1].url,
sampleNewsResources[1].headerImageUrl,
sampleNewsResources[1].publishDate,
sampleNewsResources[1].type,
sampleNewsResources[1].topics.map { topic ->
FollowableTopic(
topic = topic,
isFollowed = false
)
},
false),
UserNewsResource(
sampleNewsResources[2].id,
sampleNewsResources[2].title,
sampleNewsResources[2].content,
sampleNewsResources[2].url,
sampleNewsResources[2].headerImageUrl,
sampleNewsResources[2].publishDate,
sampleNewsResources[2].type,
sampleNewsResources[2].topics.map { topic ->
FollowableTopic(
topic = topic,
isFollowed = true
)
},
true),
), ),
saveableNewsResources.first() saveableNewsResources.first()
) )
@ -77,7 +120,21 @@ class GetUserNewsResourcesUseCaseTest {
assertEquals( assertEquals(
sampleNewsResources sampleNewsResources
.filter { it.topics.contains(sampleTopic1) } .filter { it.topics.contains(sampleTopic1) }
.map { UserNewsResource(it, false) }, .map { UserNewsResource(
id = it.id,
title = it.title,
content = it.content,
url = it.url,
headerImageUrl = it.headerImageUrl,
publishDate = it.publishDate,
type = it.type,
topics = it.topics.map { topic ->
FollowableTopic(
topic = topic,
isFollowed = false
)
},
isSaved = false) },
saveableNewsResources.first() saveableNewsResources.first()
) )
} }

@ -35,7 +35,7 @@ class NewsResourceCardTest {
composeTestRule.setContent { composeTestRule.setContent {
NewsResourceCardExpanded( NewsResourceCardExpanded(
newsResource = newsWithKnownResourceType, userNewsResource = newsWithKnownResourceType,
isBookmarked = false, isBookmarked = false,
onToggleBookmark = {}, onToggleBookmark = {},
onClick = {} onClick = {}
@ -62,7 +62,7 @@ class NewsResourceCardTest {
composeTestRule.setContent { composeTestRule.setContent {
NewsResourceCardExpanded( NewsResourceCardExpanded(
newsResource = newsWithUnknownResourceType, userNewsResource = newsWithUnknownResourceType,
isBookmarked = false, isBookmarked = false,
onToggleBookmark = {}, onToggleBookmark = {},
onClick = {} onClick = {}

@ -38,7 +38,7 @@ 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.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources import com.google.samples.apps.nowinandroid.core.domain.model.previewUserNewsResources
/** /**
* An extension on [LazyListScope] defining a feed with news resources. * An extension on [LazyListScope] defining a feed with news resources.
@ -51,21 +51,21 @@ fun LazyGridScope.newsFeed(
when (feedState) { when (feedState) {
NewsFeedUiState.Loading -> Unit NewsFeedUiState.Loading -> Unit
is NewsFeedUiState.Success -> { is NewsFeedUiState.Success -> {
items(feedState.feed, key = { it.newsResource.id }) { saveableNewsResource -> items(feedState.feed, key = { it.id }) { userNewsResource ->
val resourceUrl by remember { val resourceUrl by remember {
mutableStateOf(Uri.parse(saveableNewsResource.newsResource.url)) mutableStateOf(Uri.parse(userNewsResource.url))
} }
val context = LocalContext.current val context = LocalContext.current
val backgroundColor = MaterialTheme.colorScheme.background.toArgb() val backgroundColor = MaterialTheme.colorScheme.background.toArgb()
NewsResourceCardExpanded( NewsResourceCardExpanded(
newsResource = saveableNewsResource.newsResource, userNewsResource = userNewsResource,
isBookmarked = saveableNewsResource.isSaved, isBookmarked = userNewsResource.isSaved,
onClick = { launchCustomChromeTab(context, resourceUrl, backgroundColor) }, onClick = { launchCustomChromeTab(context, resourceUrl, backgroundColor) },
onToggleBookmark = { onToggleBookmark = {
onNewsResourcesCheckedChanged( onNewsResourcesCheckedChanged(
saveableNewsResource.newsResource.id, userNewsResource.id,
!saveableNewsResource.isSaved !userNewsResource.isSaved
) )
} }
) )
@ -125,12 +125,7 @@ fun NewsFeedContentPreview() {
LazyVerticalGrid(columns = GridCells.Adaptive(300.dp)) { LazyVerticalGrid(columns = GridCells.Adaptive(300.dp)) {
newsFeed( newsFeed(
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(
previewNewsResources.map { previewUserNewsResources
UserNewsResource(
it,
false
)
}
), ),
onNewsResourcesCheckedChanged = { _, _ -> } onNewsResourcesCheckedChanged = { _, _ -> }
) )

@ -56,9 +56,11 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconT
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
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.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.previewUserNewsResources
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@ -73,7 +75,7 @@ import kotlinx.datetime.toJavaInstant
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun NewsResourceCardExpanded( fun NewsResourceCardExpanded(
newsResource: NewsResource, userNewsResource: UserNewsResource,
isBookmarked: Boolean, isBookmarked: Boolean,
onToggleBookmark: () -> Unit, onToggleBookmark: () -> Unit,
onClick: () -> Unit, onClick: () -> Unit,
@ -91,9 +93,9 @@ fun NewsResourceCardExpanded(
} }
) { ) {
Column { Column {
if (!newsResource.headerImageUrl.isNullOrEmpty()) { if (!userNewsResource.headerImageUrl.isNullOrEmpty()) {
Row { Row {
NewsResourceHeaderImage(newsResource.headerImageUrl) NewsResourceHeaderImage(userNewsResource.headerImageUrl)
} }
} }
Box( Box(
@ -103,18 +105,18 @@ fun NewsResourceCardExpanded(
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
Row { Row {
NewsResourceTitle( NewsResourceTitle(
newsResource.title, userNewsResource.title,
modifier = Modifier.fillMaxWidth((.8f)) modifier = Modifier.fillMaxWidth((.8f))
) )
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
BookmarkButton(isBookmarked, onToggleBookmark) BookmarkButton(isBookmarked, onToggleBookmark)
} }
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
NewsResourceMetaData(newsResource.publishDate, newsResource.type) NewsResourceMetaData(userNewsResource.publishDate, userNewsResource.type)
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
NewsResourceShortDescription(newsResource.content) NewsResourceShortDescription(userNewsResource.content)
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
NewsResourceTopics(newsResource.topics) NewsResourceTopics(userNewsResource.topics)
} }
} }
} }
@ -227,7 +229,7 @@ fun NewsResourceShortDescription(
@Composable @Composable
fun NewsResourceTopics( fun NewsResourceTopics(
topics: List<Topic>, topics: List<FollowableTopic>,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
// Store the ID of the Topic which has its "following" menu expanded, if any. // Store the ID of the Topic which has its "following" menu expanded, if any.
@ -238,17 +240,17 @@ fun NewsResourceTopics(
modifier = modifier.horizontalScroll(rememberScrollState()), // causes narrow chips modifier = modifier.horizontalScroll(rememberScrollState()), // causes narrow chips
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
) { ) {
for (topic in topics) { for (followableTopic in topics) {
NiaTopicTag( NiaTopicTag(
expanded = expandedTopicId == topic.id, expanded = expandedTopicId == followableTopic.topic.id,
followed = true, // ToDo: Check if topic is followed followed = true, // ToDo: Check if topic is followed
onDropdownMenuToggle = { show -> onDropdownMenuToggle = { show ->
expandedTopicId = if (show) topic.id else null expandedTopicId = if (show) followableTopic.topic.id else null
}, },
onFollowClick = { }, // ToDo onFollowClick = { }, // ToDo
onUnfollowClick = { }, // ToDo onUnfollowClick = { }, // ToDo
onBrowseClick = { }, // ToDo onBrowseClick = { }, // ToDo
text = { Text(text = topic.name.uppercase(Locale.getDefault())) } text = { Text(text = followableTopic.topic.name.uppercase(Locale.getDefault())) }
) )
} }
} }
@ -280,7 +282,7 @@ fun ExpandedNewsResourcePreview() {
NiaTheme { NiaTheme {
Surface { Surface {
NewsResourceCardExpanded( NewsResourceCardExpanded(
newsResource = previewNewsResources[0], userNewsResource = previewUserNewsResources[0],
isBookmarked = true, isBookmarked = true,
onToggleBookmark = {}, onToggleBookmark = {},
onClick = {} onClick = {}

@ -23,6 +23,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
/** /**
@ -37,7 +38,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
*/ */
fun <T> LazyListScope.newsResourceCardItems( fun <T> LazyListScope.newsResourceCardItems(
items: List<T>, items: List<T>,
newsResourceMapper: (item: T) -> NewsResource, newsResourceMapper: (item: T) -> UserNewsResource, //TODO remove this?
isBookmarkedMapper: (item: T) -> Boolean, isBookmarkedMapper: (item: T) -> Boolean,
onToggleBookmark: (item: T) -> Unit, onToggleBookmark: (item: T) -> Unit,
onItemClick: ((item: T) -> Unit)? = null, onItemClick: ((item: T) -> Unit)? = null,
@ -52,7 +53,7 @@ fun <T> LazyListScope.newsResourceCardItems(
val context = LocalContext.current val context = LocalContext.current
NewsResourceCardExpanded( NewsResourceCardExpanded(
newsResource = newsResource, userNewsResource = newsResource,
isBookmarked = isBookmarkedMapper(item), isBookmarked = isBookmarkedMapper(item),
onToggleBookmark = { onToggleBookmark(item) }, onToggleBookmark = { onToggleBookmark(item) },
onClick = { onClick = {

@ -53,6 +53,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel
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.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.previewUserNewsResources
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading
@ -184,9 +185,7 @@ private fun BookmarksGridPreview() {
NiaTheme { NiaTheme {
BookmarksGrid( BookmarksGrid(
feedState = Success( feedState = Success(
previewNewsResources.map { previewUserNewsResources
UserNewsResource(it, false)
}
), ),
removeFromBookmarks = {} removeFromBookmarks = {}
) )

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.GetSaveableNewsResourcesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading
@ -36,7 +36,7 @@ import kotlinx.coroutines.launch
@HiltViewModel @HiltViewModel
class BookmarksViewModel @Inject constructor( class BookmarksViewModel @Inject constructor(
private val userDataRepository: UserDataRepository, private val userDataRepository: UserDataRepository,
getSaveableNewsResources: GetSaveableNewsResourcesUseCase getSaveableNewsResources: GetUserNewsResourcesUseCase
) : ViewModel() { ) : ViewModel() {
val feedUiState: StateFlow<NewsFeedUiState> = getSaveableNewsResources() val feedUiState: StateFlow<NewsFeedUiState> = getSaveableNewsResources()

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.feature.bookmarks package com.google.samples.apps.nowinandroid.feature.bookmarks
import com.google.samples.apps.nowinandroid.core.domain.GetSaveableNewsResourcesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
@ -43,7 +43,7 @@ class BookmarksViewModelTest {
private val userDataRepository = TestUserDataRepository() private val userDataRepository = TestUserDataRepository()
private val newsRepository = TestNewsRepository() private val newsRepository = TestNewsRepository()
private val getSaveableNewsResourcesUseCase = GetSaveableNewsResourcesUseCase( private val getUserNewsResourcesUseCase = GetUserNewsResourcesUseCase(
newsRepository = newsRepository, newsRepository = newsRepository,
userDataRepository = userDataRepository userDataRepository = userDataRepository
) )
@ -53,7 +53,7 @@ class BookmarksViewModelTest {
fun setup() { fun setup() {
viewModel = BookmarksViewModel( viewModel = BookmarksViewModel(
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
getSaveableNewsResources = getSaveableNewsResourcesUseCase getSaveableNewsResources = getUserNewsResourcesUseCase
) )
} }

@ -84,6 +84,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
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.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.previewUserNewsResources
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
import com.google.samples.apps.nowinandroid.core.model.data.previewTopics import com.google.samples.apps.nowinandroid.core.model.data.previewTopics
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
@ -396,9 +397,7 @@ fun ForYouScreenPopulatedFeed() {
isSyncing = false, isSyncing = false,
onboardingUiState = OnboardingUiState.NotShown, onboardingUiState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map { feed = previewUserNewsResources
UserNewsResource(it, false)
}
), ),
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {}, saveFollowedTopics = {},
@ -417,9 +416,7 @@ fun ForYouScreenOfflinePopulatedFeed() {
isSyncing = false, isSyncing = false,
onboardingUiState = OnboardingUiState.NotShown, onboardingUiState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map { feed = previewUserNewsResources
UserNewsResource(it, false)
}
), ),
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {}, saveFollowedTopics = {},
@ -440,9 +437,7 @@ fun ForYouScreenTopicSelection() {
topics = previewTopics.map { FollowableTopic(it, false) }, topics = previewTopics.map { FollowableTopic(it, false) },
), ),
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map { feed = previewUserNewsResources
UserNewsResource(it, false)
}
), ),
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {}, saveFollowedTopics = {},
@ -478,9 +473,7 @@ fun ForYouScreenPopulatedAndLoading() {
isSyncing = true, isSyncing = true,
onboardingUiState = OnboardingUiState.Loading, onboardingUiState = OnboardingUiState.Loading,
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map { feed = previewUserNewsResources
UserNewsResource(it, false)
}
), ),
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {}, saveFollowedTopics = {},

@ -21,7 +21,7 @@ import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.data.util.SyncStatusMonitor import com.google.samples.apps.nowinandroid.core.data.util.SyncStatusMonitor
import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetSaveableNewsResourcesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -41,7 +41,7 @@ import kotlinx.coroutines.launch
class ForYouViewModel @Inject constructor( class ForYouViewModel @Inject constructor(
syncStatusMonitor: SyncStatusMonitor, syncStatusMonitor: SyncStatusMonitor,
private val userDataRepository: UserDataRepository, private val userDataRepository: UserDataRepository,
private val getSaveableNewsResources: GetSaveableNewsResourcesUseCase, private val getSaveableNewsResources: GetUserNewsResourcesUseCase,
getFollowableTopics: GetFollowableTopicsUseCase getFollowableTopics: GetFollowableTopicsUseCase
) : ViewModel() { ) : ViewModel() {

@ -17,7 +17,7 @@
package com.google.samples.apps.nowinandroid.feature.foryou package com.google.samples.apps.nowinandroid.feature.foryou
import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetSaveableNewsResourcesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
@ -54,7 +54,7 @@ class ForYouViewModelTest {
private val userDataRepository = TestUserDataRepository() private val userDataRepository = TestUserDataRepository()
private val topicsRepository = TestTopicsRepository() private val topicsRepository = TestTopicsRepository()
private val newsRepository = TestNewsRepository() private val newsRepository = TestNewsRepository()
private val getSaveableNewsResourcesUseCase = GetSaveableNewsResourcesUseCase( private val getUserNewsResourcesUseCase = GetUserNewsResourcesUseCase(
newsRepository = newsRepository, newsRepository = newsRepository,
userDataRepository = userDataRepository userDataRepository = userDataRepository
) )
@ -69,7 +69,7 @@ class ForYouViewModelTest {
viewModel = ForYouViewModel( viewModel = ForYouViewModel(
syncStatusMonitor = syncStatusMonitor, syncStatusMonitor = syncStatusMonitor,
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
getSaveableNewsResources = getSaveableNewsResourcesUseCase, getSaveableNewsResources = getUserNewsResourcesUseCase,
getFollowableTopics = getFollowableTopicsUseCase getFollowableTopics = getFollowableTopicsUseCase
) )
} }
@ -283,7 +283,19 @@ class ForYouViewModelTest {
feed = feed =
sampleNewsResources.map { sampleNewsResources.map {
UserNewsResource( UserNewsResource(
newsResource = it, id = it.id,
title = it.title,
content = it.content,
url = it.url,
headerImageUrl = it.headerImageUrl,
publishDate = it.publishDate,
type = it.type,
topics = it.topics.map{ topic ->
FollowableTopic(
topic = topic,
isFollowed = false
)
},
isSaved = false isSaved = false
) )
} }
@ -398,11 +410,35 @@ class ForYouViewModelTest {
NewsFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
UserNewsResource( UserNewsResource(
newsResource = sampleNewsResources[1], id = sampleNewsResources[1].id,
title = sampleNewsResources[1].title,
content = sampleNewsResources[1].content,
url = sampleNewsResources[1].url,
headerImageUrl = sampleNewsResources[1].headerImageUrl,
publishDate = sampleNewsResources[1].publishDate,
type = sampleNewsResources[1].type,
topics = sampleNewsResources[1].topics.map{ topic ->
FollowableTopic(
topic = topic,
isFollowed = false
)
},
isSaved = false isSaved = false
), ),
UserNewsResource( UserNewsResource(
newsResource = sampleNewsResources[2], id = sampleNewsResources[2].id,
title = sampleNewsResources[2].title,
content = sampleNewsResources[2].content,
url = sampleNewsResources[2].url,
headerImageUrl = sampleNewsResources[2].headerImageUrl,
publishDate = sampleNewsResources[2].publishDate,
type = sampleNewsResources[2].type,
topics = sampleNewsResources[2].topics.map{ topic ->
FollowableTopic(
topic = topic,
isFollowed = false
)
},
isSaved = false isSaved = false
) )
) )
@ -498,13 +534,37 @@ class ForYouViewModelTest {
NewsFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
UserNewsResource( UserNewsResource(
newsResource = sampleNewsResources[1], id = sampleNewsResources[1].id,
title = sampleNewsResources[1].title,
content = sampleNewsResources[1].content,
url = sampleNewsResources[1].url,
headerImageUrl = sampleNewsResources[1].headerImageUrl,
publishDate = sampleNewsResources[1].publishDate,
type = sampleNewsResources[1].type,
topics = sampleNewsResources[1].topics.map{ topic ->
FollowableTopic(
topic = topic,
isFollowed = false
)
},
isSaved = true isSaved = true
), ),
UserNewsResource( UserNewsResource(
newsResource = sampleNewsResources[2], id = sampleNewsResources[2].id,
title = sampleNewsResources[2].title,
content = sampleNewsResources[2].content,
url = sampleNewsResources[2].url,
headerImageUrl = sampleNewsResources[2].headerImageUrl,
publishDate = sampleNewsResources[2].publishDate,
type = sampleNewsResources[2].type,
topics = sampleNewsResources[2].topics.map{ topic ->
FollowableTopic(
topic = topic,
isFollowed = false
)
},
isSaved = false isSaved = false
) ),
) )
), ),
viewModel.feedState.value viewModel.feedState.value

@ -54,6 +54,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
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.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.previewUserNewsResources
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
import com.google.samples.apps.nowinandroid.core.model.data.previewTopics import com.google.samples.apps.nowinandroid.core.model.data.previewTopics
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
@ -182,9 +183,9 @@ private fun LazyListScope.TopicCards(
is NewsUiState.Success -> { is NewsUiState.Success -> {
newsResourceCardItems( newsResourceCardItems(
items = news.news, items = news.news,
newsResourceMapper = { it.newsResource }, newsResourceMapper = { it },
isBookmarkedMapper = { it.isSaved }, isBookmarkedMapper = { it.isSaved },
onToggleBookmark = { onBookmarkChanged(it.newsResource.id, !it.isSaved) }, onToggleBookmark = { onBookmarkChanged(it.id, !it.isSaved) },
itemModifier = Modifier.padding(24.dp) itemModifier = Modifier.padding(24.dp)
) )
} }
@ -257,12 +258,7 @@ fun TopicScreenPopulated() {
TopicScreen( TopicScreen(
topicUiState = TopicUiState.Success(FollowableTopic(previewTopics[0], false)), topicUiState = TopicUiState.Success(FollowableTopic(previewTopics[0], false)),
newsUiState = NewsUiState.Success( newsUiState = NewsUiState.Success(
previewNewsResources.mapIndexed { index, newsResource -> previewUserNewsResources
UserNewsResource(
newsResource = newsResource,
isSaved = index % 2 == 0,
)
}
), ),
onBackClick = {}, onBackClick = {},
onFollowClick = {}, onFollowClick = {},

@ -22,7 +22,7 @@ import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder
import com.google.samples.apps.nowinandroid.core.domain.GetSaveableNewsResourcesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
@ -46,7 +46,7 @@ class TopicViewModel @Inject constructor(
private val userDataRepository: UserDataRepository, private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository, topicsRepository: TopicsRepository,
// newsRepository: NewsRepository, // newsRepository: NewsRepository,
getSaveableNewsResources: GetSaveableNewsResourcesUseCase getSaveableNewsResources: GetUserNewsResourcesUseCase
) : ViewModel() { ) : ViewModel() {
private val topicArgs: TopicArgs = TopicArgs(savedStateHandle, stringDecoder) private val topicArgs: TopicArgs = TopicArgs(savedStateHandle, stringDecoder)
@ -131,7 +131,7 @@ private fun topicUiState(
private fun newsUiState( private fun newsUiState(
topicId: String, topicId: String,
getSaveableNewsResources: GetSaveableNewsResourcesUseCase, getSaveableNewsResources: GetUserNewsResourcesUseCase,
userDataRepository: UserDataRepository, userDataRepository: UserDataRepository,
): Flow<NewsUiState> { ): Flow<NewsUiState> {
// Observe news // Observe news

@ -17,7 +17,7 @@
package com.google.samples.apps.nowinandroid.feature.topic package com.google.samples.apps.nowinandroid.feature.topic
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import com.google.samples.apps.nowinandroid.core.domain.GetSaveableNewsResourcesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
@ -53,7 +53,7 @@ class TopicViewModelTest {
private val userDataRepository = TestUserDataRepository() private val userDataRepository = TestUserDataRepository()
private val topicsRepository = TestTopicsRepository() private val topicsRepository = TestTopicsRepository()
private val newsRepository = TestNewsRepository() private val newsRepository = TestNewsRepository()
private val getSaveableNewsResourcesUseCase = GetSaveableNewsResourcesUseCase( private val getUserNewsResourcesUseCase = GetUserNewsResourcesUseCase(
newsRepository = newsRepository, newsRepository = newsRepository,
userDataRepository = userDataRepository userDataRepository = userDataRepository
) )
@ -66,7 +66,7 @@ class TopicViewModelTest {
stringDecoder = FakeStringDecoder(), stringDecoder = FakeStringDecoder(),
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
topicsRepository = topicsRepository, topicsRepository = topicsRepository,
getSaveableNewsResources = getSaveableNewsResourcesUseCase getSaveableNewsResources = getUserNewsResourcesUseCase
) )
} }

Loading…
Cancel
Save