feat: expose bookmark notes through UserData, UserNewsResource, and UserDataRepository

Co-Authored-By: Claude <noreply@anthropic.com>
pull/2125/head
Rohit Karadkar 3 months ago
parent 01ed90dcde
commit a37cc724f1

@ -65,4 +65,12 @@ class FakeUserDataRepository @Inject constructor(
override suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean) { override suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean) {
niaPreferencesDataSource.setShouldHideOnboarding(shouldHideOnboarding) niaPreferencesDataSource.setShouldHideOnboarding(shouldHideOnboarding)
} }
override suspend fun setBookmarkNote(newsResourceId: String, note: String) {
niaPreferencesDataSource.setBookmarkNote(newsResourceId, note)
}
override suspend fun removeBookmarkNote(newsResourceId: String) {
niaPreferencesDataSource.removeBookmarkNote(newsResourceId)
}
} }

@ -72,4 +72,12 @@ internal class OfflineFirstUserDataRepository @Inject constructor(
niaPreferencesDataSource.setShouldHideOnboarding(shouldHideOnboarding) niaPreferencesDataSource.setShouldHideOnboarding(shouldHideOnboarding)
analyticsHelper.logOnboardingStateChanged(shouldHideOnboarding) analyticsHelper.logOnboardingStateChanged(shouldHideOnboarding)
} }
override suspend fun setBookmarkNote(newsResourceId: String, note: String) {
niaPreferencesDataSource.setBookmarkNote(newsResourceId, note)
}
override suspend fun removeBookmarkNote(newsResourceId: String) {
niaPreferencesDataSource.removeBookmarkNote(newsResourceId)
}
} }

@ -67,4 +67,8 @@ interface UserDataRepository {
* Sets whether the user has completed the onboarding process. * Sets whether the user has completed the onboarding process.
*/ */
suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean) suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean)
suspend fun setBookmarkNote(newsResourceId: String, note: String)
suspend fun removeBookmarkNote(newsResourceId: String)
} }

@ -56,6 +56,7 @@ class NiaPreferencesDataSource @Inject constructor(
}, },
useDynamicColor = it.useDynamicColor, useDynamicColor = it.useDynamicColor,
shouldHideOnboarding = it.shouldHideOnboarding, shouldHideOnboarding = it.shouldHideOnboarding,
bookmarkNotes = it.bookmarkNotesMap,
) )
} }

@ -23,7 +23,9 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
class NiaPreferencesDataSourceTest { class NiaPreferencesDataSourceTest {
@ -87,6 +89,27 @@ class NiaPreferencesDataSourceTest {
assertTrue(subject.userData.first().useDynamicColor) assertTrue(subject.userData.first().useDynamicColor)
} }
// TODO(Task 2): Add tests for setBookmarkNote, removeBookmarkNote, and @Test
// bookmarkNotes auto-delete on unbookmark once UserData exposes bookmarkNotes. fun setBookmarkNote_persistsNote() = runTest {
subject.setBookmarkNote("news1", "my note")
val prefs = subject.userData.first()
assertEquals("my note", prefs.bookmarkNotes["news1"])
}
@Test
fun removeBookmarkNote_deletesNote() = runTest {
subject.setBookmarkNote("news1", "my note")
subject.removeBookmarkNote("news1")
val prefs = subject.userData.first()
assertNull(prefs.bookmarkNotes["news1"])
}
@Test
fun setNewsResourceBookmarked_false_deletesNote() = runTest {
subject.setNewsResourceBookmarked("news1", true)
subject.setBookmarkNote("news1", "my note")
subject.setNewsResourceBookmarked("news1", false)
val prefs = subject.userData.first()
assertNull(prefs.bookmarkNotes["news1"])
}
} }

@ -27,4 +27,5 @@ data class UserData(
val darkThemeConfig: DarkThemeConfig, val darkThemeConfig: DarkThemeConfig,
val useDynamicColor: Boolean, val useDynamicColor: Boolean,
val shouldHideOnboarding: Boolean, val shouldHideOnboarding: Boolean,
val bookmarkNotes: Map<String, String> = emptyMap(),
) )

@ -23,34 +23,27 @@ import kotlinx.datetime.Instant
* news resource's topics and whether they have saved (bookmarked) this news resource. * news resource's topics and whether they have saved (bookmarked) this news resource.
*/ */
data class UserNewsResource internal constructor( data class UserNewsResource internal constructor(
val id: String, val newsResource: NewsResource,
val title: String, val userData: UserData,
val content: String,
val url: String,
val headerImageUrl: String?,
val publishDate: Instant,
val type: String,
val followableTopics: List<FollowableTopic>,
val isSaved: Boolean,
val hasBeenViewed: Boolean,
) { ) {
constructor(newsResource: NewsResource, userData: UserData) : this( val id: String get() = newsResource.id
id = newsResource.id, val title: String get() = newsResource.title
title = newsResource.title, val content: String get() = newsResource.content
content = newsResource.content, val url: String get() = newsResource.url
url = newsResource.url, val headerImageUrl: String? get() = newsResource.headerImageUrl
headerImageUrl = newsResource.headerImageUrl, val publishDate: Instant get() = newsResource.publishDate
publishDate = newsResource.publishDate, val type: String get() = newsResource.type
type = newsResource.type, val followableTopics: List<FollowableTopic>
followableTopics = newsResource.topics.map { topic -> get() = newsResource.topics.map { topic ->
FollowableTopic( FollowableTopic(
topic = topic, topic = topic,
isFollowed = topic.id in userData.followedTopics, isFollowed = topic.id in userData.followedTopics,
) )
}, }
isSaved = newsResource.id in userData.bookmarkedNewsResources, val isSaved: Boolean get() = newsResource.id in userData.bookmarkedNewsResources
hasBeenViewed = newsResource.id in userData.viewedNewsResources, val hasBeenViewed: Boolean get() = newsResource.id in userData.viewedNewsResources
) val bookmarkNote: String?
get() = userData.bookmarkNotes[newsResource.id].takeIf { !it.isNullOrBlank() }
} }
fun List<NewsResource>.mapToUserNewsResources(userData: UserData): List<UserNewsResource> = fun List<NewsResource>.mapToUserNewsResources(userData: UserData): List<UserNewsResource> =

@ -112,6 +112,22 @@ class TestUserDataRepository : UserDataRepository {
} }
} }
override suspend fun setBookmarkNote(newsResourceId: String, note: String) {
currentUserData.let { current ->
_userData.tryEmit(
current.copy(bookmarkNotes = current.bookmarkNotes + (newsResourceId to note)),
)
}
}
override suspend fun removeBookmarkNote(newsResourceId: String) {
currentUserData.let { current ->
_userData.tryEmit(
current.copy(bookmarkNotes = current.bookmarkNotes - newsResourceId),
)
}
}
/** /**
* A test-only API to allow setting of user data directly. * A test-only API to allow setting of user data directly.
*/ */

Loading…
Cancel
Save