@@ -17,7 +17,9 @@ import kotlinx.coroutines.flow.map
1717import kotlinx.coroutines.flow.update
1818import 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
0 commit comments