Skip to content

Commit 6df7a1f

Browse files
committed
Expose updated methods within bookmarks dao
1 parent 7cd7818 commit 6df7a1f

4 files changed

Lines changed: 138 additions & 2 deletions

File tree

app/src/test/kotlin/com/quran/labs/androidquran/fakes/FakeBookmarksDao.kt

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import kotlinx.coroutines.flow.map
1717
import kotlinx.coroutines.flow.update
1818
import kotlin.time.Instant
1919

20-
class FakeBookmarksDao : BookmarksDao {
20+
class FakeBookmarksDao(
21+
private val pageForSuraAyah: (SuraAyah) -> Int = { 0 }
22+
) : BookmarksDao {
2123
private val bookmarks = MutableStateFlow<List<Bookmark>>(emptyList())
2224
private val tags = MutableStateFlow<List<Tag>>(emptyList())
2325
private val defaultBookmarkIds = MutableStateFlow<Set<String>>(emptySet())
@@ -81,6 +83,16 @@ class FakeBookmarksDao : BookmarksDao {
8183
}
8284
}
8385

86+
override suspend fun addCollection(name: String): ReadingCollection {
87+
val id = addTag(name)
88+
return ReadingCollection(
89+
id = id,
90+
name = name,
91+
lastUpdated = currentTimestampSeconds().toInstant(),
92+
isSystem = false
93+
)
94+
}
95+
8496
override suspend fun tags(): List<Tag> {
8597
return tags.value
8698
}
@@ -200,13 +212,90 @@ class FakeBookmarksDao : BookmarksDao {
200212
changesFlow.tryEmit(Unit)
201213
}
202214

215+
override suspend fun deleteAyahBookmark(suraAyah: SuraAyah): Boolean {
216+
val bookmarkIds = bookmarks.value
217+
.filter { bookmark -> bookmark.sura == suraAyah.sura && bookmark.ayah == suraAyah.ayah }
218+
.map { bookmark -> bookmark.id }
219+
.toSet()
220+
if (bookmarkIds.isEmpty()) {
221+
return false
222+
}
223+
224+
bookmarks.update { current -> current.filterNot { bookmark -> bookmark.id in bookmarkIds } }
225+
defaultBookmarkIds.update { current -> current - bookmarkIds }
226+
changesFlow.tryEmit(Unit)
227+
return true
228+
}
229+
203230
override suspend fun replaceAyahBookmarks(bookmarks: List<Bookmark>) {
204231
val ayahBookmarks = bookmarks.filter { bookmark -> bookmark.sura != null && bookmark.ayah != null }
205232
this.bookmarks.value = ayahBookmarks
206233
defaultBookmarkIds.value = ayahBookmarks.map { bookmark -> bookmark.id }.toSet()
207234
changesFlow.tryEmit(Unit)
208235
}
209236

237+
override suspend fun replaceAyahBookmarkCollections(
238+
suraAyah: SuraAyah,
239+
collectionIds: Set<String>
240+
): Boolean {
241+
val targetCollectionIds = normalizedCollectionIds(collectionIds)
242+
val customCollectionIds = targetCollectionIds.filterNot { collectionId ->
243+
collectionId == DEFAULT_BOOKMARK_COLLECTION_ID
244+
}.toSet()
245+
val isDefaultBookmark = DEFAULT_BOOKMARK_COLLECTION_ID in targetCollectionIds
246+
val existingBookmark = bookmarks.value.firstOrNull { bookmark ->
247+
bookmark.sura == suraAyah.sura && bookmark.ayah == suraAyah.ayah
248+
}
249+
250+
if (existingBookmark == null) {
251+
val id = "bookmark-${bookmarks.value.size + 1}"
252+
bookmarks.update { current ->
253+
current + Bookmark(
254+
id = id,
255+
sura = suraAyah.sura,
256+
ayah = suraAyah.ayah,
257+
page = pageForSuraAyah(suraAyah),
258+
timestamp = currentTimestampSeconds(),
259+
tags = customCollectionIds.toList()
260+
)
261+
}
262+
if (isDefaultBookmark) {
263+
defaultBookmarkIds.update { current -> current + id }
264+
}
265+
changesFlow.tryEmit(Unit)
266+
return true
267+
}
268+
269+
val customCollectionsChanged = existingBookmark.tags.toSet() != customCollectionIds
270+
val defaultCollectionChanged = (existingBookmark.id in defaultBookmarkIds.value) != isDefaultBookmark
271+
if (!customCollectionsChanged && !defaultCollectionChanged) {
272+
return false
273+
}
274+
275+
if (customCollectionsChanged) {
276+
bookmarks.update { current ->
277+
current.map { bookmark ->
278+
if (bookmark.id == existingBookmark.id) {
279+
bookmark.copy(tags = customCollectionIds.toList())
280+
} else {
281+
bookmark
282+
}
283+
}
284+
}
285+
}
286+
if (defaultCollectionChanged) {
287+
defaultBookmarkIds.update { current ->
288+
if (isDefaultBookmark) {
289+
current + existingBookmark.id
290+
} else {
291+
current - existingBookmark.id
292+
}
293+
}
294+
}
295+
changesFlow.tryEmit(Unit)
296+
return true
297+
}
298+
210299
override suspend fun isSuraAyahBookmarked(suraAyah: SuraAyah): Boolean {
211300
return bookmarks.value.any { it.sura == suraAyah.sura && it.ayah == suraAyah.ayah }
212301
}
@@ -299,6 +388,19 @@ class FakeBookmarksDao : BookmarksDao {
299388
}
300389
}
301390

391+
private fun normalizedCollectionIds(collectionIds: Set<String>): Set<String> {
392+
return collectionIds
393+
.map { collectionId -> collectionId.trim() }
394+
.filter { collectionId -> collectionId.isNotEmpty() }
395+
.distinct()
396+
.ifEmpty { listOf(DEFAULT_BOOKMARK_COLLECTION_ID) }
397+
.toSet()
398+
}
399+
400+
private fun currentTimestampSeconds(): Long {
401+
return System.currentTimeMillis() / 1000
402+
}
403+
302404
private companion object {
303405
private const val DEFAULT_COLLECTION_NAME = "Default"
304406
private const val EPOCH_SECONDS_UPPER_BOUND = 10_000_000_000L

common/bookmark/src/main/java/com/quran/mobile/bookmark/model/BookmarksDaoImpl.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.quran.data.di.AppScope
1010
import com.quran.data.model.SuraAyah
1111
import com.quran.data.model.bookmark.Bookmark
1212
import com.quran.data.model.bookmark.Tag
13+
import com.quran.data.model.collection.ReadingCollection
1314
import com.quran.data.model.collection.ReadingCollectionBookmarks
1415
import com.quran.mobile.bookmark.sync.LocalDataChangeNotifier
1516
import com.quran.mobile.bookmark.sync.notifyLocalDataChanged
@@ -108,6 +109,13 @@ class BookmarksDaoImpl @Inject constructor(
108109
.map { collections -> collections.map { it.asReadingCollectionBookmarks() } }
109110
}
110111

112+
override suspend fun addCollection(name: String): ReadingCollection {
113+
return withContext(Dispatchers.IO) {
114+
collectionsRepository.addCollection(name)
115+
.asReadingCollection()
116+
}
117+
}
118+
111119
override fun bookmarksForPage(page: Int): Flow<List<Bookmark>> {
112120
return bookmarksFlow(BookmarkSortOrder.SORT_LOCATION)
113121
.map { bookmarks -> bookmarks.filter { it.page == page } }
@@ -316,6 +324,12 @@ class BookmarksDaoImpl @Inject constructor(
316324
}
317325
}
318326

327+
override suspend fun deleteAyahBookmark(suraAyah: SuraAyah): Boolean {
328+
return withContext(Dispatchers.IO) {
329+
bookmarksRepository.deleteBookmark(suraAyah.sura, suraAyah.ayah)
330+
}
331+
}
332+
319333
override suspend fun replaceAyahBookmarks(bookmarks: List<Bookmark>) {
320334
val replaced = withContext(Dispatchers.IO) {
321335
val quranInfo = quranInfoProvider()
@@ -342,6 +356,19 @@ class BookmarksDaoImpl @Inject constructor(
342356
}
343357
}
344358

359+
override suspend fun replaceAyahBookmarkCollections(
360+
suraAyah: SuraAyah,
361+
collectionIds: Set<String>
362+
): Boolean {
363+
return withContext(Dispatchers.IO) {
364+
bookmarksRepository.replaceAyahBookmarkCollections(
365+
suraAyah.sura,
366+
suraAyah.ayah,
367+
collectionIds.toList()
368+
).changed
369+
}
370+
}
371+
345372
override suspend fun isSuraAyahBookmarked(suraAyah: SuraAyah): Boolean {
346373
return withContext(Dispatchers.IO) {
347374
bookmarkForSuraAyah(suraAyah) != null

common/bookmark/src/main/java/com/quran/mobile/bookmark/model/Extensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ internal fun CollectionWithAyahBookmarks.asReadingCollectionBookmarks(): Reading
1414
)
1515
}
1616

17-
private fun Collection.asReadingCollection(): ReadingCollection {
17+
internal fun Collection.asReadingCollection(): ReadingCollection {
1818
return ReadingCollection(
1919
id = localId,
2020
name = name,

common/data/src/main/java/com/quran/data/dao/BookmarksDao.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.quran.data.dao
33
import com.quran.data.model.SuraAyah
44
import com.quran.data.model.bookmark.Bookmark
55
import com.quran.data.model.bookmark.Tag
6+
import com.quran.data.model.collection.ReadingCollection
67
import com.quran.data.model.collection.ReadingCollectionBookmarks
78
import kotlinx.coroutines.flow.Flow
89

@@ -19,6 +20,7 @@ interface BookmarksDao {
1920
fun bookmarksForPage(page: Int): Flow<List<Bookmark>>
2021

2122
fun collectionsWithBookmarksFlow(): Flow<List<ReadingCollectionBookmarks>>
23+
suspend fun addCollection(name: String): ReadingCollection
2224

2325
suspend fun tags(): List<Tag>
2426
fun tagsFlow(): Flow<List<Tag>>
@@ -43,7 +45,12 @@ interface BookmarksDao {
4345

4446
suspend fun removeBookmarks(bookmarks: List<Bookmark>)
4547
suspend fun removeBookmarksForPage(page: Int)
48+
suspend fun deleteAyahBookmark(suraAyah: SuraAyah): Boolean
4649
suspend fun replaceAyahBookmarks(bookmarks: List<Bookmark>)
4750
suspend fun isSuraAyahBookmarked(suraAyah: SuraAyah): Boolean
51+
suspend fun replaceAyahBookmarkCollections(
52+
suraAyah: SuraAyah,
53+
collectionIds: Set<String>
54+
): Boolean
4855
suspend fun toggleAyahBookmark(suraAyah: SuraAyah, page: Int): Boolean
4956
}

0 commit comments

Comments
 (0)