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.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.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.UserData
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.map
/**
* A use case responsible for obtaining news resources with their associated bookmarked (also known
* as "saved") state.
*/
class GetSaveableNewsResourcesUseCase @Inject constructor(
class GetUserNewsResourcesUseCase @Inject constructor(
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
* this is empty the list of news resources will not be filtered.
@ -52,18 +48,30 @@ class GetSaveableNewsResourcesUseCase @Inject constructor(
newsRepository.getNewsResources()
} else {
newsRepository.getNewsResources(filterTopicIds = filterTopicIds)
}.mapToSaveableNewsResources(bookmarkedNewsResources)
}.mapToSaveableNewsResources(userDataRepository.userData)
}
private fun Flow<List<NewsResource>>.mapToSaveableNewsResources(
savedNewsResourceIds: Flow<Set<String>>
userData: Flow<UserData>
): Flow<List<UserNewsResource>> =
filterNot { it.isEmpty() }
.combine(savedNewsResourceIds) { newsResources, savedNewsResourceIds ->
.combine(userData) { newsResources, userData ->
newsResources.map { newsResource ->
UserNewsResource(
newsResource = newsResource,
isSaved = savedNewsResourceIds.contains(newsResource.id)
id = 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
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.
*/
data class FollowableTopic(
data class FollowableTopic( //TODO consider changing to UserTopic and flattening
val topic: Topic,
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
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(
val newsResource: NewsResource,
val isSaved: Boolean,
val id: String,
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
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.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
@ -38,7 +39,7 @@ class GetUserNewsResourcesUseCaseTest {
private val newsRepository = TestNewsRepository()
private val userDataRepository = TestUserDataRepository()
val useCase = GetSaveableNewsResourcesUseCase(newsRepository, userDataRepository)
val useCase = GetUserNewsResourcesUseCase(newsRepository, userDataRepository)
@Test
fun whenNoFilters_allNewsResourcesAreReturned() = runTest {
@ -55,9 +56,51 @@ class GetUserNewsResourcesUseCaseTest {
// Check that the correct news resources are returned with their bookmarked state.
assertEquals(
listOf(
UserNewsResource(sampleNewsResources[0], true),
UserNewsResource(sampleNewsResources[1], false),
UserNewsResource(sampleNewsResources[2], true)
UserNewsResource(
sampleNewsResources[0].id,
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()
)
@ -77,7 +120,21 @@ class GetUserNewsResourcesUseCaseTest {
assertEquals(
sampleNewsResources
.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()
)
}

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

@ -38,7 +38,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
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.model.data.previewNewsResources
import com.google.samples.apps.nowinandroid.core.domain.model.previewUserNewsResources
/**
* An extension on [LazyListScope] defining a feed with news resources.
@ -51,21 +51,21 @@ fun LazyGridScope.newsFeed(
when (feedState) {
NewsFeedUiState.Loading -> Unit
is NewsFeedUiState.Success -> {
items(feedState.feed, key = { it.newsResource.id }) { saveableNewsResource ->
items(feedState.feed, key = { it.id }) { userNewsResource ->
val resourceUrl by remember {
mutableStateOf(Uri.parse(saveableNewsResource.newsResource.url))
mutableStateOf(Uri.parse(userNewsResource.url))
}
val context = LocalContext.current
val backgroundColor = MaterialTheme.colorScheme.background.toArgb()
NewsResourceCardExpanded(
newsResource = saveableNewsResource.newsResource,
isBookmarked = saveableNewsResource.isSaved,
userNewsResource = userNewsResource,
isBookmarked = userNewsResource.isSaved,
onClick = { launchCustomChromeTab(context, resourceUrl, backgroundColor) },
onToggleBookmark = {
onNewsResourcesCheckedChanged(
saveableNewsResource.newsResource.id,
!saveableNewsResource.isSaved
userNewsResource.id,
!userNewsResource.isSaved
)
}
)
@ -125,12 +125,7 @@ fun NewsFeedContentPreview() {
LazyVerticalGrid(columns = GridCells.Adaptive(300.dp)) {
newsFeed(
feedState = NewsFeedUiState.Success(
previewNewsResources.map {
UserNewsResource(
it,
false
)
}
previewUserNewsResources
),
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.icon.NiaIcons
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.NewsResourceType
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
import java.time.ZoneId
import java.time.format.DateTimeFormatter
@ -73,7 +75,7 @@ import kotlinx.datetime.toJavaInstant
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NewsResourceCardExpanded(
newsResource: NewsResource,
userNewsResource: UserNewsResource,
isBookmarked: Boolean,
onToggleBookmark: () -> Unit,
onClick: () -> Unit,
@ -91,9 +93,9 @@ fun NewsResourceCardExpanded(
}
) {
Column {
if (!newsResource.headerImageUrl.isNullOrEmpty()) {
if (!userNewsResource.headerImageUrl.isNullOrEmpty()) {
Row {
NewsResourceHeaderImage(newsResource.headerImageUrl)
NewsResourceHeaderImage(userNewsResource.headerImageUrl)
}
}
Box(
@ -103,18 +105,18 @@ fun NewsResourceCardExpanded(
Spacer(modifier = Modifier.height(12.dp))
Row {
NewsResourceTitle(
newsResource.title,
userNewsResource.title,
modifier = Modifier.fillMaxWidth((.8f))
)
Spacer(modifier = Modifier.weight(1f))
BookmarkButton(isBookmarked, onToggleBookmark)
}
Spacer(modifier = Modifier.height(12.dp))
NewsResourceMetaData(newsResource.publishDate, newsResource.type)
NewsResourceMetaData(userNewsResource.publishDate, userNewsResource.type)
Spacer(modifier = Modifier.height(12.dp))
NewsResourceShortDescription(newsResource.content)
NewsResourceShortDescription(userNewsResource.content)
Spacer(modifier = Modifier.height(12.dp))
NewsResourceTopics(newsResource.topics)
NewsResourceTopics(userNewsResource.topics)
}
}
}
@ -227,7 +229,7 @@ fun NewsResourceShortDescription(
@Composable
fun NewsResourceTopics(
topics: List<Topic>,
topics: List<FollowableTopic>,
modifier: Modifier = Modifier
) {
// 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
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
for (topic in topics) {
for (followableTopic in topics) {
NiaTopicTag(
expanded = expandedTopicId == topic.id,
expanded = expandedTopicId == followableTopic.topic.id,
followed = true, // ToDo: Check if topic is followed
onDropdownMenuToggle = { show ->
expandedTopicId = if (show) topic.id else null
expandedTopicId = if (show) followableTopic.topic.id else null
},
onFollowClick = { }, // ToDo
onUnfollowClick = { }, // 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 {
Surface {
NewsResourceCardExpanded(
newsResource = previewNewsResources[0],
userNewsResource = previewUserNewsResources[0],
isBookmarked = true,
onToggleBookmark = {},
onClick = {}

@ -23,6 +23,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
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
/**
@ -37,7 +38,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
*/
fun <T> LazyListScope.newsResourceCardItems(
items: List<T>,
newsResourceMapper: (item: T) -> NewsResource,
newsResourceMapper: (item: T) -> UserNewsResource, //TODO remove this?
isBookmarkedMapper: (item: T) -> Boolean,
onToggleBookmark: (item: T) -> Unit,
onItemClick: ((item: T) -> Unit)? = null,
@ -52,7 +53,7 @@ fun <T> LazyListScope.newsResourceCardItems(
val context = LocalContext.current
NewsResourceCardExpanded(
newsResource = newsResource,
userNewsResource = newsResource,
isBookmarked = isBookmarkedMapper(item),
onToggleBookmark = { onToggleBookmark(item) },
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.theme.NiaTheme
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.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading
@ -184,9 +185,7 @@ private fun BookmarksGridPreview() {
NiaTheme {
BookmarksGrid(
feedState = Success(
previewNewsResources.map {
UserNewsResource(it, false)
}
previewUserNewsResources
),
removeFromBookmarks = {}
)

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

@ -16,7 +16,7 @@
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.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
@ -43,7 +43,7 @@ class BookmarksViewModelTest {
private val userDataRepository = TestUserDataRepository()
private val newsRepository = TestNewsRepository()
private val getSaveableNewsResourcesUseCase = GetSaveableNewsResourcesUseCase(
private val getUserNewsResourcesUseCase = GetUserNewsResourcesUseCase(
newsRepository = newsRepository,
userDataRepository = userDataRepository
)
@ -53,7 +53,7 @@ class BookmarksViewModelTest {
fun setup() {
viewModel = BookmarksViewModel(
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.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.previewNewsResources
import com.google.samples.apps.nowinandroid.core.model.data.previewTopics
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
@ -396,9 +397,7 @@ fun ForYouScreenPopulatedFeed() {
isSyncing = false,
onboardingUiState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map {
UserNewsResource(it, false)
}
feed = previewUserNewsResources
),
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
@ -417,9 +416,7 @@ fun ForYouScreenOfflinePopulatedFeed() {
isSyncing = false,
onboardingUiState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map {
UserNewsResource(it, false)
}
feed = previewUserNewsResources
),
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
@ -440,9 +437,7 @@ fun ForYouScreenTopicSelection() {
topics = previewTopics.map { FollowableTopic(it, false) },
),
feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map {
UserNewsResource(it, false)
}
feed = previewUserNewsResources
),
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
@ -478,9 +473,7 @@ fun ForYouScreenPopulatedAndLoading() {
isSyncing = true,
onboardingUiState = OnboardingUiState.Loading,
feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map {
UserNewsResource(it, false)
}
feed = previewUserNewsResources
),
onTopicCheckedChanged = { _, _ -> },
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.util.SyncStatusMonitor
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.ui.NewsFeedUiState
import dagger.hilt.android.lifecycle.HiltViewModel
@ -41,7 +41,7 @@ import kotlinx.coroutines.launch
class ForYouViewModel @Inject constructor(
syncStatusMonitor: SyncStatusMonitor,
private val userDataRepository: UserDataRepository,
private val getSaveableNewsResources: GetSaveableNewsResourcesUseCase,
private val getSaveableNewsResources: GetUserNewsResourcesUseCase,
getFollowableTopics: GetFollowableTopicsUseCase
) : ViewModel() {

@ -17,7 +17,7 @@
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.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.UserNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
@ -54,7 +54,7 @@ class ForYouViewModelTest {
private val userDataRepository = TestUserDataRepository()
private val topicsRepository = TestTopicsRepository()
private val newsRepository = TestNewsRepository()
private val getSaveableNewsResourcesUseCase = GetSaveableNewsResourcesUseCase(
private val getUserNewsResourcesUseCase = GetUserNewsResourcesUseCase(
newsRepository = newsRepository,
userDataRepository = userDataRepository
)
@ -69,7 +69,7 @@ class ForYouViewModelTest {
viewModel = ForYouViewModel(
syncStatusMonitor = syncStatusMonitor,
userDataRepository = userDataRepository,
getSaveableNewsResources = getSaveableNewsResourcesUseCase,
getSaveableNewsResources = getUserNewsResourcesUseCase,
getFollowableTopics = getFollowableTopicsUseCase
)
}
@ -283,7 +283,19 @@ class ForYouViewModelTest {
feed =
sampleNewsResources.map {
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
)
}
@ -398,11 +410,35 @@ class ForYouViewModelTest {
NewsFeedUiState.Success(
feed = listOf(
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
),
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
)
)
@ -498,13 +534,37 @@ class ForYouViewModelTest {
NewsFeedUiState.Success(
feed = listOf(
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
),
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
)
),
)
),
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.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.previewNewsResources
import com.google.samples.apps.nowinandroid.core.model.data.previewTopics
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
@ -182,9 +183,9 @@ private fun LazyListScope.TopicCards(
is NewsUiState.Success -> {
newsResourceCardItems(
items = news.news,
newsResourceMapper = { it.newsResource },
newsResourceMapper = { it },
isBookmarkedMapper = { it.isSaved },
onToggleBookmark = { onBookmarkChanged(it.newsResource.id, !it.isSaved) },
onToggleBookmark = { onBookmarkChanged(it.id, !it.isSaved) },
itemModifier = Modifier.padding(24.dp)
)
}
@ -257,12 +258,7 @@ fun TopicScreenPopulated() {
TopicScreen(
topicUiState = TopicUiState.Success(FollowableTopic(previewTopics[0], false)),
newsUiState = NewsUiState.Success(
previewNewsResources.mapIndexed { index, newsResource ->
UserNewsResource(
newsResource = newsResource,
isSaved = index % 2 == 0,
)
}
previewUserNewsResources
),
onBackClick = {},
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.UserDataRepository
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.UserNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.Topic
@ -46,7 +46,7 @@ class TopicViewModel @Inject constructor(
private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository,
// newsRepository: NewsRepository,
getSaveableNewsResources: GetSaveableNewsResourcesUseCase
getSaveableNewsResources: GetUserNewsResourcesUseCase
) : ViewModel() {
private val topicArgs: TopicArgs = TopicArgs(savedStateHandle, stringDecoder)
@ -131,7 +131,7 @@ private fun topicUiState(
private fun newsUiState(
topicId: String,
getSaveableNewsResources: GetSaveableNewsResourcesUseCase,
getSaveableNewsResources: GetUserNewsResourcesUseCase,
userDataRepository: UserDataRepository,
): Flow<NewsUiState> {
// Observe news

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

Loading…
Cancel
Save