Merge "[NiA] Extract feed code into core ui so it can be reused for saved tab" into main

pull/205/head
Jolanda Verhoef 2 years ago committed by Gerrit Code Review
commit 1cf4ff585d

@ -17,6 +17,7 @@
package com.google.samples.apps.nowinandroid.core.model.data package com.google.samples.apps.nowinandroid.core.model.data
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Codelab import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Codelab
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
@ -60,5 +61,35 @@ val previewNewsResources = listOf(
).toInstant(TimeZone.UTC), ).toInstant(TimeZone.UTC),
type = Codelab, type = Codelab,
topics = listOf(previewTopics[1]) topics = listOf(previewTopics[1])
),
NewsResource(
id = "2",
episodeId = "52",
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,
authors = listOf(previewAuthors[1]),
topics = listOf(previewTopics[0], previewTopics[1])
),
NewsResource(
id = "3",
episodeId = "52",
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,
authors = listOf(previewAuthors[0], previewAuthors[1]),
topics = listOf(previewTopics[2])
) )
) )

@ -0,0 +1,203 @@
/*
* 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.annotation.IntRange
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
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.model.data.SaveableNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
/**
* An extension on [LazyListScope] defining a feed with news resources.
* Depending on the [feedState], this might emit no items.
*
* @param showLoadingUIIfLoading if true, show a visual indication of loading if the
* [feedState] is loading. This allows a caller to suppress a loading visual if one is already
* present in the UI elsewhere.
*/
fun LazyListScope.NewsFeed(
feedState: NewsFeedUiState,
showLoadingUIIfLoading: Boolean,
@StringRes loadingContentDescription: Int,
@IntRange(from = 1) numberOfColumns: Int,
onNewsResourcesCheckedChanged: (String, Boolean) -> Unit
) {
when (feedState) {
NewsFeedUiState.Loading -> {
if (showLoadingUIIfLoading) {
item {
NiaLoadingWheel(
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(),
contentDesc = stringResource(loadingContentDescription),
)
}
}
}
is NewsFeedUiState.Success -> {
items(
feedState.feed.chunked(numberOfColumns)
) { saveableNewsResources ->
Row(
modifier = Modifier.padding(
top = 32.dp,
start = 16.dp,
end = 16.dp
),
horizontalArrangement = Arrangement.spacedBy(32.dp)
) {
// The last row may not be complete, but for a consistent grid
// structure we still want an element taking up the empty space.
// Therefore, the last row may have empty boxes.
repeat(numberOfColumns) { index ->
Box(
modifier = Modifier.weight(1f)
) {
val saveableNewsResource =
saveableNewsResources.getOrNull(index)
if (saveableNewsResource != null) {
val launchResourceIntent =
Intent(
Intent.ACTION_VIEW,
Uri.parse(saveableNewsResource.newsResource.url)
)
val context = LocalContext.current
NewsResourceCardExpanded(
newsResource = saveableNewsResource.newsResource,
isBookmarked = saveableNewsResource.isSaved,
onClick = {
ContextCompat.startActivity(
context,
launchResourceIntent,
null
)
},
onToggleBookmark = {
onNewsResourcesCheckedChanged(
saveableNewsResource.newsResource.id,
!saveableNewsResource.isSaved
)
}
)
}
}
}
}
}
}
}
}
/**
* A sealed hierarchy describing the state of the feed of news resources.
*/
sealed interface NewsFeedUiState {
/**
* The feed is still loading.
*/
object Loading : NewsFeedUiState
/**
* The feed is loaded with the given list of news resources.
*/
data class Success(
/**
* The list of news resources contained in this feed.
*/
val feed: List<SaveableNewsResource>
) : NewsFeedUiState
}
@Preview
@Composable
fun NewsFeedLoadingPreview() {
NiaTheme {
LazyColumn {
NewsFeed(
feedState = NewsFeedUiState.Loading,
showLoadingUIIfLoading = true,
loadingContentDescription = 0,
numberOfColumns = 1,
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
}
}
@Preview
@Composable
fun NewsFeedSingleColumnPreview() {
NiaTheme {
LazyColumn {
NewsFeed(
feedState = NewsFeedUiState.Success(
previewNewsResources.map {
SaveableNewsResource(it, false)
}
),
showLoadingUIIfLoading = true,
loadingContentDescription = 0,
numberOfColumns = 1,
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
}
}
@Preview(device = Devices.TABLET)
@Composable
fun NewsFeedTwoColumnPreview() {
NiaTheme {
LazyColumn {
NewsFeed(
feedState = NewsFeedUiState.Success(
(previewNewsResources + previewNewsResources).map {
SaveableNewsResource(it, false)
}
),
showLoadingUIIfLoading = true,
loadingContentDescription = 0,
numberOfColumns = 2,
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
}
}

@ -72,7 +72,7 @@ Here's what's happening in each step. The easiest way to find the associated cod
</td> </td>
<td>The initial news feed state is set to <code>Loading</code>, which causes the UI to show a loading spinner on the screen. <td>The initial news feed state is set to <code>Loading</code>, which causes the UI to show a loading spinner on the screen.
</td> </td>
<td>Search for usages of <code>ForYouFeedState.Loading</code> <td>Search for usages of <code>NewsFeedUiState.Loading</code>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -138,7 +138,7 @@ Here's what's happening in each step. The easiest way to find the associated cod
<p> <p>
The screen shows the newly retrieved news resources (as long as the user has chosen at least one topic or author). The screen shows the newly retrieved news resources (as long as the user has chosen at least one topic or author).
</td> </td>
<td>Search for instances of <code>ForYouFeedState.Success</code> <td>Search for instances of <code>NewsFeedUiState.Success</code>
</td> </td>
</tr> </tr>
</table> </table>
@ -260,7 +260,7 @@ UI state is modeled as a sealed hierarchy using interfaces and immutable data cl
**Example: News feed on For You screen** **Example: News feed on For You screen**
The feed (a list) of news resources on the For You screen is modeled using `ForYouFeedState`. This is a sealed interface which creates a hierarchy of two possible states: The feed (a list) of news resources on the For You screen is modeled using `NewsFeedUiState`. This is a sealed interface which creates a hierarchy of two possible states:

@ -41,6 +41,7 @@ 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
import com.google.samples.apps.nowinandroid.core.model.data.SaveableNewsResource 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.model.data.Topic
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import org.junit.Assert import org.junit.Assert
import org.junit.Rule import org.junit.Rule
@ -66,7 +67,7 @@ class ForYouScreenTest {
DpSize(maxWidth, maxHeight) DpSize(maxWidth, maxHeight)
), ),
interestsSelectionState = ForYouInterestsSelectionUiState.Loading, interestsSelectionState = ForYouInterestsSelectionUiState.Loading,
feedState = ForYouFeedUiState.Loading, feedState = NewsFeedUiState.Loading,
onAuthorCheckedChanged = { _, _ -> }, onAuthorCheckedChanged = { _, _ -> },
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {}, saveFollowedTopics = {},
@ -95,7 +96,7 @@ class ForYouScreenTest {
topics = testTopics, topics = testTopics,
authors = testAuthors authors = testAuthors
), ),
feedState = ForYouFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
onAuthorCheckedChanged = { _, _ -> }, onAuthorCheckedChanged = { _, _ -> },
@ -149,7 +150,7 @@ class ForYouScreenTest {
}, },
authors = testAuthors authors = testAuthors
), ),
feedState = ForYouFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
onAuthorCheckedChanged = { _, _ -> }, onAuthorCheckedChanged = { _, _ -> },
@ -203,7 +204,7 @@ class ForYouScreenTest {
testAuthor.copy(isFollowed = index == 1) testAuthor.copy(isFollowed = index == 1)
} }
), ),
feedState = ForYouFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
onAuthorCheckedChanged = { _, _ -> }, onAuthorCheckedChanged = { _, _ -> },
@ -254,7 +255,7 @@ class ForYouScreenTest {
topics = testTopics, topics = testTopics,
authors = testAuthors authors = testAuthors
), ),
feedState = ForYouFeedUiState.Loading, feedState = NewsFeedUiState.Loading,
onAuthorCheckedChanged = { _, _ -> }, onAuthorCheckedChanged = { _, _ -> },
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {}, saveFollowedTopics = {},
@ -289,7 +290,7 @@ class ForYouScreenTest {
DpSize(maxWidth, maxHeight) DpSize(maxWidth, maxHeight)
), ),
interestsSelectionState = ForYouInterestsSelectionUiState.NoInterestsSelection, interestsSelectionState = ForYouInterestsSelectionUiState.NoInterestsSelection,
feedState = ForYouFeedUiState.Loading, feedState = NewsFeedUiState.Loading,
onAuthorCheckedChanged = { _, _ -> }, onAuthorCheckedChanged = { _, _ -> },
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {}, saveFollowedTopics = {},
@ -328,7 +329,7 @@ class ForYouScreenTest {
ForYouScreen( ForYouScreen(
windowSizeClass = windowSizeClass, windowSizeClass = windowSizeClass,
interestsSelectionState = ForYouInterestsSelectionUiState.NoInterestsSelection, interestsSelectionState = ForYouInterestsSelectionUiState.NoInterestsSelection,
feedState = ForYouFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = testNewsResources feed = testNewsResources
), ),
onAuthorCheckedChanged = { _, _ -> }, onAuthorCheckedChanged = { _, _ -> },

@ -1,39 +0,0 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.feature.foryou
import com.google.samples.apps.nowinandroid.core.model.data.SaveableNewsResource
/**
* A sealed hierarchy describing the state of the feed on the for you screen.
*/
sealed interface ForYouFeedUiState {
/**
* The feed is still loading.
*/
object Loading : ForYouFeedUiState
/**
* The feed is loaded with the given list of news resources.
*/
data class Success(
/**
* The list of news resources contained in this [PopulatedFeed].
*/
val feed: List<SaveableNewsResource>
) : ForYouFeedUiState
}

@ -96,6 +96,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.SaveableNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.previewAuthors import com.google.samples.apps.nowinandroid.core.model.data.previewAuthors
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.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.NewsResourceCardExpanded import com.google.samples.apps.nowinandroid.core.ui.NewsResourceCardExpanded
import kotlin.math.floor import kotlin.math.floor
@ -124,7 +125,7 @@ fun ForYouRoute(
fun ForYouScreen( fun ForYouScreen(
windowSizeClass: WindowSizeClass, windowSizeClass: WindowSizeClass,
interestsSelectionState: ForYouInterestsSelectionUiState, interestsSelectionState: ForYouInterestsSelectionUiState,
feedState: ForYouFeedUiState, feedState: NewsFeedUiState,
onTopicCheckedChanged: (String, Boolean) -> Unit, onTopicCheckedChanged: (String, Boolean) -> Unit,
onAuthorCheckedChanged: (String, Boolean) -> Unit, onAuthorCheckedChanged: (String, Boolean) -> Unit,
saveFollowedTopics: () -> Unit, saveFollowedTopics: () -> Unit,
@ -172,7 +173,7 @@ fun ForYouScreen(
// and relates to Time To Full Display. // and relates to Time To Full Display.
val interestsLoaded = val interestsLoaded =
interestsSelectionState !is ForYouInterestsSelectionUiState.Loading interestsSelectionState !is ForYouInterestsSelectionUiState.Loading
val feedLoaded = feedState !is ForYouFeedUiState.Loading val feedLoaded = feedState !is NewsFeedUiState.Loading
if (interestsLoaded && feedLoaded) { if (interestsLoaded && feedLoaded) {
val localView = LocalView.current val localView = LocalView.current
@ -426,13 +427,13 @@ fun TopicIcon(
* states. * states.
*/ */
private fun LazyListScope.Feed( private fun LazyListScope.Feed(
feedState: ForYouFeedUiState, feedState: NewsFeedUiState,
showLoadingUIIfLoading: Boolean, showLoadingUIIfLoading: Boolean,
@IntRange(from = 1) numberOfColumns: Int, @IntRange(from = 1) numberOfColumns: Int,
onNewsResourcesCheckedChanged: (String, Boolean) -> Unit onNewsResourcesCheckedChanged: (String, Boolean) -> Unit
) { ) {
when (feedState) { when (feedState) {
ForYouFeedUiState.Loading -> { NewsFeedUiState.Loading -> {
if (showLoadingUIIfLoading) { if (showLoadingUIIfLoading) {
item { item {
NiaLoadingWheel( NiaLoadingWheel(
@ -444,7 +445,7 @@ private fun LazyListScope.Feed(
} }
} }
} }
is ForYouFeedUiState.Success -> { is NewsFeedUiState.Success -> {
items( items(
feedState.feed.chunked(numberOfColumns) feedState.feed.chunked(numberOfColumns)
) { saveableNewsResources -> ) { saveableNewsResources ->
@ -512,7 +513,7 @@ fun ForYouScreenPopulatedFeed() {
ForYouScreen( ForYouScreen(
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)), windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)),
interestsSelectionState = ForYouInterestsSelectionUiState.NoInterestsSelection, interestsSelectionState = ForYouInterestsSelectionUiState.NoInterestsSelection,
feedState = ForYouFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map { feed = previewNewsResources.map {
SaveableNewsResource(it, false) SaveableNewsResource(it, false)
} }
@ -541,7 +542,7 @@ fun ForYouScreenTopicSelection() {
topics = previewTopics.map { FollowableTopic(it, false) }, topics = previewTopics.map { FollowableTopic(it, false) },
authors = previewAuthors.map { FollowableAuthor(it, false) } authors = previewAuthors.map { FollowableAuthor(it, false) }
), ),
feedState = ForYouFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map { feed = previewNewsResources.map {
SaveableNewsResource(it, false) SaveableNewsResource(it, false)
} }
@ -567,7 +568,7 @@ fun ForYouScreenLoading() {
ForYouScreen( ForYouScreen(
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)), windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)),
interestsSelectionState = ForYouInterestsSelectionUiState.Loading, interestsSelectionState = ForYouInterestsSelectionUiState.Loading,
feedState = ForYouFeedUiState.Loading, feedState = NewsFeedUiState.Loading,
onTopicCheckedChanged = { _, _ -> }, onTopicCheckedChanged = { _, _ -> },
onAuthorCheckedChanged = { _, _ -> }, onAuthorCheckedChanged = { _, _ -> },
saveFollowedTopics = {}, saveFollowedTopics = {},

@ -32,6 +32,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.FollowableAuthor
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.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.SaveableNewsResource import com.google.samples.apps.nowinandroid.core.model.data.SaveableNewsResource
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.feature.foryou.FollowedInterestsState.FollowedInterests import com.google.samples.apps.nowinandroid.feature.foryou.FollowedInterestsState.FollowedInterests
import com.google.samples.apps.nowinandroid.feature.foryou.FollowedInterestsState.None import com.google.samples.apps.nowinandroid.feature.foryou.FollowedInterestsState.None
import com.google.samples.apps.nowinandroid.feature.foryou.FollowedInterestsState.Unknown import com.google.samples.apps.nowinandroid.feature.foryou.FollowedInterestsState.Unknown
@ -103,7 +104,7 @@ class ForYouViewModel @Inject constructor(
mutableStateOf<Set<String>>(emptySet()) mutableStateOf<Set<String>>(emptySet())
} }
val feedState: StateFlow<ForYouFeedUiState> = val feedState: StateFlow<NewsFeedUiState> =
combine( combine(
followedInterestsState, followedInterestsState,
snapshotFlow { inProgressTopicSelection }, snapshotFlow { inProgressTopicSelection },
@ -114,7 +115,7 @@ class ForYouViewModel @Inject constructor(
when (followedInterestsUserState) { when (followedInterestsUserState) {
// If we don't know the current selection state, emit loading. // If we don't know the current selection state, emit loading.
Unknown -> flowOf<ForYouFeedUiState>(ForYouFeedUiState.Loading) Unknown -> flowOf<NewsFeedUiState>(NewsFeedUiState.Loading)
// If the user has followed topics, use those followed topics to populate the feed // If the user has followed topics, use those followed topics to populate the feed
is FollowedInterests -> { is FollowedInterests -> {
newsRepository.getNewsResourcesStream( newsRepository.getNewsResourcesStream(
@ -126,7 +127,7 @@ class ForYouViewModel @Inject constructor(
// on the in-progress interests selections, if there are any. // on the in-progress interests selections, if there are any.
None -> { None -> {
if (inProgressTopicSelection.isEmpty() && inProgressAuthorSelection.isEmpty()) { if (inProgressTopicSelection.isEmpty() && inProgressAuthorSelection.isEmpty()) {
flowOf<ForYouFeedUiState>(ForYouFeedUiState.Success(emptyList())) flowOf<NewsFeedUiState>(NewsFeedUiState.Success(emptyList()))
} else { } else {
newsRepository.getNewsResourcesStream( newsRepository.getNewsResourcesStream(
filterTopicIds = inProgressTopicSelection, filterTopicIds = inProgressTopicSelection,
@ -143,7 +144,7 @@ class ForYouViewModel @Inject constructor(
.stateIn( .stateIn(
scope = viewModelScope, scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000), started = SharingStarted.WhileSubscribed(5_000),
initialValue = ForYouFeedUiState.Loading initialValue = NewsFeedUiState.Loading
) )
val interestsSelectionState: StateFlow<ForYouInterestsSelectionUiState> = val interestsSelectionState: StateFlow<ForYouInterestsSelectionUiState> =
@ -245,7 +246,7 @@ class ForYouViewModel @Inject constructor(
private fun Flow<List<NewsResource>>.mapToFeedState( private fun Flow<List<NewsResource>>.mapToFeedState(
savedNewsResources: Set<String> savedNewsResources: Set<String>
): Flow<ForYouFeedUiState> = ): Flow<NewsFeedUiState> =
filterNot { it.isEmpty() } filterNot { it.isEmpty() }
.map { newsResources -> .map { newsResources ->
newsResources.map { newsResource -> newsResources.map { newsResource ->
@ -255,5 +256,5 @@ private fun Flow<List<NewsResource>>.mapToFeedState(
) )
} }
} }
.map<List<SaveableNewsResource>, ForYouFeedUiState>(ForYouFeedUiState::Success) .map<List<SaveableNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success)
.onStart { emit(ForYouFeedUiState.Loading) } .onStart { emit(NewsFeedUiState.Loading) }

@ -29,6 +29,7 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepo
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher
@ -71,7 +72,7 @@ class ForYouViewModelTest {
ForYouInterestsSelectionUiState.Loading, ForYouInterestsSelectionUiState.Loading,
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals(ForYouFeedUiState.Loading, viewModel.feedState.value) assertEquals(NewsFeedUiState.Loading, viewModel.feedState.value)
} }
@Test @Test
@ -86,7 +87,7 @@ class ForYouViewModelTest {
ForYouInterestsSelectionUiState.Loading, ForYouInterestsSelectionUiState.Loading,
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals(ForYouFeedUiState.Loading, viewModel.feedState.value) assertEquals(NewsFeedUiState.Loading, viewModel.feedState.value)
collectJob1.cancel() collectJob1.cancel()
collectJob2.cancel() collectJob2.cancel()
@ -104,7 +105,7 @@ class ForYouViewModelTest {
ForYouInterestsSelectionUiState.Loading, ForYouInterestsSelectionUiState.Loading,
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals(ForYouFeedUiState.Loading, viewModel.feedState.value) assertEquals(NewsFeedUiState.Loading, viewModel.feedState.value)
collectJob1.cancel() collectJob1.cancel()
collectJob2.cancel() collectJob2.cancel()
@ -122,7 +123,7 @@ class ForYouViewModelTest {
ForYouInterestsSelectionUiState.Loading, ForYouInterestsSelectionUiState.Loading,
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals(ForYouFeedUiState.Success(emptyList()), viewModel.feedState.value) assertEquals(NewsFeedUiState.Success(emptyList()), viewModel.feedState.value)
collectJob1.cancel() collectJob1.cancel()
collectJob2.cancel() collectJob2.cancel()
@ -140,7 +141,7 @@ class ForYouViewModelTest {
ForYouInterestsSelectionUiState.Loading, ForYouInterestsSelectionUiState.Loading,
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals(ForYouFeedUiState.Success(emptyList()), viewModel.feedState.value) assertEquals(NewsFeedUiState.Success(emptyList()), viewModel.feedState.value)
collectJob1.cancel() collectJob1.cancel()
collectJob2.cancel() collectJob2.cancel()
@ -233,7 +234,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
viewModel.feedState.value viewModel.feedState.value
@ -331,7 +332,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
@ -357,7 +358,7 @@ class ForYouViewModelTest {
ForYouInterestsSelectionUiState.NoInterestsSelection, ForYouInterestsSelectionUiState.NoInterestsSelection,
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals(ForYouFeedUiState.Loading, viewModel.feedState.value) assertEquals(NewsFeedUiState.Loading, viewModel.feedState.value)
newsRepository.sendNewsResources(sampleNewsResources) newsRepository.sendNewsResources(sampleNewsResources)
@ -366,7 +367,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = feed =
sampleNewsResources.map { sampleNewsResources.map {
SaveableNewsResource( SaveableNewsResource(
@ -398,7 +399,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Loading, NewsFeedUiState.Loading,
viewModel.feedState.value viewModel.feedState.value
) )
@ -409,7 +410,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = sampleNewsResources.map { feed = sampleNewsResources.map {
SaveableNewsResource( SaveableNewsResource(
newsResource = it, newsResource = it,
@ -512,7 +513,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = emptyList(), feed = emptyList(),
), ),
viewModel.feedState.value viewModel.feedState.value
@ -596,7 +597,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
SaveableNewsResource( SaveableNewsResource(
newsResource = sampleNewsResources[1], newsResource = sampleNewsResources[1],
@ -703,7 +704,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = emptyList(), feed = emptyList(),
), ),
viewModel.feedState.value viewModel.feedState.value
@ -787,7 +788,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
SaveableNewsResource( SaveableNewsResource(
newsResource = sampleNewsResources[1], newsResource = sampleNewsResources[1],
@ -897,7 +898,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
viewModel.feedState.value viewModel.feedState.value
@ -998,7 +999,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
viewModel.feedState.value viewModel.feedState.value
@ -1028,7 +1029,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
SaveableNewsResource( SaveableNewsResource(
newsResource = sampleNewsResources[1], newsResource = sampleNewsResources[1],
@ -1069,7 +1070,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
SaveableNewsResource( SaveableNewsResource(
newsResource = sampleNewsResources[0], newsResource = sampleNewsResources[0],
@ -1107,7 +1108,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
SaveableNewsResource( SaveableNewsResource(
newsResource = sampleNewsResources[1], newsResource = sampleNewsResources[1],
@ -1220,7 +1221,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
viewModel.feedState.value viewModel.feedState.value
@ -1322,7 +1323,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = emptyList() feed = emptyList()
), ),
viewModel.feedState.value viewModel.feedState.value
@ -1350,7 +1351,7 @@ class ForYouViewModelTest {
viewModel.interestsSelectionState.value viewModel.interestsSelectionState.value
) )
assertEquals( assertEquals(
ForYouFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
SaveableNewsResource( SaveableNewsResource(
newsResource = sampleNewsResources[1], newsResource = sampleNewsResources[1],

Loading…
Cancel
Save