diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/InterestsItem.kt
similarity index 95%
rename from feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt
rename to core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/InterestsItem.kt
index 6ac0340ee..28cd8d938 100644
--- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt
+++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/InterestsItem.kt
@@ -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");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* 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.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.icon.NiaIcons
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
fun InterestsItem(
@@ -70,7 +70,7 @@ fun InterestsItem(
Icon(
imageVector = NiaIcons.Add,
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(
imageVector = NiaIcons.Check,
contentDescription = stringResource(
- id = string.feature_interests_card_unfollow_button_content_desc,
+ id = string.core_ui_interests_card_unfollow_button_content_desc,
),
)
},
diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml
index 65a855fc9..ab76748ef 100644
--- a/core/ui/src/main/res/values/strings.xml
+++ b/core/ui/src/main/res/values/strings.xml
@@ -26,4 +26,7 @@
%1$s is followed
%1$s is not followed
+
+ Follow interest
+ Unfollow interest
diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt
index b75573975..2fbdf0a79 100644
--- a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt
+++ b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt
@@ -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.feature.foryou.navigation.LINKED_NEWS_RESOURCE_ID
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
@@ -70,6 +71,7 @@ class ForYouViewModelTest {
topicsRepository = topicsRepository,
userDataRepository = userDataRepository,
)
+
private val savedStateHandle = SavedStateHandle()
private lateinit var viewModel: ForYouViewModel
@@ -504,6 +506,24 @@ class ForYouViewModelTest {
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(
diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt
index 4a48645c5..83058c12e 100644
--- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt
+++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt
@@ -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.scrollbarState
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
+import com.google.samples.apps.nowinandroid.core.ui.InterestsItem
@Composable
fun TopicsTabContent(
diff --git a/feature/interests/src/main/res/values/strings.xml b/feature/interests/src/main/res/values/strings.xml
index 2dd1c18a9..8d5322859 100644
--- a/feature/interests/src/main/res/values/strings.xml
+++ b/feature/interests/src/main/res/values/strings.xml
@@ -18,6 +18,4 @@
Interests
Loading data
"No available data"
- Follow interest
- Unfollow interest
diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts
index 206f4c0f9..98052e9ab 100644
--- a/feature/search/build.gradle.kts
+++ b/feature/search/build.gradle.kts
@@ -27,9 +27,7 @@ android {
dependencies {
implementation(projects.core.data)
implementation(projects.core.domain)
- implementation(projects.feature.bookmarks)
- implementation(projects.feature.foryou)
- implementation(projects.feature.interests)
+ implementation(projects.core.ui)
testImplementation(projects.core.testing)
diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt
index ca159c80b..1e4711016 100644
--- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt
+++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt
@@ -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.UserNewsResource
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.R.string
import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
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
@Composable
@@ -104,10 +101,7 @@ internal fun SearchRoute(
onInterestsClick: () -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
- bookmarksViewModel: BookmarksViewModel = hiltViewModel(),
- interestsViewModel: InterestsViewModel = hiltViewModel(),
searchViewModel: SearchViewModel = hiltViewModel(),
- forYouViewModel: ForYouViewModel = hiltViewModel(),
) {
val recentSearchQueriesUiState by searchViewModel.recentSearchQueriesUiState.collectAsStateWithLifecycle()
val searchResultUiState by searchViewModel.searchResultUiState.collectAsStateWithLifecycle()
@@ -120,9 +114,9 @@ internal fun SearchRoute(
onSearchQueryChanged = searchViewModel::onSearchQueryChanged,
onSearchTriggered = searchViewModel::onSearchTriggered,
onClearRecentSearches = searchViewModel::clearRecentSearches,
- onNewsResourcesCheckedChanged = forYouViewModel::updateNewsResourceSaved,
- onNewsResourceViewed = { bookmarksViewModel.setNewsResourceViewed(it, true) },
- onFollowButtonClick = interestsViewModel::followTopic,
+ onNewsResourcesCheckedChanged = searchViewModel::setNewsResourceBookmarked,
+ onNewsResourceViewed = { searchViewModel.setNewsResourceViewed(it, true) },
+ onFollowButtonClick = searchViewModel::followTopic,
onBackClick = onBackClick,
onInterestsClick = onInterestsClick,
onTopicClick = onTopicClick,
diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt
index 7c05f81c5..43d5b9405 100644
--- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt
+++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt
@@ -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.AnalyticsHelper
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.GetSearchContentsCountUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase
@@ -44,6 +45,7 @@ class SearchViewModel @Inject constructor(
getSearchContentsCountUseCase: GetSearchContentsCountUseCase,
recentSearchQueriesUseCase: GetRecentSearchQueriesUseCase,
private val recentSearchRepository: RecentSearchRepository,
+ private val userDataRepository: UserDataRepository,
private val savedStateHandle: SavedStateHandle,
private val analyticsHelper: AnalyticsHelper,
) : ViewModel() {
@@ -111,6 +113,24 @@ class SearchViewModel @Inject constructor(
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) =
diff --git a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt
index da0d5654e..662afca7d 100644
--- a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt
+++ b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt
@@ -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.SearchNotReady
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -60,6 +61,7 @@ class SearchViewModelTest {
private val recentSearchRepository = TestRecentSearchRepository()
private val getRecentQueryUseCase = GetRecentSearchQueriesUseCase(recentSearchRepository)
private val getSearchContentsCountUseCase = GetSearchContentsCountUseCase(searchContentsRepository)
+
private lateinit var viewModel: SearchViewModel
@Before
@@ -70,6 +72,7 @@ class SearchViewModelTest {
recentSearchQueriesUseCase = getRecentQueryUseCase,
savedStateHandle = SavedStateHandle(),
recentSearchRepository = recentSearchRepository,
+ userDataRepository = userDataRepository,
analyticsHelper = NoOpAnalyticsHelper(),
)
userDataRepository.setUserData(emptyUserData)
@@ -128,4 +131,22 @@ class SearchViewModelTest {
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,
+ )
+ }
}