Replace GetUserNewsResourcesUseCase with UserNewsResourceRepository

This moves the responsibility for joining the UserData and the
NewsResources to UserNewsResourceRepository. This way, the work can be
done once and shared with all consumers in a SharedFlow, rather than
having each consumer perform the join itself by invoking the UseCase.
pull/595/head
James Rose 2 years ago
parent 9ee2acf327
commit bd450099fb

@ -24,5 +24,6 @@ import kotlin.annotation.AnnotationRetention.RUNTIME
annotation class Dispatcher(val niaDispatcher: NiaDispatchers)
enum class NiaDispatchers {
Default,
IO,
}

@ -17,6 +17,7 @@
package com.google.samples.apps.nowinandroid.core.network.di
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import dagger.Module
import dagger.Provides
@ -31,4 +32,8 @@ object DispatchersModule {
@Provides
@Dispatcher(IO)
fun providesIODispatcher(): CoroutineDispatcher = Dispatchers.IO
@Provides
@Dispatcher(Default)
fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
}

@ -1,58 +0,0 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.domain
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.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.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNot
import javax.inject.Inject
/**
* A use case responsible for obtaining news resources with their associated bookmarked (also known
* as "saved") state.
*/
class GetUserNewsResourcesUseCase @Inject constructor(
private val newsRepository: NewsRepository,
private val userDataRepository: UserDataRepository,
) {
/**
* Returns a list of UserNewsResources which match the supplied set of topic ids.
*
* @param query - Summary of query parameters for news resources.
*/
operator fun invoke(
query: NewsResourceQuery = NewsResourceQuery(),
): Flow<List<UserNewsResource>> =
newsRepository.getNewsResources(
query = query,
).mapToUserNewsResources(userDataRepository.userData)
}
private fun Flow<List<NewsResource>>.mapToUserNewsResources(
userDataStream: Flow<UserData>,
): Flow<List<UserNewsResource>> =
filterNot { it.isEmpty() }
.combine(userDataStream) { newsResources, userData ->
newsResources.mapToUserNewsResources(userData)
}

@ -0,0 +1,42 @@
/*
* 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)
}

@ -0,0 +1,33 @@
/*
* 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 com.google.samples.apps.nowinandroid.core.domain.repository.CompositeUserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface UserNewsResourceRepositoryModule {
@Binds
fun bindsUserNewsResourceRepository(
userDataRepository: CompositeUserNewsResourceRepository,
): UserNewsResourceRepository
}

@ -0,0 +1,74 @@
/*
* 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<List<UserNewsResource>> =
userNewsResources.map { resources ->
resources.filter { resource ->
query.filterTopicIds?.let { topics -> resource.hasTopic(topics) } ?: true &&
query.filterNewsIds?.contains(resource.id) ?: true
}
}
override fun getUserNewsResourcesForFollowedTopics(): Flow<List<UserNewsResource>> =
userDataRepository.userData.flatMapLatest { getUserNewsResources(NewsResourceQuery(filterTopicIds = it.followedTopics)) }
private fun UserNewsResource.hasTopic(filterTopicIds: Set<String>) =
followableTopics.any { filterTopicIds.contains(it.topic.id) }
}
private fun Flow<List<NewsResource>>.mapToUserNewsResources(
userDataStream: Flow<UserData>,
): Flow<List<UserNewsResource>> =
filterNot { it.isEmpty() }
.combine(userDataStream) { newsResources, userData ->
newsResources.mapToUserNewsResources(userData)
}

@ -0,0 +1,41 @@
/*
* 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.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import kotlinx.coroutines.flow.Flow
/**
* Data layer implementation for [UserNewsResource]
*/
interface UserNewsResourceRepository {
/**
* Returns available news resources as a stream.
*/
fun getUserNewsResources(
query: NewsResourceQuery = NewsResourceQuery(
filterTopicIds = null,
filterNewsIds = null,
),
): Flow<List<UserNewsResource>>
/**
* Returns available news resources for the user's followed topics as a stream.
*/
fun getUserNewsResourcesForFollowedTopics(): Flow<List<UserNewsResource>>
}

@ -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.
@ -18,34 +18,36 @@ package com.google.samples.apps.nowinandroid.core.domain
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.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.emptyUserData
import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule
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.Rule
import org.junit.Test
import kotlin.test.assertEquals
class GetUserNewsResourcesUseCaseTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
class CompositeUserNewsResourceRepositoryTest {
private val newsRepository = TestNewsRepository()
private val userDataRepository = TestUserDataRepository()
val useCase = GetUserNewsResourcesUseCase(newsRepository, userDataRepository)
private val userNewsResourceRepository = CompositeUserNewsResourceRepository(
coroutineScope = TestScope(UnconfinedTestDispatcher()),
newsRepository = newsRepository,
userDataRepository = userDataRepository,
)
@Test
fun whenNoFilters_allNewsResourcesAreReturned() = runTest {
// Obtain the user news resources stream.
val userNewsResources = useCase()
// Obtain the user news resources flow.
val userNewsResources = userNewsResourceRepository.getUserNewsResources()
// Send some news resources and user data into the data repositories.
newsRepository.sendNewsResources(sampleNewsResources)
@ -68,11 +70,8 @@ class GetUserNewsResourcesUseCaseTest {
@Test
fun whenFilteredByTopicId_matchingNewsResourcesAreReturned() = runTest {
// Obtain a stream of user news resources for the given topic id.
val userNewsResources = useCase(
NewsResourceQuery(
filterTopicIds = setOf(sampleTopic1.id),
),
)
val userNewsResources =
userNewsResourceRepository.getUserNewsResources(NewsResourceQuery(filterTopicIds = setOf(sampleTopic1.id)))
// Send test data into the repositories.
newsRepository.sendNewsResources(sampleNewsResources)
@ -86,6 +85,28 @@ class GetUserNewsResourcesUseCaseTest {
userNewsResources.first(),
)
}
@Test
fun whenFilteredByFollowedTopics_matchingNewsResourcesAreReturned() = runTest {
// Obtain a stream of user news resources for the given topic id.
val userNewsResources =
userNewsResourceRepository.getUserNewsResourcesForFollowedTopics()
// Send test data into the repositories.
val userData = emptyUserData.copy(
followedTopics = setOf(sampleTopic1.id),
)
newsRepository.sendNewsResources(sampleNewsResources)
userDataRepository.setUserData(userData)
// Check that only news resources with the given topic id are returned.
assertEquals(
sampleNewsResources
.filter { it.topics.contains(sampleTopic1) }
.mapToUserNewsResources(userData),
userNewsResources.first(),
)
}
}
private val sampleTopic1 = Topic(

@ -19,8 +19,8 @@ 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.GetUserNewsResourcesUseCase
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.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading
import dagger.hilt.android.lifecycle.HiltViewModel
@ -36,10 +36,10 @@ import javax.inject.Inject
@HiltViewModel
class BookmarksViewModel @Inject constructor(
private val userDataRepository: UserDataRepository,
getSaveableNewsResources: GetUserNewsResourcesUseCase,
userNewsResourceRepository: UserNewsResourceRepository,
) : ViewModel() {
val feedUiState: StateFlow<NewsFeedUiState> = getSaveableNewsResources()
val feedUiState: StateFlow<NewsFeedUiState> = userNewsResourceRepository.getUserNewsResources()
.filterNot { it.isEmpty() }
.map { newsResources -> newsResources.filter(UserNewsResource::isSaved) } // Only show bookmarked news resources.
.map<List<UserNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success)

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.feature.bookmarks
import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.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,6 +25,7 @@ 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
@ -43,7 +44,8 @@ class BookmarksViewModelTest {
private val userDataRepository = TestUserDataRepository()
private val newsRepository = TestNewsRepository()
private val getUserNewsResourcesUseCase = GetUserNewsResourcesUseCase(
private val userNewsResourceRepository = CompositeUserNewsResourceRepository(
coroutineScope = TestScope(UnconfinedTestDispatcher()),
newsRepository = newsRepository,
userDataRepository = userDataRepository,
)
@ -53,7 +55,7 @@ class BookmarksViewModelTest {
fun setup() {
viewModel = BookmarksViewModel(
userDataRepository = userDataRepository,
getSaveableNewsResources = getUserNewsResourcesUseCase,
userNewsResourceRepository = userNewsResourceRepository,
)
}

@ -22,8 +22,8 @@ import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQue
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.data.util.SyncStatusMonitor
import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
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
@ -43,7 +43,7 @@ import javax.inject.Inject
class ForYouViewModel @Inject constructor(
syncStatusMonitor: SyncStatusMonitor,
private val userDataRepository: UserDataRepository,
getUserNewsResources: GetUserNewsResourcesUseCase,
userNewsResourceRepository: UserNewsResourceRepository,
getFollowableTopics: GetFollowableTopicsUseCase,
) : ViewModel() {
@ -58,7 +58,7 @@ class ForYouViewModel @Inject constructor(
)
val feedState: StateFlow<NewsFeedUiState> =
userDataRepository.getFollowedUserNewsResources(getUserNewsResources)
userDataRepository.getFollowedUserNewsResources(userNewsResourceRepository)
.map(NewsFeedUiState::Success)
.stateIn(
scope = viewModelScope,
@ -108,7 +108,7 @@ class ForYouViewModel @Inject constructor(
* getUserNewsResources: The `UseCase` used to obtain the flow of user news resources.
*/
private fun UserDataRepository.getFollowedUserNewsResources(
getUserNewsResources: GetUserNewsResourcesUseCase,
userNewsResourceRepository: UserNewsResourceRepository,
): Flow<List<UserNewsResource>> = userData
// Map the user data into a set of followed topic IDs or null if we should return an empty list.
.map { userData ->
@ -128,10 +128,8 @@ private fun UserDataRepository.getFollowedUserNewsResources(
if (followedTopics == null) {
flowOf(emptyList())
} else {
getUserNewsResources(
NewsResourceQuery(
filterTopicIds = followedTopics,
),
userNewsResourceRepository.getUserNewsResources(
NewsResourceQuery(filterTopicIds = followedTopics),
)
}
}

@ -17,10 +17,10 @@
package com.google.samples.apps.nowinandroid.feature.foryou
import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.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
@ -34,6 +34,7 @@ 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
@ -56,7 +57,8 @@ class ForYouViewModelTest {
private val userDataRepository = TestUserDataRepository()
private val topicsRepository = TestTopicsRepository()
private val newsRepository = TestNewsRepository()
private val getUserNewsResourcesUseCase = GetUserNewsResourcesUseCase(
private val userNewsResourceRepository = CompositeUserNewsResourceRepository(
coroutineScope = TestScope(UnconfinedTestDispatcher()),
newsRepository = newsRepository,
userDataRepository = userDataRepository,
)
@ -72,7 +74,7 @@ class ForYouViewModelTest {
viewModel = ForYouViewModel(
syncStatusMonitor = syncStatusMonitor,
userDataRepository = userDataRepository,
getUserNewsResources = getUserNewsResourcesUseCase,
userNewsResourceRepository = userNewsResourceRepository,
getFollowableTopics = getFollowableTopicsUseCase,
)
}

@ -23,9 +23,9 @@ import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQue
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder
import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.result.Result
import com.google.samples.apps.nowinandroid.core.result.asResult
@ -46,7 +46,7 @@ class TopicViewModel @Inject constructor(
stringDecoder: StringDecoder,
private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository,
getSaveableNewsResources: GetUserNewsResourcesUseCase,
userNewsResourceRepository: UserNewsResourceRepository,
) : ViewModel() {
private val topicArgs: TopicArgs = TopicArgs(savedStateHandle, stringDecoder)
@ -67,7 +67,7 @@ class TopicViewModel @Inject constructor(
val newUiState: StateFlow<NewsUiState> = newsUiState(
topicId = topicArgs.topicId,
userDataRepository = userDataRepository,
getSaveableNewsResources = getSaveableNewsResources,
userNewsResourceRepository = userNewsResourceRepository,
)
.stateIn(
scope = viewModelScope,
@ -135,14 +135,12 @@ private fun topicUiState(
private fun newsUiState(
topicId: String,
getSaveableNewsResources: GetUserNewsResourcesUseCase,
userNewsResourceRepository: UserNewsResourceRepository,
userDataRepository: UserDataRepository,
): Flow<NewsUiState> {
// Observe news
val newsStream: Flow<List<UserNewsResource>> = getSaveableNewsResources(
NewsResourceQuery(
filterTopicIds = setOf(element = topicId),
),
val newsStream: Flow<List<UserNewsResource>> = userNewsResourceRepository.getUserNewsResources(
NewsResourceQuery(filterTopicIds = setOf(element = topicId)),
)
// Observe bookmarks

@ -17,8 +17,8 @@
package com.google.samples.apps.nowinandroid.feature.topic
import androidx.lifecycle.SavedStateHandle
import com.google.samples.apps.nowinandroid.core.domain.GetUserNewsResourcesUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.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
@ -32,6 +32,7 @@ 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
@ -53,7 +54,8 @@ class TopicViewModelTest {
private val userDataRepository = TestUserDataRepository()
private val topicsRepository = TestTopicsRepository()
private val newsRepository = TestNewsRepository()
private val getUserNewsResourcesUseCase = GetUserNewsResourcesUseCase(
private val userNewsResourceRepository = CompositeUserNewsResourceRepository(
coroutineScope = TestScope(UnconfinedTestDispatcher()),
newsRepository = newsRepository,
userDataRepository = userDataRepository,
)
@ -66,7 +68,7 @@ class TopicViewModelTest {
stringDecoder = FakeStringDecoder(),
userDataRepository = userDataRepository,
topicsRepository = topicsRepository,
getSaveableNewsResources = getUserNewsResourcesUseCase,
userNewsResourceRepository = userNewsResourceRepository,
)
}

Loading…
Cancel
Save