diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8197ad57b..81c128b91 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -86,11 +86,9 @@ dependencies { implementation(project(":feature:settings")) implementation(project(":core:common")) - implementation(project(":core:domain")) implementation(project(":core:ui")) implementation(project(":core:designsystem")) implementation(project(":core:data")) - implementation(project(":core:domain")) implementation(project(":core:model")) implementation(project(":core:analytics")) diff --git a/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationUiTest.kt b/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationUiTest.kt index a0a737237..cd4b40a50 100644 --- a/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationUiTest.kt +++ b/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationUiTest.kt @@ -25,16 +25,14 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import com.google.accompanist.testharness.TestHarness +import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor -import com.google.samples.apps.nowinandroid.core.domain.repository.CompositeUserNewsResourceRepository 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.uitesthiltmanifest.HiltComponentActivity import dagger.hilt.android.testing.BindValue import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.junit.Before import org.junit.Rule import org.junit.Test @@ -69,7 +67,6 @@ class NavigationUiTest { val composeTestRule = createAndroidComposeRule() val userNewsResourceRepository = CompositeUserNewsResourceRepository( - coroutineScope = TestScope(UnconfinedTestDispatcher()), newsRepository = TestNewsRepository(), userDataRepository = TestUserDataRepository(), ) diff --git a/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt b/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt index 64896a544..2457af900 100644 --- a/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt +++ b/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt @@ -30,6 +30,9 @@ import androidx.navigation.compose.ComposeNavigator import androidx.navigation.compose.composable import androidx.navigation.createGraph import androidx.navigation.testing.TestNavHostController +import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository +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.util.TestNetworkMonitor import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -56,6 +59,9 @@ class NiaAppStateTest { // Create the test dependencies. private val networkMonitor = TestNetworkMonitor() + private val userNewsResourceRepository = + CompositeUserNewsResourceRepository(TestNewsRepository(), TestUserDataRepository()) + // Subject under test. private lateinit var state: NiaAppState @@ -67,10 +73,11 @@ class NiaAppStateTest { val navController = rememberTestNavController() state = remember(navController) { NiaAppState( - windowSizeClass = getCompactWindowClass(), navController = navController, - networkMonitor = networkMonitor, coroutineScope = backgroundScope, + windowSizeClass = getCompactWindowClass(), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, ) } @@ -92,6 +99,7 @@ class NiaAppStateTest { state = rememberNiaAppState( windowSizeClass = getCompactWindowClass(), networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, ) } @@ -105,10 +113,11 @@ class NiaAppStateTest { fun niaAppState_showBottomBar_compact() = runTest { composeTestRule.setContent { state = NiaAppState( - windowSizeClass = getCompactWindowClass(), navController = NavHostController(LocalContext.current), - networkMonitor = networkMonitor, coroutineScope = backgroundScope, + windowSizeClass = getCompactWindowClass(), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, ) } @@ -120,10 +129,11 @@ class NiaAppStateTest { fun niaAppState_showNavRail_medium() = runTest { composeTestRule.setContent { state = NiaAppState( - windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(800.dp, 800.dp)), navController = NavHostController(LocalContext.current), - networkMonitor = networkMonitor, coroutineScope = backgroundScope, + windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(800.dp, 800.dp)), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, ) } @@ -135,10 +145,11 @@ class NiaAppStateTest { fun niaAppState_showNavRail_large() = runTest { composeTestRule.setContent { state = NiaAppState( - windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)), navController = NavHostController(LocalContext.current), - networkMonitor = networkMonitor, coroutineScope = backgroundScope, + windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, ) } @@ -150,10 +161,11 @@ class NiaAppStateTest { fun stateIsOfflineWhenNetworkMonitorIsOffline() = runTest(UnconfinedTestDispatcher()) { composeTestRule.setContent { state = NiaAppState( - windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)), navController = NavHostController(LocalContext.current), - networkMonitor = networkMonitor, coroutineScope = backgroundScope, + windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)), + networkMonitor = networkMonitor, + userNewsResourceRepository = userNewsResourceRepository, ) } diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/MainActivity.kt index 200c963b7..79d556f73 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -40,9 +40,9 @@ import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading import com.google.samples.apps.nowinandroid.MainActivityUiState.Success import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper +import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme -import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.ui.NiaApp diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaApp.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaApp.kt index c8648b666..780849cf2 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaApp.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaApp.kt @@ -57,6 +57,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hierarchy import com.google.samples.apps.nowinandroid.R +import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground @@ -70,7 +71,6 @@ import com.google.samples.apps.nowinandroid.core.designsystem.icon.Icon.ImageVec import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors -import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.feature.settings.SettingsDialog import com.google.samples.apps.nowinandroid.navigation.NiaNavHost import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination @@ -85,11 +85,12 @@ import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR fun NiaApp( windowSizeClass: WindowSizeClass, networkMonitor: NetworkMonitor, + userNewsResourceRepository: UserNewsResourceRepository, appState: NiaAppState = rememberNiaAppState( networkMonitor = networkMonitor, windowSizeClass = windowSizeClass, + userNewsResourceRepository = userNewsResourceRepository, ), - userNewsResourceRepository: UserNewsResourceRepository, ) { val shouldShowGradientBackground = appState.currentTopLevelDestination == TopLevelDestination.FOR_YOU @@ -133,14 +134,7 @@ fun NiaApp( snackbarHost = { SnackbarHost(snackbarHostState) }, bottomBar = { if (appState.shouldShowBottomBar) { - val forYouNewsResources by userNewsResourceRepository.getUserNewsResourcesForFollowedTopics() - .collectAsStateWithLifecycle(emptyList()) - val unreadDestinations = - when { - forYouNewsResources.all { it.isViewed } -> emptySet() - else -> setOf(TopLevelDestination.FOR_YOU) - } - + val unreadDestinations by appState.topLevelDestinationsWithUnreadResources.collectAsStateWithLifecycle() NiaBottomBar( destinations = appState.topLevelDestinations, destinationsWithUnreadResources = unreadDestinations, @@ -275,30 +269,31 @@ private fun NiaBottomBar( } }, label = { Text(stringResource(destination.iconTextId)) }, - modifier = if (hasUnread) { - val tertiaryColor = MaterialTheme.colorScheme.tertiary - Modifier.drawWithContent { - drawContent() - drawCircle( - tertiaryColor, - radius = 5.dp.toPx(), - // This is based on the dimensions of the NavigationBar's "indicator pill"; - // however, its parameters are private, so we must depend on them implicitly - // (NavigationBarTokens.ActiveIndicatorWidth = 64.dp) - center = center + Offset( - 64.dp.toPx() * .45f, - 32.dp.toPx() * -.45f - 6.dp.toPx(), - ), - ) - } - } else { - Modifier - }, + modifier = if (hasUnread) notificationDot() else Modifier, ) } } } +@Composable +private fun notificationDot(): Modifier { + val tertiaryColor = MaterialTheme.colorScheme.tertiary + return Modifier.drawWithContent { + drawContent() + drawCircle( + tertiaryColor, + radius = 5.dp.toPx(), + // This is based on the dimensions of the NavigationBar's "indicator pill"; + // however, its parameters are private, so we must depend on them implicitly + // (NavigationBarTokens.ActiveIndicatorWidth = 64.dp) + center = center + Offset( + 64.dp.toPx() * .45f, + 32.dp.toPx() * -.45f - 6.dp.toPx(), + ), + ) + } +} + private fun NavDestination?.isTopLevelDestinationInHierarchy(destination: TopLevelDestination) = this?.hierarchy?.any { it.route?.contains(destination.name, true) ?: false diff --git a/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt index 7f655af21..e472ee2af 100644 --- a/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt +++ b/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt @@ -33,6 +33,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions import androidx.tracing.trace +import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.ui.TrackDisposableJank import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.bookmarksRoute @@ -47,6 +48,8 @@ import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.FOR_Y import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.INTERESTS import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -54,12 +57,25 @@ import kotlinx.coroutines.flow.stateIn fun rememberNiaAppState( windowSizeClass: WindowSizeClass, networkMonitor: NetworkMonitor, + userNewsResourceRepository: UserNewsResourceRepository, coroutineScope: CoroutineScope = rememberCoroutineScope(), navController: NavHostController = rememberNavController(), ): NiaAppState { NavigationTrackingSideEffect(navController) - return remember(navController, coroutineScope, windowSizeClass, networkMonitor) { - NiaAppState(navController, coroutineScope, windowSizeClass, networkMonitor) + return remember( + navController, + coroutineScope, + windowSizeClass, + networkMonitor, + userNewsResourceRepository, + ) { + NiaAppState( + navController, + coroutineScope, + windowSizeClass, + networkMonitor, + userNewsResourceRepository, + ) } } @@ -69,6 +85,7 @@ class NiaAppState( val coroutineScope: CoroutineScope, val windowSizeClass: WindowSizeClass, networkMonitor: NetworkMonitor, + userNewsResourceRepository: UserNewsResourceRepository, ) { val currentDestination: NavDestination? @Composable get() = navController @@ -105,6 +122,22 @@ class NiaAppState( */ val topLevelDestinations: List = TopLevelDestination.values().asList() + /** + * The top level destinations that have unread news resources. + */ + val topLevelDestinationsWithUnreadResources: StateFlow> = + userNewsResourceRepository.getUserNewsResourcesForFollowedTopics() + .combine(userNewsResourceRepository.getBookmarkedUserNewsResources()) { forYouNewsResources, bookmarkedNewsResources -> + setOfNotNull( + FOR_YOU.takeIf { forYouNewsResources.any { !it.hasBeenViewed } }, + BOOKMARKS.takeIf { bookmarkedNewsResources.any { !it.hasBeenViewed } }, + ) + }.stateIn( + coroutineScope, + SharingStarted.WhileSubscribed(5_000), + initialValue = emptySet(), + ) + /** * UI logic for navigating to a top level destination in the app. Top level destinations have * only one copy of the destination of the back stack, and save and restore state whenever you diff --git a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/di/UserNewsResourceRepositoryModule.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt similarity index 79% rename from core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/di/UserNewsResourceRepositoryModule.kt rename to core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt index 0dd83a852..1a7a80fff 100644 --- a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/di/UserNewsResourceRepositoryModule.kt +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain.di +package com.google.samples.apps.nowinandroid.core.data.di -import com.google.samples.apps.nowinandroid.core.domain.repository.CompositeUserNewsResourceRepository -import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import dagger.Binds import dagger.Module import dagger.hilt.InstallIn diff --git a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt new file mode 100644 index 000000000..dc9ad299f --- /dev/null +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2023 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.data.repository + +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource +import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResources +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +/** + * Implements a [UserNewsResourceRepository] by combining a [NewsRepository] with a + * [UserDataRepository]. + */ +class CompositeUserNewsResourceRepository @Inject constructor( + val newsRepository: NewsRepository, + val userDataRepository: UserDataRepository, +) : UserNewsResourceRepository { + + /** + * Returns available news resources (joined with user data) matching the given query. + */ + override fun getUserNewsResources( + query: NewsResourceQuery, + ): Flow> = + newsRepository.getNewsResources(query) + .combine(userDataRepository.userData) { newsResources, userData -> + newsResources.mapToUserNewsResources(userData) + } + + /** + * Returns available news resources (joined with user data) for the followed topics. + */ + override fun getUserNewsResourcesForFollowedTopics(): Flow> = + userDataRepository.userData.map { it.followedTopics }.distinctUntilChanged() + .flatMapLatest { followedTopics -> + when { + followedTopics.isEmpty() -> flowOf(emptyList()) + else -> getUserNewsResources(NewsResourceQuery(filterTopicIds = followedTopics)) + } + } + + override fun getBookmarkedUserNewsResources(): Flow> = + userDataRepository.userData.map { it.bookmarkedNewsResources }.distinctUntilChanged() + .flatMapLatest { bookmarkedNewsResources -> + when { + bookmarkedNewsResources.isEmpty() -> flowOf(emptyList()) + else -> getUserNewsResources(NewsResourceQuery(filterNewsIds = bookmarkedNewsResources)) + } + } +} diff --git a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt index f10046f73..2559362ba 100644 --- a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt @@ -50,8 +50,8 @@ class OfflineFirstUserDataRepository @Inject constructor( ) } - override suspend fun updateNewsResourceViewed(newsResourceId: String, viewed: Boolean) = - niaPreferencesDataSource.toggleNewsResourceViewed(newsResourceId, viewed) + override suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) = + niaPreferencesDataSource.setNewsResourceViewed(newsResourceId, viewed) override suspend fun setThemeBrand(themeBrand: ThemeBrand) { niaPreferencesDataSource.setThemeBrand(themeBrand) diff --git a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt index 2ce84a963..5e0e7ebfc 100644 --- a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt @@ -46,7 +46,7 @@ interface UserDataRepository { /** * Updates the viewed status for a news resource */ - suspend fun updateNewsResourceViewed(newsResourceId: String, viewed: Boolean) + suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) /** * Sets the desired theme brand. diff --git a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/repository/UserNewsResourceRepository.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt similarity index 83% rename from core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/repository/UserNewsResourceRepository.kt rename to core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt index d81a3d1e0..9f7540da2 100644 --- a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/repository/UserNewsResourceRepository.kt +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain.repository +package com.google.samples.apps.nowinandroid.core.data.repository -import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery -import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.coroutines.flow.Flow /** @@ -38,4 +37,9 @@ interface UserNewsResourceRepository { * Returns available news resources for the user's followed topics as a stream. */ fun getUserNewsResourcesForFollowedTopics(): Flow> + + /** + * + */ + fun getBookmarkedUserNewsResources(): Flow> } diff --git a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeUserDataRepository.kt b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeUserDataRepository.kt index 8b8a1f7f8..74813389e 100644 --- a/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeUserDataRepository.kt +++ b/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeUserDataRepository.kt @@ -47,8 +47,8 @@ class FakeUserDataRepository @Inject constructor( niaPreferencesDataSource.toggleNewsResourceBookmark(newsResourceId, bookmarked) } - override suspend fun updateNewsResourceViewed(newsResourceId: String, viewed: Boolean) = - niaPreferencesDataSource.toggleNewsResourceViewed(newsResourceId, viewed) + override suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) = + niaPreferencesDataSource.setNewsResourceViewed(newsResourceId, viewed) override suspend fun setThemeBrand(themeBrand: ThemeBrand) { niaPreferencesDataSource.setThemeBrand(themeBrand) diff --git a/core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/CompositeUserNewsResourceRepositoryTest.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt similarity index 81% rename from core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/CompositeUserNewsResourceRepositoryTest.kt rename to core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt index 9462cf89e..78271b809 100644 --- a/core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/CompositeUserNewsResourceRepositoryTest.kt +++ b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt @@ -14,20 +14,18 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain +package com.google.samples.apps.nowinandroid.core.data +import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery -import com.google.samples.apps.nowinandroid.core.domain.model.mapToUserNewsResources -import com.google.samples.apps.nowinandroid.core.domain.repository.CompositeUserNewsResourceRepository 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.Topic +import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResources 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.emptyUserData import kotlinx.coroutines.flow.first -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import org.junit.Test @@ -39,7 +37,6 @@ class CompositeUserNewsResourceRepositoryTest { private val userDataRepository = TestUserDataRepository() private val userNewsResourceRepository = CompositeUserNewsResourceRepository( - coroutineScope = TestScope(UnconfinedTestDispatcher()), newsRepository = newsRepository, userDataRepository = userDataRepository, ) @@ -71,7 +68,13 @@ class CompositeUserNewsResourceRepositoryTest { fun whenFilteredByTopicId_matchingNewsResourcesAreReturned() = runTest { // Obtain a stream of user news resources for the given topic id. val userNewsResources = - userNewsResourceRepository.getUserNewsResources(NewsResourceQuery(filterTopicIds = setOf(sampleTopic1.id))) + userNewsResourceRepository.getUserNewsResources( + NewsResourceQuery( + filterTopicIds = setOf( + sampleTopic1.id, + ), + ), + ) // Send test data into the repositories. newsRepository.sendNewsResources(sampleNewsResources) @@ -107,6 +110,29 @@ class CompositeUserNewsResourceRepositoryTest { userNewsResources.first(), ) } + + @Test + fun whenFilteredByBookmarkedResources_matchingNewsResourcesAreReturned() = runTest { + // Obtain the bookmarked user news resources flow. + val userNewsResources = userNewsResourceRepository.getBookmarkedUserNewsResources() + + // Send some news resources and user data into the data repositories. + newsRepository.sendNewsResources(sampleNewsResources) + + // Construct the test user data with bookmarks and followed topics. + val userData = emptyUserData.copy( + bookmarkedNewsResources = setOf(sampleNewsResources[0].id, sampleNewsResources[2].id), + followedTopics = setOf(sampleTopic1.id), + ) + + userDataRepository.setUserData(userData) + + // Check that the correct news resources are returned with their bookmarked state. + assertEquals( + listOf(sampleNewsResources[0], sampleNewsResources[2]).mapToUserNewsResources(userData), + userNewsResources.first(), + ) + } } private val sampleTopic1 = Topic( diff --git a/core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/UserNewsResourceTest.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt similarity index 95% rename from core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/UserNewsResourceTest.kt rename to core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt index 7931d3f80..004966ec9 100644 --- a/core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/UserNewsResourceTest.kt +++ b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain +package com.google.samples.apps.nowinandroid.core.data -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.DarkThemeConfig.FOLLOW_SYSTEM +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.NewsResourceType.Article import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.UserData +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.datetime.Clock import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue diff --git a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt index 994ae71b5..952f667f7 100644 --- a/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt +++ b/core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt @@ -164,7 +164,7 @@ class OfflineFirstUserDataRepositoryTest { @Test fun offlineFirstUserDataRepository_update_viewed_news_resources_delegates_to_nia_preferences() = runTest { - subject.updateNewsResourceViewed(newsResourceId = "0", viewed = true) + subject.setNewsResourceViewed(newsResourceId = "0", viewed = true) assertEquals( setOf("0"), @@ -173,7 +173,7 @@ class OfflineFirstUserDataRepositoryTest { .first(), ) - subject.updateNewsResourceViewed(newsResourceId = "1", viewed = true) + subject.setNewsResourceViewed(newsResourceId = "1", viewed = true) assertEquals( setOf("0", "1"), diff --git a/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt b/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt index 91f8a3df2..33c04b70d 100644 --- a/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt +++ b/core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt @@ -138,7 +138,7 @@ class NiaPreferencesDataSource @Inject constructor( } } - suspend fun toggleNewsResourceViewed(newsResourceId: String, viewed: Boolean) { + suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { userPreferences.updateData { it.copy { if (viewed) { diff --git a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCase.kt b/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCase.kt index ccc7e4ee1..c3c045d44 100644 --- a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCase.kt +++ b/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCase.kt @@ -20,7 +20,7 @@ import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepositor import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.TopicSortField.NAME import com.google.samples.apps.nowinandroid.core.domain.TopicSortField.NONE -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import javax.inject.Inject diff --git a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/di/CoroutineScopesModule.kt b/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/di/CoroutineScopesModule.kt deleted file mode 100644 index cfd07e565..000000000 --- a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/di/CoroutineScopesModule.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2023 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.domain.di - -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import javax.inject.Qualifier -import javax.inject.Singleton - -@Retention(AnnotationRetention.RUNTIME) -@Qualifier -annotation class ApplicationScope - -@InstallIn(SingletonComponent::class) -@Module -object CoroutinesScopesModule { - - @Singleton - @ApplicationScope - @Provides - fun providesCoroutineScope(): CoroutineScope = - CoroutineScope(SupervisorJob() + Dispatchers.Default) -} diff --git a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/repository/CompositeUserNewsResourceRepository.kt b/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/repository/CompositeUserNewsResourceRepository.kt deleted file mode 100644 index 43c2ddf8f..000000000 --- a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/repository/CompositeUserNewsResourceRepository.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2023 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.domain.repository - -import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository -import com.google.samples.apps.nowinandroid.core.domain.di.ApplicationScope -import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource -import com.google.samples.apps.nowinandroid.core.domain.model.mapToUserNewsResources -import com.google.samples.apps.nowinandroid.core.model.data.NewsResource -import com.google.samples.apps.nowinandroid.core.model.data.UserData -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filterNot -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.shareIn -import javax.inject.Inject - -/** - * Implements a [UserNewsResourceRepository] by combining a [NewsRepository] with a - * [UserDataRepository]. - */ -class CompositeUserNewsResourceRepository @Inject constructor( - @ApplicationScope private val coroutineScope: CoroutineScope, - val newsRepository: NewsRepository, - val userDataRepository: UserDataRepository, -) : UserNewsResourceRepository { - - private val userNewsResources = - newsRepository.getNewsResources().mapToUserNewsResources(userDataRepository.userData) - .shareIn(coroutineScope, started = WhileSubscribed(5000), replay = 1) - - override fun getUserNewsResources( - query: NewsResourceQuery, - ): Flow> = - userNewsResources.map { resources -> - resources.filter { resource -> - query.filterTopicIds?.let { topics -> resource.hasTopic(topics) } ?: true && - query.filterNewsIds?.contains(resource.id) ?: true - } - } - - override fun getUserNewsResourcesForFollowedTopics(): Flow> = - userDataRepository.userData.flatMapLatest { getUserNewsResources(NewsResourceQuery(filterTopicIds = it.followedTopics)) } - - private fun UserNewsResource.hasTopic(filterTopicIds: Set) = - followableTopics.any { filterTopicIds.contains(it.topic.id) } -} - -private fun Flow>.mapToUserNewsResources( - userDataStream: Flow, -): Flow> = - filterNot { it.isEmpty() } - .combine(userDataStream) { newsResources, userData -> - newsResources.mapToUserNewsResources(userData) - } diff --git a/core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCaseTest.kt b/core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCaseTest.kt index 8bf63aea4..42a31f858 100644 --- a/core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCaseTest.kt +++ b/core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCaseTest.kt @@ -17,7 +17,7 @@ package com.google.samples.apps.nowinandroid.core.domain import com.google.samples.apps.nowinandroid.core.domain.TopicSortField.NAME -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository diff --git a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/model/FollowableTopic.kt b/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt similarity index 81% rename from core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/model/FollowableTopic.kt rename to core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt index 7b59df412..cef319c5f 100644 --- a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/model/FollowableTopic.kt +++ b/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2023 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. @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain.model - -import com.google.samples.apps.nowinandroid.core.model.data.Topic +package com.google.samples.apps.nowinandroid.core.model.data /** * A [topic] with the additional information for whether or not it is followed. diff --git a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/model/UserNewsResource.kt b/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt similarity index 81% rename from core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/model/UserNewsResource.kt rename to core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt index 1d0051918..251911930 100644 --- a/core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/model/UserNewsResource.kt +++ b/core/model/src/main/java/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2023 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. @@ -14,11 +14,8 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain.model +package com.google.samples.apps.nowinandroid.core.model.data -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.UserData import kotlinx.datetime.Instant /** @@ -35,7 +32,7 @@ data class UserNewsResource internal constructor( val type: NewsResourceType, val followableTopics: List, val isSaved: Boolean, - val isViewed: Boolean, + val hasBeenViewed: Boolean, ) { constructor(newsResource: NewsResource, userData: UserData) : this( id = newsResource.id, @@ -52,7 +49,7 @@ data class UserNewsResource internal constructor( ) }, isSaved = userData.bookmarkedNewsResources.contains(newsResource.id), - isViewed = userData.viewedNewsResources.contains(newsResource.id), + hasBeenViewed = userData.viewedNewsResources.contains(newsResource.id), ) } diff --git a/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt b/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt index 40e9327d3..32a0cd127 100644 --- a/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt +++ b/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/data/FollowableTopicTestData.kt @@ -16,7 +16,7 @@ package com.google.samples.apps.nowinandroid.core.testing.data -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic /* ktlint-disable max-line-length */ diff --git a/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt b/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt index f4085d11e..987b48b57 100644 --- a/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt +++ b/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt @@ -16,12 +16,14 @@ package com.google.samples.apps.nowinandroid.core.testing.data -import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig 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.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.UserData +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.datetime.Instant import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone @@ -54,7 +56,7 @@ val userNewsResourcesTestData: List = UserData( second = 0, nanosecond = 0, ).toInstant(TimeZone.UTC), - type = NewsResourceType.Codelab, + type = Codelab, topics = listOf(topicsTestData[2]), ), userData = userData, @@ -70,7 +72,7 @@ val userNewsResourcesTestData: List = UserData( url = "https://youtu.be/-fJ6poHQrjM", headerImageUrl = "https://i.ytimg.com/vi/-fJ6poHQrjM/maxresdefault.jpg", publishDate = Instant.parse("2021-11-09T00:00:00.000Z"), - type = NewsResourceType.Video, + type = Video, topics = topicsTestData.take(2), ), userData = userData, @@ -86,7 +88,7 @@ val userNewsResourcesTestData: List = UserData( url = "https://youtu.be/ZARz0pjm5YM", headerImageUrl = "https://i.ytimg.com/vi/ZARz0pjm5YM/maxresdefault.jpg", publishDate = Instant.parse("2021-11-01T00:00:00.000Z"), - type = NewsResourceType.Video, + type = Video, topics = listOf(topicsTestData[2]), ), userData = userData, @@ -100,7 +102,7 @@ val userNewsResourcesTestData: List = UserData( url = "https://developer.android.com/jetpack/androidx/versions/all-channel", headerImageUrl = "", publishDate = Instant.parse("2022-10-01T00:00:00.000Z"), - type = NewsResourceType.Unknown, + type = Unknown, topics = listOf(topicsTestData[2]), ), userData = userData, diff --git a/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt b/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt index 1b8483d1a..66ac80868 100644 --- a/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt +++ b/core/testing/src/main/java/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt @@ -73,7 +73,7 @@ class TestUserDataRepository : UserDataRepository { } } - override suspend fun updateNewsResourceViewed(newsResourceId: String, viewed: Boolean) { + override suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { currentUserData.let { current -> _userData.tryEmit( current.copy( diff --git a/core/ui/src/androidTest/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardTest.kt b/core/ui/src/androidTest/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardTest.kt index 8e2e8fb4a..a495a6266 100644 --- a/core/ui/src/androidTest/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardTest.kt +++ b/core/ui/src/androidTest/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardTest.kt @@ -40,7 +40,7 @@ class NewsResourceCardTest { NewsResourceCardExpanded( userNewsResource = newsWithKnownResourceType, isBookmarked = false, - isViewed = false, + hasBeenViewed = false, onToggleBookmark = {}, onClick = {}, onTopicClick = {}, @@ -69,7 +69,7 @@ class NewsResourceCardTest { NewsResourceCardExpanded( userNewsResource = newsWithUnknownResourceType, isBookmarked = false, - isViewed = false, + hasBeenViewed = false, onToggleBookmark = {}, onClick = {}, onTopicClick = {}, @@ -113,7 +113,7 @@ class NewsResourceCardTest { NewsResourceCardExpanded( userNewsResource = unreadNews, isBookmarked = false, - isViewed = false, + hasBeenViewed = false, onToggleBookmark = {}, onClick = {}, onTopicClick = {}, @@ -137,7 +137,7 @@ class NewsResourceCardTest { NewsResourceCardExpanded( userNewsResource = readNews, isBookmarked = false, - isViewed = true, + hasBeenViewed = true, onToggleBookmark = {}, onClick = {}, onTopicClick = {}, diff --git a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/FollowableTopicPreviewParameterProvider.kt b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/FollowableTopicPreviewParameterProvider.kt index 3c83b973c..0dd9501b4 100644 --- a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/FollowableTopicPreviewParameterProvider.kt +++ b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/FollowableTopicPreviewParameterProvider.kt @@ -17,7 +17,7 @@ package com.google.samples.apps.nowinandroid.core.ui import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic /* ktlint-disable max-line-length */ diff --git a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt index fb1fb56b7..412266034 100644 --- a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt +++ b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt @@ -39,7 +39,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper 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.UserNewsResource /** * An extension on [LazyListScope] defining a feed with news resources. @@ -48,7 +48,7 @@ import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource fun LazyGridScope.newsFeed( feedState: NewsFeedUiState, onNewsResourcesCheckedChanged: (String, Boolean) -> Unit, - onNewsResourcesViewedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, onTopicClick: (String) -> Unit, ) { when (feedState) { @@ -71,9 +71,9 @@ fun LazyGridScope.newsFeed( newsResourceTitle = userNewsResource.title, ) launchCustomChromeTab(context, resourceUrl, backgroundColor) - onNewsResourcesViewedChanged(userNewsResource.id, true) + onNewsResourceViewed(userNewsResource.id) }, - isViewed = userNewsResource.isViewed, + hasBeenViewed = userNewsResource.hasBeenViewed, onToggleBookmark = { onNewsResourcesCheckedChanged( userNewsResource.id, @@ -125,7 +125,7 @@ private fun NewsFeedLoadingPreview() { newsFeed( feedState = NewsFeedUiState.Loading, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } @@ -144,7 +144,7 @@ private fun NewsFeedContentPreview( newsFeed( feedState = NewsFeedUiState.Success(userNewsResources), onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } diff --git a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt index 67a41fece..f74fb48ca 100644 --- a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt +++ b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt @@ -61,10 +61,10 @@ 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.model.data.FollowableTopic 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.UserNewsResource import kotlinx.datetime.Instant import kotlinx.datetime.toJavaInstant import java.time.ZoneId @@ -81,7 +81,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.R as DesignsystemR fun NewsResourceCardExpanded( userNewsResource: UserNewsResource, isBookmarked: Boolean, - isViewed: Boolean, + hasBeenViewed: Boolean, onToggleBookmark: () -> Unit, onClick: () -> Unit, onTopicClick: (String) -> Unit, @@ -119,8 +119,8 @@ fun NewsResourceCardExpanded( } Spacer(modifier = Modifier.height(12.dp)) Row(verticalAlignment = Alignment.CenterVertically) { - if (!isViewed) { - Dot( + if (!hasBeenViewed) { + NotificationDot( color = MaterialTheme.colorScheme.tertiary, modifier = Modifier.size(8.dp), ) @@ -196,7 +196,7 @@ fun BookmarkButton( } @Composable -fun Dot( +fun NotificationDot( color: Color, modifier: Modifier = Modifier, ) { @@ -333,7 +333,7 @@ private fun ExpandedNewsResourcePreview( NewsResourceCardExpanded( userNewsResource = userNewsResources[0], isBookmarked = true, - isViewed = false, + hasBeenViewed = false, onToggleBookmark = {}, onClick = {}, onTopicClick = {}, diff --git a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt index 6c971e7a2..884da93b5 100644 --- a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt +++ b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCardList.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper -import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource /** * Extension function for displaying a [List] of [NewsResourceCardExpanded] backed by a list of @@ -37,7 +37,7 @@ import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource fun LazyListScope.userNewsResourceCardItems( items: List, onToggleBookmark: (item: UserNewsResource) -> Unit, - onNewsResourcesViewedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, onItemClick: ((item: UserNewsResource) -> Unit)? = null, onTopicClick: (String) -> Unit, itemModifier: Modifier = Modifier, @@ -53,7 +53,7 @@ fun LazyListScope.userNewsResourceCardItems( NewsResourceCardExpanded( userNewsResource = userNewsResource, isBookmarked = userNewsResource.isSaved, - isViewed = userNewsResource.isViewed, + hasBeenViewed = userNewsResource.hasBeenViewed, onToggleBookmark = { onToggleBookmark(userNewsResource) }, onClick = { analyticsHelper.logNewsResourceOpened( @@ -61,12 +61,10 @@ fun LazyListScope.userNewsResourceCardItems( newsResourceTitle = userNewsResource.title, ) when (onItemClick) { - null -> { - launchCustomChromeTab(context, resourceUrl, backgroundColor) - onNewsResourcesViewedChanged(userNewsResource.id, true) - } + null -> launchCustomChromeTab(context, resourceUrl, backgroundColor) else -> onItemClick(userNewsResource) } + onNewsResourceViewed(userNewsResource.id) }, onTopicClick = onTopicClick, modifier = itemModifier, diff --git a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt index 8a1c108c6..3f3f9bddd 100644 --- a/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt +++ b/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt @@ -17,7 +17,6 @@ package com.google.samples.apps.nowinandroid.core.ui import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType @@ -25,6 +24,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Vid import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.UserData +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.datetime.Instant import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone diff --git a/feature/bookmarks/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt b/feature/bookmarks/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt index c5ddd5c10..680c6dcf7 100644 --- a/feature/bookmarks/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt +++ b/feature/bookmarks/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt @@ -52,7 +52,7 @@ class BookmarksScreenTest { feedState = NewsFeedUiState.Loading, removeFromBookmarks = {}, onTopicClick = {}, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } @@ -72,7 +72,7 @@ class BookmarksScreenTest { ), removeFromBookmarks = {}, onTopicClick = {}, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } @@ -115,7 +115,7 @@ class BookmarksScreenTest { removeFromBookmarksCalled = true }, onTopicClick = {}, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } @@ -146,7 +146,7 @@ class BookmarksScreenTest { feedState = NewsFeedUiState.Success(emptyList()), removeFromBookmarks = {}, onTopicClick = {}, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } diff --git a/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt b/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt index b39f189d1..a9ef26f64 100644 --- a/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt +++ b/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreen.kt @@ -54,7 +54,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.LocalTintTheme 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.UserNewsResource 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.Success @@ -73,7 +73,7 @@ internal fun BookmarksRoute( BookmarksScreen( feedState = feedState, removeFromBookmarks = viewModel::removeFromSavedResources, - onNewsResourcesViewedChanged = viewModel::updateNewsResourceViewed, + onNewsResourceViewed = { viewModel.setNewsResourceViewed(it, true) }, onTopicClick = onTopicClick, modifier = modifier, ) @@ -87,14 +87,14 @@ internal fun BookmarksRoute( internal fun BookmarksScreen( feedState: NewsFeedUiState, removeFromBookmarks: (String) -> Unit, - onNewsResourcesViewedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, ) { when (feedState) { Loading -> LoadingState(modifier) is Success -> if (feedState.feed.isNotEmpty()) { - BookmarksGrid(feedState, removeFromBookmarks, onNewsResourcesViewedChanged, onTopicClick, modifier) + BookmarksGrid(feedState, removeFromBookmarks, onNewsResourceViewed, onTopicClick, modifier) } else { EmptyState(modifier) } @@ -117,7 +117,7 @@ private fun LoadingState(modifier: Modifier = Modifier) { private fun BookmarksGrid( feedState: NewsFeedUiState, removeFromBookmarks: (String) -> Unit, - onNewsResourcesViewedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, ) { @@ -136,7 +136,7 @@ private fun BookmarksGrid( newsFeed( feedState = feedState, onNewsResourcesCheckedChanged = { id, _ -> removeFromBookmarks(id) }, - onNewsResourcesViewedChanged = onNewsResourcesViewedChanged, + onNewsResourceViewed = onNewsResourceViewed, onTopicClick = onTopicClick, ) item(span = { GridItemSpan(maxLineSpan) }) { @@ -202,7 +202,7 @@ private fun BookmarksGridPreview( BookmarksGrid( feedState = Success(userNewsResources), removeFromBookmarks = {}, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } diff --git a/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt b/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt index 7d0003aed..82d2c0e19 100644 --- a/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt +++ b/feature/bookmarks/src/main/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt @@ -19,14 +19,13 @@ 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.model.UserNewsResource -import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn @@ -39,16 +38,15 @@ class BookmarksViewModel @Inject constructor( userNewsResourceRepository: UserNewsResourceRepository, ) : ViewModel() { - val feedUiState: StateFlow = userNewsResourceRepository.getUserNewsResources() - .filterNot { it.isEmpty() } - .map { newsResources -> newsResources.filter(UserNewsResource::isSaved) } // Only show bookmarked news resources. - .map, NewsFeedUiState>(NewsFeedUiState::Success) - .onStart { emit(Loading) } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5_000), - initialValue = Loading, - ) + val feedUiState: StateFlow = + userNewsResourceRepository.getBookmarkedUserNewsResources() + .map, NewsFeedUiState>(NewsFeedUiState::Success) + .onStart { emit(Loading) } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = Loading, + ) fun removeFromSavedResources(newsResourceId: String) { viewModelScope.launch { @@ -56,9 +54,9 @@ class BookmarksViewModel @Inject constructor( } } - fun updateNewsResourceViewed(newsResourceId: String, isViewed: Boolean) { + fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { viewModelScope.launch { - userDataRepository.updateNewsResourceViewed(newsResourceId, isViewed) + userDataRepository.setNewsResourceViewed(newsResourceId, viewed) } } } diff --git a/feature/bookmarks/src/test/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt b/feature/bookmarks/src/test/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt index d97f71095..6469a684b 100644 --- a/feature/bookmarks/src/test/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt +++ b/feature/bookmarks/src/test/java/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt @@ -16,7 +16,7 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks -import com.google.samples.apps.nowinandroid.core.domain.repository.CompositeUserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.testing.data.newsResourcesTestData import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository @@ -25,7 +25,6 @@ import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Before @@ -45,7 +44,6 @@ class BookmarksViewModelTest { private val userDataRepository = TestUserDataRepository() private val newsRepository = TestNewsRepository() private val userNewsResourceRepository = CompositeUserNewsResourceRepository( - coroutineScope = TestScope(UnconfinedTestDispatcher()), newsRepository = newsRepository, userDataRepository = userDataRepository, ) diff --git a/feature/foryou/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt b/feature/foryou/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt index a3566fc31..fde215aa1 100644 --- a/feature/foryou/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt +++ b/feature/foryou/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt @@ -56,7 +56,7 @@ class ForYouScreenTest { onTopicClick = {}, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } } @@ -80,7 +80,7 @@ class ForYouScreenTest { onTopicClick = {}, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } } @@ -110,7 +110,7 @@ class ForYouScreenTest { onTopicClick = {}, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } } @@ -155,7 +155,7 @@ class ForYouScreenTest { onTopicClick = {}, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } } @@ -193,7 +193,7 @@ class ForYouScreenTest { onTopicClick = {}, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } } @@ -217,7 +217,7 @@ class ForYouScreenTest { onTopicClick = {}, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } } @@ -242,7 +242,7 @@ class ForYouScreenTest { onTopicClick = {}, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } diff --git a/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt index 44a323868..961046538 100644 --- a/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt +++ b/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt @@ -81,7 +81,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconT import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaOverlayLoadingWheel 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.UserNewsResource +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent @@ -107,7 +107,7 @@ internal fun ForYouRoute( onTopicClick = onTopicClick, saveFollowedTopics = viewModel::dismissOnboarding, onNewsResourcesCheckedChanged = viewModel::updateNewsResourceSaved, - onNewsResourcesViewedChanged = viewModel::updateNewsResourceViewed, + onNewsResourceViewed = { viewModel.setNewsResourceViewed(it, true) }, modifier = modifier, ) } @@ -121,7 +121,7 @@ internal fun ForYouScreen( onTopicClick: (String) -> Unit, saveFollowedTopics: () -> Unit, onNewsResourcesCheckedChanged: (String, Boolean) -> Unit, - onNewsResourcesViewedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, modifier: Modifier = Modifier, ) { val isOnboardingLoading = onboardingUiState is OnboardingUiState.Loading @@ -179,7 +179,7 @@ internal fun ForYouScreen( newsFeed( feedState = feedState, onNewsResourcesCheckedChanged = onNewsResourcesCheckedChanged, - onNewsResourcesViewedChanged = onNewsResourcesViewedChanged, + onNewsResourceViewed = onNewsResourceViewed, onTopicClick = onTopicClick, ) @@ -416,7 +416,7 @@ fun ForYouScreenPopulatedFeed( onTopicCheckedChanged = { _, _ -> }, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } @@ -440,7 +440,7 @@ fun ForYouScreenOfflinePopulatedFeed( onTopicCheckedChanged = { _, _ -> }, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } @@ -466,7 +466,7 @@ fun ForYouScreenTopicSelection( onTopicCheckedChanged = { _, _ -> }, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } @@ -485,7 +485,7 @@ fun ForYouScreenLoading() { onTopicCheckedChanged = { _, _ -> }, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } @@ -509,7 +509,7 @@ fun ForYouScreenPopulatedAndLoading( onTopicCheckedChanged = { _, _ -> }, saveFollowedTopics = {}, onNewsResourcesCheckedChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } diff --git a/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt b/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt index 363356aef..84638c55e 100644 --- a/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt +++ b/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt @@ -18,22 +18,16 @@ package com.google.samples.apps.nowinandroid.feature.foryou import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository 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.model.UserNewsResource -import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository -import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -58,7 +52,7 @@ class ForYouViewModel @Inject constructor( ) val feedState: StateFlow = - userDataRepository.getFollowedUserNewsResources(userNewsResourceRepository) + userNewsResourceRepository.getUserNewsResourcesForFollowedTopics() .map(NewsFeedUiState::Success) .stateIn( scope = viewModelScope, @@ -95,9 +89,9 @@ class ForYouViewModel @Inject constructor( } } - fun updateNewsResourceViewed(newsResourceId: String, isViewed: Boolean) { + fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { viewModelScope.launch { - userDataRepository.updateNewsResourceViewed(newsResourceId, isViewed) + userDataRepository.setNewsResourceViewed(newsResourceId, viewed) } } @@ -107,47 +101,3 @@ class ForYouViewModel @Inject constructor( } } } - -/** - * Obtain a flow of user news resources whose topics match those the user is following. - * - * getUserNewsResources: The `UseCase` used to obtain the flow of user news resources. - */ -private fun UserDataRepository.getFollowedUserNewsResources( - userNewsResourceRepository: UserNewsResourceRepository, -): Flow> = userData - // Map the user data into a set of followed topic IDs or null if we should return an empty list. - .map { userData -> - if (userData.shouldShowEmptyFeed()) { - null - } else { - userData.followedTopics - } - } - // Only emit a set of followed topic IDs if it's changed. This avoids calling potentially - // expensive operations (like setting up a new flow) when nothing has changed. - .distinctUntilChanged() - // getUserNewsResources returns a flow, so we have a flow inside a flow. flatMapLatest moves - // the inner flow (the one we want to return) to the outer flow and cancels any previous flows - // created by getUserNewsResources. - .flatMapLatest { followedTopics -> - if (followedTopics == null) { - flowOf(emptyList()) - } else { - userNewsResourceRepository.getUserNewsResources( - NewsResourceQuery(filterTopicIds = followedTopics), - ) - } - } - -/** - * If the user hasn't completed the onboarding and hasn't selected any interests - * show an empty news list to clearly demonstrate that their selections affect the - * news articles they will see. - * - * Note: It should not be possible for the user to get into a state where the onboarding - * is not displayed AND they haven't followed any topics, however, this method is to safeguard - * against that scenario in future. - */ -private fun UserData.shouldShowEmptyFeed() = - !shouldHideOnboarding && followedTopics.isEmpty() diff --git a/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt b/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt index faf368b1e..58f4f1683 100644 --- a/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt +++ b/feature/foryou/src/main/java/com/google/samples/apps/nowinandroid/feature/foryou/OnboardingUiState.kt @@ -16,7 +16,7 @@ package com.google.samples.apps.nowinandroid.feature.foryou -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic /** * A sealed hierarchy describing the onboarding state for the for you screen. diff --git a/feature/foryou/src/test/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt b/feature/foryou/src/test/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt index 9bac2549c..16c593aa0 100644 --- a/feature/foryou/src/test/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt +++ b/feature/foryou/src/test/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt @@ -16,14 +16,14 @@ package com.google.samples.apps.nowinandroid.feature.foryou +import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase -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.mapToUserNewsResources -import com.google.samples.apps.nowinandroid.core.domain.repository.CompositeUserNewsResourceRepository +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.NewsResourceType.Video import com.google.samples.apps.nowinandroid.core.model.data.Topic +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource +import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResources import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository @@ -34,7 +34,6 @@ import com.google.samples.apps.nowinandroid.core.testing.util.TestSyncStatusMoni import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest @@ -58,7 +57,6 @@ class ForYouViewModelTest { private val topicsRepository = TestTopicsRepository() private val newsRepository = TestNewsRepository() private val userNewsResourceRepository = CompositeUserNewsResourceRepository( - coroutineScope = TestScope(UnconfinedTestDispatcher()), newsRepository = newsRepository, userDataRepository = userDataRepository, ) diff --git a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt index 8f863ba5a..e618c1c9f 100644 --- a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt +++ b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt @@ -29,7 +29,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground 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.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent diff --git a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt index d6ef94521..debc49bcd 100644 --- a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt +++ b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt @@ -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.domain.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.TopicSortField -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow diff --git a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt index dcca35795..457014cc2 100644 --- a/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt +++ b/feature/interests/src/main/java/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt @@ -27,7 +27,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic @Composable fun TopicsTabContent( diff --git a/feature/interests/src/test/java/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt b/feature/interests/src/test/java/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt index e47b25021..c46cb7780 100644 --- a/feature/interests/src/test/java/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt +++ b/feature/interests/src/test/java/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt @@ -17,7 +17,7 @@ package com.google.samples.apps.nowinandroid.interests import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository diff --git a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt index 65d923442..94f86a8e4 100644 --- a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt +++ b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt @@ -59,7 +59,7 @@ class TopicScreenTest { onFollowClick = {}, onTopicClick = {}, onBookmarkChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } @@ -79,7 +79,7 @@ class TopicScreenTest { onFollowClick = {}, onTopicClick = {}, onBookmarkChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } @@ -104,7 +104,7 @@ class TopicScreenTest { onFollowClick = {}, onTopicClick = {}, onBookmarkChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } @@ -127,7 +127,7 @@ class TopicScreenTest { onFollowClick = {}, onTopicClick = {}, onBookmarkChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, ) } diff --git a/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt b/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt index 4fc9faaca..fd408f9cf 100644 --- a/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt +++ b/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt @@ -51,8 +51,8 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaFilte import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel 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.model.data.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank @@ -79,7 +79,7 @@ internal fun TopicRoute( onBackClick = onBackClick, onFollowClick = viewModel::followTopicToggle, onBookmarkChanged = viewModel::bookmarkNews, - onNewsResourcesViewedChanged = viewModel::updateNewsResourceViewed, + onNewsResourceViewed = { viewModel.setNewsResourceViewed(it, true) }, onTopicClick = onTopicClick, ) } @@ -93,7 +93,7 @@ internal fun TopicScreen( onFollowClick: (Boolean) -> Unit, onTopicClick: (String) -> Unit, onBookmarkChanged: (String, Boolean) -> Unit, - onNewsResourcesViewedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, modifier: Modifier = Modifier, ) { val state = rememberLazyListState() @@ -129,7 +129,7 @@ internal fun TopicScreen( news = newsUiState, imageUrl = topicUiState.followableTopic.topic.imageUrl, onBookmarkChanged = onBookmarkChanged, - onNewsResourcesViewedChanged = onNewsResourcesViewedChanged, + onNewsResourceViewed = onNewsResourceViewed, onTopicClick = onTopicClick, ) } @@ -146,7 +146,7 @@ private fun LazyListScope.TopicBody( news: NewsUiState, imageUrl: String, onBookmarkChanged: (String, Boolean) -> Unit, - onNewsResourcesViewedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, onTopicClick: (String) -> Unit, ) { // TODO: Show icon if available @@ -154,7 +154,7 @@ private fun LazyListScope.TopicBody( TopicHeader(name, description, imageUrl) } - userNewsResourceCards(news, onBookmarkChanged, onNewsResourcesViewedChanged, onTopicClick) + userNewsResourceCards(news, onBookmarkChanged, onNewsResourceViewed, onTopicClick) } @Composable @@ -185,7 +185,7 @@ private fun TopicHeader(name: String, description: String, imageUrl: String) { private fun LazyListScope.userNewsResourceCards( news: NewsUiState, onBookmarkChanged: (String, Boolean) -> Unit, - onNewsResourcesViewedChanged: (String, Boolean) -> Unit, + onNewsResourceViewed: (String) -> Unit, onTopicClick: (String) -> Unit, ) { when (news) { @@ -193,7 +193,7 @@ private fun LazyListScope.userNewsResourceCards( userNewsResourceCardItems( items = news.news, onToggleBookmark = { onBookmarkChanged(it.id, !it.isSaved) }, - onNewsResourcesViewedChanged = onNewsResourcesViewedChanged, + onNewsResourceViewed = onNewsResourceViewed, onTopicClick = onTopicClick, itemModifier = Modifier.padding(24.dp), ) @@ -220,7 +220,7 @@ private fun TopicBodyPreview() { news = NewsUiState.Success(emptyList()), imageUrl = "", onBookmarkChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } @@ -278,7 +278,7 @@ fun TopicScreenPopulated( onBackClick = {}, onFollowClick = {}, onBookmarkChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } @@ -296,7 +296,7 @@ fun TopicScreenLoading() { onBackClick = {}, onFollowClick = {}, onBookmarkChanged = { _, _ -> }, - onNewsResourcesViewedChanged = { _, _ -> }, + onNewsResourceViewed = {}, onTopicClick = {}, ) } diff --git a/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt b/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt index 4dac25983..425d66c73 100644 --- a/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt +++ b/feature/topic/src/main/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt @@ -22,11 +22,11 @@ import androidx.lifecycle.viewModelScope import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery 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.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder -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.repository.UserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic +import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.result.Result import com.google.samples.apps.nowinandroid.core.result.asResult import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicArgs @@ -87,9 +87,9 @@ class TopicViewModel @Inject constructor( } } - fun updateNewsResourceViewed(newsResourceId: String, isViewed: Boolean) { + fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { viewModelScope.launch { - userDataRepository.updateNewsResourceViewed(newsResourceId, isViewed) + userDataRepository.setNewsResourceViewed(newsResourceId, viewed) } } } diff --git a/feature/topic/src/test/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt b/feature/topic/src/test/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt index 3580a960b..ff7a88160 100644 --- a/feature/topic/src/test/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt +++ b/feature/topic/src/test/java/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt @@ -17,8 +17,8 @@ package com.google.samples.apps.nowinandroid.feature.topic import androidx.lifecycle.SavedStateHandle -import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic -import com.google.samples.apps.nowinandroid.core.domain.repository.CompositeUserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository +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.NewsResourceType.Video import com.google.samples.apps.nowinandroid.core.model.data.Topic @@ -32,7 +32,6 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant @@ -55,7 +54,6 @@ class TopicViewModelTest { private val topicsRepository = TestTopicsRepository() private val newsRepository = TestNewsRepository() private val userNewsResourceRepository = CompositeUserNewsResourceRepository( - coroutineScope = TestScope(UnconfinedTestDispatcher()), newsRepository = newsRepository, userDataRepository = userDataRepository, )