Remove the search module's dependency on other feature modules

Change-Id: I17df9948fed04ddc7ba507b437d39536b8b180bb
pull/1837/head
Don Turner 2 years ago
parent 315c284be2
commit 6da850d28a

@ -1,5 +1,5 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2024 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.feature.interests package com.google.samples.apps.nowinandroid.core.ui
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -38,7 +38,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.DynamicA
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.feature.interests.R.string import com.google.samples.apps.nowinandroid.core.ui.R.string
@Composable @Composable
fun InterestsItem( fun InterestsItem(
@ -70,7 +70,7 @@ fun InterestsItem(
Icon( Icon(
imageVector = NiaIcons.Add, imageVector = NiaIcons.Add,
contentDescription = stringResource( contentDescription = stringResource(
id = string.feature_interests_card_follow_button_content_desc, id = string.core_ui_interests_card_follow_button_content_desc,
), ),
) )
}, },
@ -78,7 +78,7 @@ fun InterestsItem(
Icon( Icon(
imageVector = NiaIcons.Check, imageVector = NiaIcons.Check,
contentDescription = stringResource( contentDescription = stringResource(
id = string.feature_interests_card_unfollow_button_content_desc, id = string.core_ui_interests_card_unfollow_button_content_desc,
), ),
) )
}, },

@ -26,4 +26,7 @@
<string name="core_ui_topic_chip_content_description_when_followed">%1$s is followed</string> <string name="core_ui_topic_chip_content_description_when_followed">%1$s is followed</string>
<string name="core_ui_topic_chip_content_description_when_not_followed">%1$s is not followed</string> <string name="core_ui_topic_chip_content_description_when_not_followed">%1$s is not followed</string>
<string name="core_ui_interests_card_follow_button_content_desc">Follow interest</string>
<string name="core_ui_interests_card_unfollow_button_content_desc">Unfollow interest</string>
</resources> </resources>

@ -36,6 +36,7 @@ import com.google.samples.apps.nowinandroid.core.testing.util.TestSyncManager
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.feature.foryou.navigation.LINKED_NEWS_RESOURCE_ID import com.google.samples.apps.nowinandroid.feature.foryou.navigation.LINKED_NEWS_RESOURCE_ID
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.advanceUntilIdle
@ -70,6 +71,7 @@ class ForYouViewModelTest {
topicsRepository = topicsRepository, topicsRepository = topicsRepository,
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
) )
private val savedStateHandle = SavedStateHandle() private val savedStateHandle = SavedStateHandle()
private lateinit var viewModel: ForYouViewModel private lateinit var viewModel: ForYouViewModel
@ -504,6 +506,24 @@ class ForYouViewModelTest {
collectJob.cancel() collectJob.cancel()
} }
@Test
fun whenUpdateNewsResourceSavedIsCalled_bookmarkStateIsUpdated() = runTest {
val newsResourceId = "123"
viewModel.updateNewsResourceSaved(newsResourceId, true)
assertEquals(
expected = setOf(newsResourceId),
actual = userDataRepository.userData.first().bookmarkedNewsResources,
)
viewModel.updateNewsResourceSaved(newsResourceId, false)
assertEquals(
expected = emptySet(),
actual = userDataRepository.userData.first().bookmarkedNewsResources,
)
}
} }
private val sampleTopics = listOf( private val sampleTopics = listOf(

@ -39,6 +39,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.rememberDraggableScroller import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.rememberDraggableScroller
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.ui.InterestsItem
@Composable @Composable
fun TopicsTabContent( fun TopicsTabContent(

@ -18,6 +18,4 @@
<string name="feature_interests_title">Interests</string> <string name="feature_interests_title">Interests</string>
<string name="feature_interests_loading">Loading data</string> <string name="feature_interests_loading">Loading data</string>
<string name="feature_interests_empty_header">"No available data"</string> <string name="feature_interests_empty_header">"No available data"</string>
<string name="feature_interests_card_follow_button_content_desc">Follow interest</string>
<string name="feature_interests_card_unfollow_button_content_desc">Unfollow interest</string>
</resources> </resources>

@ -27,9 +27,7 @@ android {
dependencies { dependencies {
implementation(projects.core.data) implementation(projects.core.data)
implementation(projects.core.domain) implementation(projects.core.domain)
implementation(projects.feature.bookmarks) implementation(projects.core.ui)
implementation(projects.feature.foryou)
implementation(projects.feature.interests)
testImplementation(projects.core.testing) testImplementation(projects.core.testing)

@ -88,14 +88,11 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.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.DevicePreviews
import com.google.samples.apps.nowinandroid.core.ui.InterestsItem
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success
import com.google.samples.apps.nowinandroid.core.ui.R.string import com.google.samples.apps.nowinandroid.core.ui.R.string
import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
import com.google.samples.apps.nowinandroid.core.ui.newsFeed import com.google.samples.apps.nowinandroid.core.ui.newsFeed
import com.google.samples.apps.nowinandroid.feature.bookmarks.BookmarksViewModel
import com.google.samples.apps.nowinandroid.feature.foryou.ForYouViewModel
import com.google.samples.apps.nowinandroid.feature.interests.InterestsItem
import com.google.samples.apps.nowinandroid.feature.interests.InterestsViewModel
import com.google.samples.apps.nowinandroid.feature.search.R as searchR import com.google.samples.apps.nowinandroid.feature.search.R as searchR
@Composable @Composable
@ -104,10 +101,7 @@ internal fun SearchRoute(
onInterestsClick: () -> Unit, onInterestsClick: () -> Unit,
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
bookmarksViewModel: BookmarksViewModel = hiltViewModel(),
interestsViewModel: InterestsViewModel = hiltViewModel(),
searchViewModel: SearchViewModel = hiltViewModel(), searchViewModel: SearchViewModel = hiltViewModel(),
forYouViewModel: ForYouViewModel = hiltViewModel(),
) { ) {
val recentSearchQueriesUiState by searchViewModel.recentSearchQueriesUiState.collectAsStateWithLifecycle() val recentSearchQueriesUiState by searchViewModel.recentSearchQueriesUiState.collectAsStateWithLifecycle()
val searchResultUiState by searchViewModel.searchResultUiState.collectAsStateWithLifecycle() val searchResultUiState by searchViewModel.searchResultUiState.collectAsStateWithLifecycle()
@ -120,9 +114,9 @@ internal fun SearchRoute(
onSearchQueryChanged = searchViewModel::onSearchQueryChanged, onSearchQueryChanged = searchViewModel::onSearchQueryChanged,
onSearchTriggered = searchViewModel::onSearchTriggered, onSearchTriggered = searchViewModel::onSearchTriggered,
onClearRecentSearches = searchViewModel::clearRecentSearches, onClearRecentSearches = searchViewModel::clearRecentSearches,
onNewsResourcesCheckedChanged = forYouViewModel::updateNewsResourceSaved, onNewsResourcesCheckedChanged = searchViewModel::setNewsResourceBookmarked,
onNewsResourceViewed = { bookmarksViewModel.setNewsResourceViewed(it, true) }, onNewsResourceViewed = { searchViewModel.setNewsResourceViewed(it, true) },
onFollowButtonClick = interestsViewModel::followTopic, onFollowButtonClick = searchViewModel::followTopic,
onBackClick = onBackClick, onBackClick = onBackClick,
onInterestsClick = onInterestsClick, onInterestsClick = onInterestsClick,
onTopicClick = onTopicClick, onTopicClick = onTopicClick,

@ -23,6 +23,7 @@ import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent.Param import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent.Param
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsCountUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsCountUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase
@ -44,6 +45,7 @@ class SearchViewModel @Inject constructor(
getSearchContentsCountUseCase: GetSearchContentsCountUseCase, getSearchContentsCountUseCase: GetSearchContentsCountUseCase,
recentSearchQueriesUseCase: GetRecentSearchQueriesUseCase, recentSearchQueriesUseCase: GetRecentSearchQueriesUseCase,
private val recentSearchRepository: RecentSearchRepository, private val recentSearchRepository: RecentSearchRepository,
private val userDataRepository: UserDataRepository,
private val savedStateHandle: SavedStateHandle, private val savedStateHandle: SavedStateHandle,
private val analyticsHelper: AnalyticsHelper, private val analyticsHelper: AnalyticsHelper,
) : ViewModel() { ) : ViewModel() {
@ -111,6 +113,24 @@ class SearchViewModel @Inject constructor(
recentSearchRepository.clearRecentSearches() recentSearchRepository.clearRecentSearches()
} }
} }
fun setNewsResourceBookmarked(newsResourceId: String, isChecked: Boolean) {
viewModelScope.launch {
userDataRepository.updateNewsResourceBookmark(newsResourceId, isChecked)
}
}
fun followTopic(followedTopicId: String, followed: Boolean) {
viewModelScope.launch {
userDataRepository.setTopicIdFollowed(followedTopicId, followed)
}
}
fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) {
viewModelScope.launch {
userDataRepository.setNewsResourceViewed(newsResourceId, viewed)
}
}
} }
private fun AnalyticsHelper.logEventSearchTriggered(query: String) = private fun AnalyticsHelper.logEventSearchTriggered(query: String) =

@ -33,6 +33,7 @@ import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.E
import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.Loading import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.Loading
import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.SearchNotReady import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.SearchNotReady
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -60,6 +61,7 @@ class SearchViewModelTest {
private val recentSearchRepository = TestRecentSearchRepository() private val recentSearchRepository = TestRecentSearchRepository()
private val getRecentQueryUseCase = GetRecentSearchQueriesUseCase(recentSearchRepository) private val getRecentQueryUseCase = GetRecentSearchQueriesUseCase(recentSearchRepository)
private val getSearchContentsCountUseCase = GetSearchContentsCountUseCase(searchContentsRepository) private val getSearchContentsCountUseCase = GetSearchContentsCountUseCase(searchContentsRepository)
private lateinit var viewModel: SearchViewModel private lateinit var viewModel: SearchViewModel
@Before @Before
@ -70,6 +72,7 @@ class SearchViewModelTest {
recentSearchQueriesUseCase = getRecentQueryUseCase, recentSearchQueriesUseCase = getRecentQueryUseCase,
savedStateHandle = SavedStateHandle(), savedStateHandle = SavedStateHandle(),
recentSearchRepository = recentSearchRepository, recentSearchRepository = recentSearchRepository,
userDataRepository = userDataRepository,
analyticsHelper = NoOpAnalyticsHelper(), analyticsHelper = NoOpAnalyticsHelper(),
) )
userDataRepository.setUserData(emptyUserData) userDataRepository.setUserData(emptyUserData)
@ -128,4 +131,22 @@ class SearchViewModelTest {
collectJob.cancel() collectJob.cancel()
} }
@Test
fun whenToggleNewsResourceSavedIsCalled_bookmarkStateIsUpdated() = runTest {
val newsResourceId = "123"
viewModel.setNewsResourceBookmarked(newsResourceId, true)
assertEquals(
expected = setOf(newsResourceId),
actual = userDataRepository.userData.first().bookmarkedNewsResources,
)
viewModel.setNewsResourceBookmarked(newsResourceId, false)
assertEquals(
expected = emptySet(),
actual = userDataRepository.userData.first().bookmarkedNewsResources,
)
}
} }

Loading…
Cancel
Save