From 1cbdcf69e096dee870b4873339abcf11f0b24072 Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Sat, 4 Jul 2026 22:28:45 +0300 Subject: [PATCH] Clean up the api Applications using the sdk should not know about things like remote ids, local ids, etc. This patch attempts to clean up some of these leaks. --- .../shared/demo/android/ui/SyncViewModel.kt | 4 +- .../demo/android/ui/auth/CollectionsTab.kt | 4 +- .../shared/demo/android/ui/auth/NotesTab.kt | 2 +- .../QuranSyncDemo/BookmarksTabView.swift | 2 +- .../QuranSyncDemo/CollectionsTabView.swift | 10 +- .../QuranSyncDemo/NotesTabView.swift | 4 +- .../QuranSyncDemo/SyncViewModel.swift | 14 +- .../shared/persistence/model/AyahBookmark.kt | 11 +- .../shared/persistence/model/Collection.kt | 11 +- .../model/CollectionAyahBookmark.kt | 20 +- .../quran/shared/persistence/model/Note.kt | 13 +- .../persistence/model/ReadingBookmark.kt | 24 ++- .../persistence/model/ReadingSession.kt | 10 +- .../extension/BookmarkQueriesExtensions.kt | 2 +- .../repository/BookmarksRepository.kt | 18 +- .../repository/BookmarksRepositoryImpl.kt | 44 ++--- .../extension/CollectionQueriesExtensions.kt | 2 +- .../repository/CollectionsRepository.kt | 16 +- .../repository/CollectionsRepositoryImpl.kt | 36 ++-- .../CollectionBookmarksRepository.kt | 14 +- .../CollectionBookmarksRepositoryImpl.kt | 164 +++++++++------ ...ctionBookmarksSynchronizationRepository.kt | 3 +- .../note/extension/NoteQueriesExtensions.kt | 2 +- .../note/repository/NotesRepository.kt | 10 +- .../note/repository/NotesRepositoryImpl.kt | 25 +-- .../ReadingBookmarkQueriesExtensions.kt | 14 +- .../ReadingSessionsQueriesExtensions.kt | 2 +- .../repository/ReadingSessionsRepository.kt | 8 +- .../ReadingSessionsRepositoryImpl.kt | 28 +-- .../persistence/bookmark_collections.sq | 8 +- .../BookmarkSyncArchitectureTest.kt | 186 ++++++++++-------- .../repository/CollectionsRepositoryTest.kt | 72 +++---- .../repository/NotesRepositoryTest.kt | 58 +++--- .../ReadingSessionsRepositoryTest.kt | 100 +++++----- .../quran/shared/pipeline/QuranDataService.kt | 112 +++++------ .../shared/pipeline/SyncEnginePipeline.kt | 14 +- .../pipeline/QuranDataServiceLifecycleTest.kt | 167 ++++++++-------- 37 files changed, 669 insertions(+), 565 deletions(-) diff --git a/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/SyncViewModel.kt b/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/SyncViewModel.kt index e2c78a06..9a8ce7f2 100644 --- a/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/SyncViewModel.kt +++ b/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/SyncViewModel.kt @@ -103,7 +103,7 @@ class SyncViewModel( service.deleteNote(noteId) } - fun getBookmarksForCollectionFlow(collectionLocalId: String): Flow> = - service.getBookmarksForCollectionFlow(collectionLocalId) + fun getBookmarksForCollectionFlow(collectionId: String): Flow> = + service.getBookmarksForCollectionFlow(collectionId) } diff --git a/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/auth/CollectionsTab.kt b/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/auth/CollectionsTab.kt index fbe7c6f0..f359fa10 100644 --- a/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/auth/CollectionsTab.kt +++ b/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/auth/CollectionsTab.kt @@ -68,7 +68,7 @@ fun CollectionsTab( ListItem( headlineContent = { Text(item.collection.name) }, modifier = Modifier.clickable { - onAddRandomBookmarkToCollection(item.collection.localId) + onAddRandomBookmarkToCollection(item.collection.id) showSelectCollectionDialog = false } ) @@ -117,7 +117,7 @@ fun CollectionsTab( items(collectionsWithBookmarks) { collectionWithBookmarks -> CollectionItem( collectionWithBookmarks = collectionWithBookmarks, - onDelete = { onDeleteCollection(collectionWithBookmarks.collection.localId) } + onDelete = { onDeleteCollection(collectionWithBookmarks.collection.id) } ) } } diff --git a/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/auth/NotesTab.kt b/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/auth/NotesTab.kt index d537a1be..962b09da 100644 --- a/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/auth/NotesTab.kt +++ b/demo/android/src/main/kotlin/com/quran/shared/demo/android/ui/auth/NotesTab.kt @@ -82,7 +82,7 @@ fun NotesTab( items(notes) { note -> NoteItem( note = note, - onDelete = { onDeleteNote(note.localId) } + onDelete = { onDeleteNote(note.id) } ) } } diff --git a/demo/apple/QuranSyncDemo/QuranSyncDemo/BookmarksTabView.swift b/demo/apple/QuranSyncDemo/QuranSyncDemo/BookmarksTabView.swift index 9b87d710..1fdb7496 100644 --- a/demo/apple/QuranSyncDemo/QuranSyncDemo/BookmarksTabView.swift +++ b/demo/apple/QuranSyncDemo/QuranSyncDemo/BookmarksTabView.swift @@ -80,7 +80,7 @@ struct BookmarksTabView: View { .foregroundColor(.secondary) .italic() } else { - ForEach(viewModel.bookmarks, id: \.localId) { bookmark in + ForEach(viewModel.bookmarks, id: \.id) { bookmark in HStack { Image(systemName: "bookmark.fill") .foregroundColor(.accentColor) diff --git a/demo/apple/QuranSyncDemo/QuranSyncDemo/CollectionsTabView.swift b/demo/apple/QuranSyncDemo/QuranSyncDemo/CollectionsTabView.swift index ca3404b0..a2207832 100644 --- a/demo/apple/QuranSyncDemo/QuranSyncDemo/CollectionsTabView.swift +++ b/demo/apple/QuranSyncDemo/QuranSyncDemo/CollectionsTabView.swift @@ -28,7 +28,7 @@ struct CollectionsTabView: View { .foregroundColor(.secondary) .italic() } else { - ForEach(viewModel.collectionsWithBookmarks, id: \.collection.localId) { collectionWithBookmarks in + ForEach(viewModel.collectionsWithBookmarks, id: \.collection.id) { collectionWithBookmarks in CollectionRowView(viewModel: viewModel, collectionWithBookmarks: collectionWithBookmarks) } } @@ -51,12 +51,12 @@ struct CollectionsTabView: View { } .sheet(isPresented: $showSelectCollectionSheet) { NavigationView { - List(viewModel.collectionsWithBookmarks, id: \.collection.localId) { item in + List(viewModel.collectionsWithBookmarks, id: \.collection.id) { item in Button(action: { Task { let sura = Shared.QuranActionsUtils().getRandomSura() let ayah = Shared.QuranActionsUtils().getRandomAyah(sura: sura) - await viewModel.addAyahBookmarkToCollection(collectionId: item.collection.localId, + await viewModel.addAyahBookmarkToCollection(collectionId: item.collection.id, sura: sura, ayah: ayah) } @@ -98,7 +98,7 @@ struct CollectionRowView: View { Button(action: { Task { - await viewModel.deleteCollection(collectionId: collectionWithBookmarks.collection.localId) + await viewModel.deleteCollection(collectionId: collectionWithBookmarks.collection.id) } }) { Image(systemName: "trash") @@ -115,7 +115,7 @@ struct CollectionRowView: View { .foregroundColor(.secondary) .padding(.leading, 32) } else { - ForEach(collectionWithBookmarks.bookmarks, id: \.localId) { cb in + ForEach(collectionWithBookmarks.bookmarks, id: \.bookmarkId) { cb in HStack { Image(systemName: "bookmark") .font(.caption) diff --git a/demo/apple/QuranSyncDemo/QuranSyncDemo/NotesTabView.swift b/demo/apple/QuranSyncDemo/QuranSyncDemo/NotesTabView.swift index a5e411b0..b14d784d 100644 --- a/demo/apple/QuranSyncDemo/QuranSyncDemo/NotesTabView.swift +++ b/demo/apple/QuranSyncDemo/QuranSyncDemo/NotesTabView.swift @@ -20,7 +20,7 @@ struct NotesTabView: View { .foregroundColor(.secondary) .italic() } else { - ForEach(viewModel.notes, id: \.localId) { note in + ForEach(viewModel.notes, id: \.id) { note in HStack { Image(systemName: "note.text") .foregroundColor(.blue) @@ -34,7 +34,7 @@ struct NotesTabView: View { Spacer() Button(action: { Task { - await viewModel.deleteNote(localId: note.localId) + await viewModel.deleteNote(id: note.id) } }) { Image(systemName: "trash") diff --git a/demo/apple/QuranSyncDemo/QuranSyncDemo/SyncViewModel.swift b/demo/apple/QuranSyncDemo/QuranSyncDemo/SyncViewModel.swift index d86f71d0..1012b446 100644 --- a/demo/apple/QuranSyncDemo/QuranSyncDemo/SyncViewModel.swift +++ b/demo/apple/QuranSyncDemo/QuranSyncDemo/SyncViewModel.swift @@ -151,7 +151,7 @@ class SyncViewModel: ObservableObject { func deleteCollection(collectionId: String) async { do { - try await asyncFunction(for: quranDataService.deleteCollection(localId: collectionId)) + try await asyncFunction(for: quranDataService.deleteCollection(id: collectionId)) } catch { print("SyncViewModel: Failed to delete collection: \(error)") } @@ -173,9 +173,9 @@ class SyncViewModel: ObservableObject { } } - func deleteNote(localId: String) async { + func deleteNote(id: String) async { do { - try await asyncFunction(for: quranDataService.deleteNote(localId: localId)) + try await asyncFunction(for: quranDataService.deleteNote(id: id)) } catch { print("SyncViewModel: Failed to delete note: \(error)") } @@ -183,7 +183,7 @@ class SyncViewModel: ObservableObject { func addBookmarkToCollection(collectionId: String, bookmark: Shared.AyahBookmark) async { do { - try await asyncFunction(for: quranDataService.addBookmarkToCollection(collectionLocalId: collectionId, bookmark: bookmark)) + try await asyncFunction(for: quranDataService.addBookmarkToCollection(collectionId: collectionId, bookmark: bookmark)) } catch { print("SyncViewModel: Failed to add bookmark to collection: \(error)") } @@ -191,7 +191,7 @@ class SyncViewModel: ObservableObject { func removeBookmarkFromCollection(collectionId: String, bookmark: Shared.AyahBookmark) async { do { - try await asyncFunction(for: quranDataService.removeBookmarkFromCollection(collectionLocalId: collectionId, bookmark: bookmark)) + try await asyncFunction(for: quranDataService.removeBookmarkFromCollection(collectionId: collectionId, bookmark: bookmark)) } catch { print("SyncViewModel: Failed to remove bookmark from collection: \(error)") } @@ -199,14 +199,14 @@ class SyncViewModel: ObservableObject { func addAyahBookmarkToCollection(collectionId: String, sura: Int32, ayah: Int32) async { do { - try await asyncFunction(for: quranDataService.addAyahBookmarkToCollection(collectionLocalId: collectionId, sura: sura, ayah: ayah)) + try await asyncFunction(for: quranDataService.addAyahBookmarkToCollection(collectionId: collectionId, sura: sura, ayah: ayah)) } catch { print("SyncViewModel: Failed to add random bookmark to collection: \(error)") } } func bookmarksForCollection(collectionId: String) -> any AsyncSequence { - return asyncSequence(for: quranDataService.getBookmarksForCollectionFlow(collectionLocalId: collectionId)) + return asyncSequence(for: quranDataService.getBookmarksForCollectionFlow(collectionId: collectionId)) } func login() async throws { diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/AyahBookmark.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/AyahBookmark.kt index 6d757585..98e9543d 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/AyahBookmark.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/AyahBookmark.kt @@ -2,10 +2,19 @@ package com.quran.shared.persistence.model import com.quran.shared.persistence.util.PlatformDateTime +/** + * App-facing saved ayah bookmark. + * + * @param sura the sura number + * @param ayah the ayah number + * @param id the identifier for the ayah bookmark + * @param lastUpdated when the bookmark was last updated + * @param addedDate when the bookmark was first added + */ data class AyahBookmark( val sura: Int, val ayah: Int, - val localId: String, + val id: String, val lastUpdated: PlatformDateTime, val addedDate: PlatformDateTime = lastUpdated ) diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/Collection.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/Collection.kt index 907c613c..c78153f9 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/Collection.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/Collection.kt @@ -5,19 +5,18 @@ import com.quran.shared.persistence.util.PlatformDateTime /** * App-facing collection model. * - * The default bookmark collection is virtual: it is identified by [DEFAULT_COLLECTION_ID] and is - * derived from bookmark membership state instead of a row in the collection table. + * @param name the name of the collection + * @param lastUpdated when the collection was last updated + * @param id the identifier of the collection */ data class Collection( val name: String, val lastUpdated: PlatformDateTime, - val localId: String + val id: String ) { /** * True when this collection represents the virtual default bookmark collection. - * - * This value is derived from [localId] so callers do not pass or persist a separate flag. */ val isDefault: Boolean - get() = localId == DEFAULT_COLLECTION_ID + get() = id == DEFAULT_COLLECTION_ID } diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/CollectionAyahBookmark.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/CollectionAyahBookmark.kt index 031cde8c..e839a656 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/CollectionAyahBookmark.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/CollectionAyahBookmark.kt @@ -2,13 +2,21 @@ package com.quran.shared.persistence.model import com.quran.shared.persistence.util.PlatformDateTime +/** + * App-facing ayah bookmark membership in a collection. + * + * @param collectionId the id of the collection + * @param bookmarkId the id of the bookmark + * @param sura the sura number + * @param ayah the ayah number + * @param bookmarkLastUpdated the last time the bookmark itself changed + * @param bookmarkAddedDate the time the bookmark itself was created locally + */ data class CollectionAyahBookmark( - val collectionLocalId: String, - val collectionRemoteId: String?, - val bookmarkLocalId: String, - val bookmarkRemoteId: String?, + val collectionId: String, + val bookmarkId: String, val sura: Int, val ayah: Int, - val lastUpdated: PlatformDateTime, - val localId: String + val bookmarkLastUpdated: PlatformDateTime, + val bookmarkAddedDate: PlatformDateTime ) diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/Note.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/Note.kt index b553132e..91b09a41 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/Note.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/Note.kt @@ -2,6 +2,17 @@ package com.quran.shared.persistence.model import com.quran.shared.persistence.util.PlatformDateTime +/** + * App-facing note attached to a Quran ayah range. + * + * @param body the text of the note + * @param startSura the start sura + * @param startAyah the start ayah + * @param endSura the end sura + * @param endAyah the end ayah + * @param lastUpdated when the note was last updated + * @param id the identifier of the note + */ data class Note( val body: String, val startSura: Int, @@ -9,5 +20,5 @@ data class Note( val endSura: Int, val endAyah: Int, val lastUpdated: PlatformDateTime, - val localId: String + val id: String ) diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/ReadingBookmark.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/ReadingBookmark.kt index 5c1b9c32..20db2e49 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/ReadingBookmark.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/ReadingBookmark.kt @@ -2,20 +2,38 @@ package com.quran.shared.persistence.model import com.quran.shared.persistence.util.PlatformDateTime +/** + * App-facing current reading bookmark. + */ sealed interface ReadingBookmark { val lastUpdated: PlatformDateTime - val localId: String + val id: String } +/** + * Ayah Reading Bookmark + * + * @param sura the sura + * @parma ayah the ayah + * @param lastUpdated the last updated + * @param id the identifier of the ayah reading bookmark + */ data class AyahReadingBookmark( val sura: Int, val ayah: Int, override val lastUpdated: PlatformDateTime, - override val localId: String + override val id: String ) : ReadingBookmark +/** + * Page Reading Bookmark + * + * @param page the page + * @param lastUpdated the last updated time of the ayah reading bookmark + * @param id the identifier of the page reading bookmark + */ data class PageReadingBookmark( val page: Int, override val lastUpdated: PlatformDateTime, - override val localId: String + override val id: String ) : ReadingBookmark diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/ReadingSession.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/ReadingSession.kt index 32b96917..5bb035f5 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/ReadingSession.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/ReadingSession.kt @@ -2,9 +2,17 @@ package com.quran.shared.persistence.model import com.quran.shared.persistence.util.PlatformDateTime +/** + * App-facing reading-session entry. + * + * @param sura the sura + * @param ayah the ayah + * @param lastUpdated the last updated time of the reading session + * @param id the identifier of the reading session + */ data class ReadingSession( val sura: Int, val ayah: Int, val lastUpdated: PlatformDateTime, - val localId: String + val id: String ) diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/extension/BookmarkQueriesExtensions.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/extension/BookmarkQueriesExtensions.kt index 3d4ba19a..3ce163aa 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/extension/BookmarkQueriesExtensions.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/extension/BookmarkQueriesExtensions.kt @@ -14,7 +14,7 @@ internal fun DatabaseBookmark.toAyahBookmark(): AyahBookmark { return AyahBookmark( sura = requireNotNull(sura).toInt(), ayah = requireNotNull(ayah).toInt(), - localId = local_id.toString(), + id = local_id.toString(), lastUpdated = Instant.fromEpochMilliseconds(modified_at).toPlatform(), addedDate = Instant.fromEpochMilliseconds(created_at).toPlatform() ) diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/repository/BookmarksRepository.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/repository/BookmarksRepository.kt index c7a9d702..f2e89bc0 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/repository/BookmarksRepository.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/repository/BookmarksRepository.kt @@ -43,13 +43,13 @@ interface BookmarksRepository { * represents default membership. */ @NativeCoroutines - suspend fun addBookmark(sura: Int, ayah: Int, collectionLocalIds: List): AyahBookmark + suspend fun addBookmark(sura: Int, ayah: Int, collectionIds: List): AyahBookmark @NativeCoroutines suspend fun addBookmark( sura: Int, ayah: Int, - collectionLocalIds: List, + collectionIds: List, timestamp: PlatformDateTime ): AyahBookmark @@ -63,7 +63,7 @@ interface BookmarksRepository { * or already has exactly the requested memberships. */ @NativeCoroutines - suspend fun replaceBookmarkCollections(localId: String, collectionLocalIds: List): Boolean + suspend fun replaceBookmarkCollections(id: String, collectionIds: List): Boolean /** * Replaces the saved collection memberships for an existing ayah bookmark with an explicit @@ -77,8 +77,8 @@ interface BookmarksRepository { */ @NativeCoroutines suspend fun replaceBookmarkCollections( - localId: String, - collectionLocalIds: List, + id: String, + collectionIds: List, timestamp: PlatformDateTime ): Boolean @@ -91,7 +91,7 @@ interface BookmarksRepository { suspend fun replaceAyahBookmarkCollections( sura: Int, ayah: Int, - collectionLocalIds: List + collectionIds: List ): BookmarkCollectionsReplacementResult /** @@ -104,7 +104,7 @@ interface BookmarksRepository { suspend fun replaceAyahBookmarkCollections( sura: Int, ayah: Int, - collectionLocalIds: List, + collectionIds: List, timestamp: PlatformDateTime ): BookmarkCollectionsReplacementResult @@ -125,10 +125,10 @@ interface BookmarksRepository { suspend fun deleteBookmark(bookmark: AyahBookmark): Boolean /** - * Delete a bookmark by local ID, including any collection links. + * Delete a bookmark by its mobile-sync ID, including any collection links. * * @return a boolean denoting success */ @NativeCoroutines - suspend fun deleteBookmark(localId: String): Boolean + suspend fun deleteBookmark(id: String): Boolean } diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/repository/BookmarksRepositoryImpl.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/repository/BookmarksRepositoryImpl.kt index 0d6f782c..624435b8 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/repository/BookmarksRepositoryImpl.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/bookmark/repository/BookmarksRepositoryImpl.kt @@ -63,14 +63,14 @@ class BookmarksRepositoryImpl( } override suspend fun addBookmark(sura: Int, ayah: Int): AyahBookmark { - return addBookmark(sura = sura, ayah = ayah, collectionLocalIds = emptyList()) + return addBookmark(sura = sura, ayah = ayah, collectionIds = emptyList()) } override suspend fun addBookmark(sura: Int, ayah: Int, timestamp: PlatformDateTime): AyahBookmark { return addBookmark( sura = sura, ayah = ayah, - collectionLocalIds = emptyList(), + collectionIds = emptyList(), timestamp = timestamp ) } @@ -78,12 +78,12 @@ class BookmarksRepositoryImpl( override suspend fun addBookmark( sura: Int, ayah: Int, - collectionLocalIds: List + collectionIds: List ): AyahBookmark { return addBookmarkWithTimestampMillis( sura = sura, ayah = ayah, - collectionLocalIds = collectionLocalIds, + collectionLocalIds = collectionIds, timestampMillis = null ) } @@ -91,13 +91,13 @@ class BookmarksRepositoryImpl( override suspend fun addBookmark( sura: Int, ayah: Int, - collectionLocalIds: List, + collectionIds: List, timestamp: PlatformDateTime ): AyahBookmark { return addBookmarkWithTimestampMillis( sura = sura, ayah = ayah, - collectionLocalIds = collectionLocalIds, + collectionLocalIds = collectionIds, timestampMillis = timestamp.toEpochMillisecondsOrNull() ) } @@ -162,24 +162,24 @@ class BookmarksRepositoryImpl( } override suspend fun replaceBookmarkCollections( - localId: String, - collectionLocalIds: List + id: String, + collectionIds: List ): Boolean { return replaceBookmarkCollectionsWithTimestampMillis( - localId = localId, - collectionLocalIds = collectionLocalIds, + localId = id, + collectionLocalIds = collectionIds, timestampMillis = null ) } override suspend fun replaceBookmarkCollections( - localId: String, - collectionLocalIds: List, + id: String, + collectionIds: List, timestamp: PlatformDateTime ): Boolean { return replaceBookmarkCollectionsWithTimestampMillis( - localId = localId, - collectionLocalIds = collectionLocalIds, + localId = id, + collectionLocalIds = collectionIds, timestampMillis = timestamp.toEpochMillisecondsOrNull() ) } @@ -210,12 +210,12 @@ class BookmarksRepositoryImpl( override suspend fun replaceAyahBookmarkCollections( sura: Int, ayah: Int, - collectionLocalIds: List + collectionIds: List ): BookmarkCollectionsReplacementResult { return replaceAyahBookmarkCollectionsWithTimestampMillis( sura = sura, ayah = ayah, - collectionLocalIds = collectionLocalIds, + collectionLocalIds = collectionIds, timestampMillis = null ) } @@ -223,13 +223,13 @@ class BookmarksRepositoryImpl( override suspend fun replaceAyahBookmarkCollections( sura: Int, ayah: Int, - collectionLocalIds: List, + collectionIds: List, timestamp: PlatformDateTime ): BookmarkCollectionsReplacementResult { return replaceAyahBookmarkCollectionsWithTimestampMillis( sura = sura, ayah = ayah, - collectionLocalIds = collectionLocalIds, + collectionLocalIds = collectionIds, timestampMillis = timestamp.toEpochMillisecondsOrNull() ) } @@ -296,12 +296,12 @@ class BookmarksRepositoryImpl( override suspend fun deleteBookmark(bookmark: AyahBookmark): Boolean { logger.i { "Deleting ayah bookmark for ${bookmark.sura}:${bookmark.ayah}" } - return deleteBookmarkWithLocalId(bookmark.localId) + return deleteBookmarkWithLocalId(bookmark.id) } - override suspend fun deleteBookmark(localId: String): Boolean { - logger.i { "Deleting ayah bookmark localId=$localId" } - return deleteBookmarkWithLocalId(localId) + override suspend fun deleteBookmark(id: String): Boolean { + logger.i { "Deleting ayah bookmark id=$id" } + return deleteBookmarkWithLocalId(id) } private suspend fun deleteBookmarkWithLocalId(localId: String): Boolean { diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/extension/CollectionQueriesExtensions.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/extension/CollectionQueriesExtensions.kt index 5c1c6a97..d0a0b120 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/extension/CollectionQueriesExtensions.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/extension/CollectionQueriesExtensions.kt @@ -18,7 +18,7 @@ internal fun DatabaseCollection.toCollection(): PersistenceCollection { return PersistenceCollection( name = name, lastUpdated = Instant.fromEpochMilliseconds(modified_at).toPlatform(), - localId = local_id.toString() + id = local_id.toString() ) } diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/repository/CollectionsRepository.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/repository/CollectionsRepository.kt index 4436aba7..f5c98c11 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/repository/CollectionsRepository.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/repository/CollectionsRepository.kt @@ -20,23 +20,23 @@ interface CollectionsRepository { suspend fun addCollection(name: String, timestamp: PlatformDateTime): Collection /** - * Update the name of a collection identified by its local ID. + * Update the name of a collection identified by its mobile-sync ID. * - * @throws IllegalArgumentException when [localId] does not identify an active collection. + * @throws IllegalArgumentException when [id] does not identify an active collection. */ - suspend fun updateCollection(localId: String, name: String): Collection + suspend fun updateCollection(id: String, name: String): Collection /** - * Update the name of a collection identified by its local ID using an explicit mutation timestamp. + * Update the name of a collection identified by its mobile-sync ID using an explicit mutation timestamp. * - * @throws IllegalArgumentException when [localId] does not identify an active collection. + * @throws IllegalArgumentException when [id] does not identify an active collection. */ - suspend fun updateCollection(localId: String, name: String, timestamp: PlatformDateTime): Collection + suspend fun updateCollection(id: String, name: String, timestamp: PlatformDateTime): Collection /** - * Delete a collection identified by its local ID. + * Delete a collection identified by its mobile-sync ID. */ - suspend fun deleteCollection(localId: String): Boolean + suspend fun deleteCollection(id: String): Boolean /** * Observe the collections list as a Flow. diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/repository/CollectionsRepositoryImpl.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/repository/CollectionsRepositoryImpl.kt index 2b810c8c..56ddbf72 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/repository/CollectionsRepositoryImpl.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collection/repository/CollectionsRepositoryImpl.kt @@ -80,41 +80,41 @@ class CollectionsRepositoryImpl( } } - override suspend fun updateCollection(localId: String, name: String): Collection { - return updateCollectionWithTimestampMillis(localId, name, timestampMillis = null) + override suspend fun updateCollection(id: String, name: String): Collection { + return updateCollectionWithTimestampMillis(id, name, timestampMillis = null) } - override suspend fun updateCollection(localId: String, name: String, timestamp: PlatformDateTime): Collection { - return updateCollectionWithTimestampMillis(localId, name, timestamp.toEpochMillisecondsOrNull()) + override suspend fun updateCollection(id: String, name: String, timestamp: PlatformDateTime): Collection { + return updateCollectionWithTimestampMillis(id, name, timestamp.toEpochMillisecondsOrNull()) } private suspend fun updateCollectionWithTimestampMillis( - localId: String, + id: String, name: String, timestampMillis: Long? ): Collection { - logger.i { "Updating collection localId=$localId with name=$name" } + logger.i { "Updating collection id=$id with name=$name" } return withContext(Dispatchers.IO) { - val id = localId.toLong() + val localId = id.toLong() var updatedCollection: Collection? = null database.transaction { - val existing = collectionQueries.value.getCollectionByLocalId(id) + val existing = collectionQueries.value.getCollectionByLocalId(localId) .executeAsOneOrNull() require(existing?.deleted == 0L) { - "Expected active collection localId=$localId before update." + "Expected active collection id=$id before update." } collectionQueries.value.updateCollectionName( name = name, - id = id, + id = localId, timestamp = timestampMillis ) val record = requireNotNull( - collectionQueries.value.getCollectionByLocalId(id) + collectionQueries.value.getCollectionByLocalId(localId) .executeAsOneOrNull() - ) { "Expected collection localId=$localId after update." } - require(record.deleted == 0L) { "Expected active collection localId=$localId after update." } + ) { "Expected collection id=$id after update." } + require(record.deleted == 0L) { "Expected active collection id=$id after update." } updatedCollection = record.toCollection() } @@ -122,18 +122,18 @@ class CollectionsRepositoryImpl( } } - override suspend fun deleteCollection(localId: String): Boolean { - logger.i { "Deleting collection localId=$localId" } + override suspend fun deleteCollection(id: String): Boolean { + logger.i { "Deleting collection id=$id" } return withContext(Dispatchers.IO) { var deleted = false database.transaction { - val id = localId.toLong() - val collection = collectionQueries.value.getCollectionByLocalId(id).executeAsOneOrNull() + val localId = id.toLong() + val collection = collectionQueries.value.getCollectionByLocalId(localId).executeAsOneOrNull() if (collection?.deleted != 0L) { return@transaction } collectionQueries.value.deleteCollection( - id = id + id = localId ) reconciler.reconcile() deleted = true diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksRepository.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksRepository.kt index c400419f..1aafc12b 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksRepository.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksRepository.kt @@ -9,15 +9,15 @@ interface CollectionBookmarksRepository { /** * Returns all bookmarks linked to a collection. */ - suspend fun getBookmarksForCollection(collectionLocalId: String): List + suspend fun getBookmarksForCollection(collectionId: String): List /** * Adds a bookmark to a collection locally. */ - suspend fun addBookmarkToCollection(collectionLocalId: String, bookmark: AyahBookmark): CollectionAyahBookmark + suspend fun addBookmarkToCollection(collectionId: String, bookmark: AyahBookmark): CollectionAyahBookmark suspend fun addBookmarkToCollection( - collectionLocalId: String, + collectionId: String, bookmark: AyahBookmark, timestamp: PlatformDateTime ): CollectionAyahBookmark @@ -27,13 +27,13 @@ interface CollectionBookmarksRepository { * This operation must not leave partial state if linking fails. */ suspend fun addAyahBookmarkToCollection( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int ): CollectionAyahBookmark suspend fun addAyahBookmarkToCollection( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int, timestamp: PlatformDateTime @@ -42,12 +42,12 @@ interface CollectionBookmarksRepository { /** * Removes a bookmark from a collection locally. */ - suspend fun removeBookmarkFromCollection(collectionLocalId: String, bookmark: AyahBookmark): Boolean + suspend fun removeBookmarkFromCollection(collectionId: String, bookmark: AyahBookmark): Boolean suspend fun removeAyahBookmarkFromCollection(collectionAyahBookmark: CollectionAyahBookmark): Boolean /** * Observe the bookmarks for a collection as a Flow. */ - fun getBookmarksForCollectionFlow(collectionLocalId: String): Flow> + fun getBookmarksForCollectionFlow(collectionId: String): Flow> } diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksRepositoryImpl.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksRepositoryImpl.kt index af8ffe55..056d1fdc 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksRepositoryImpl.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksRepositoryImpl.kt @@ -49,8 +49,8 @@ class CollectionBookmarksRepositoryImpl( private val bookmarkQueries = lazy { database.bookmarksQueries } private val collectionQueries = lazy { database.collectionsQueries } - override suspend fun getBookmarksForCollection(collectionLocalId: String): List { - if (collectionLocalId == DEFAULT_COLLECTION_ID) { + override suspend fun getBookmarksForCollection(collectionId: String): List { + if (collectionId == DEFAULT_COLLECTION_ID) { return withContext(Dispatchers.IO) { bookmarkQueries.value.getDefaultCollectionAyahBookmarks() .executeAsList() @@ -59,7 +59,7 @@ class CollectionBookmarksRepositoryImpl( } return withContext(Dispatchers.IO) { bookmarkCollectionQueries.value - .getCollectionBookmarksForCollectionWithDetails(collection_local_id = collectionLocalId.toLong()) + .getCollectionBookmarksForCollectionWithDetails(collection_local_id = collectionId.toLong()) .executeAsList() .mapNotNull { record -> toCollectionBookmark( @@ -69,7 +69,9 @@ class CollectionBookmarksRepositoryImpl( ayah = record.ayah, collectionLocalId = record.collection_local_id, collectionRemoteId = record.collection_remote_id, - modifiedAt = record.modified_at, + membershipModifiedAt = record.modified_at, + bookmarkModifiedAt = record.bookmark_last_updated_at, + bookmarkCreatedAt = record.bookmark_added_at, localId = record.local_id, logMissingBookmark = false ) @@ -77,15 +79,15 @@ class CollectionBookmarksRepositoryImpl( } } - override fun getBookmarksForCollectionFlow(collectionLocalId: String): Flow> { - return if (collectionLocalId == DEFAULT_COLLECTION_ID) { + override fun getBookmarksForCollectionFlow(collectionId: String): Flow> { + return if (collectionId == DEFAULT_COLLECTION_ID) { bookmarkQueries.value.getDefaultCollectionAyahBookmarks() .asFlow() .mapToList(Dispatchers.IO) .map { list -> list.map { it.toDefaultCollectionBookmark() } } } else { bookmarkCollectionQueries.value - .getCollectionBookmarksForCollectionWithDetails(collection_local_id = collectionLocalId.toLong()) + .getCollectionBookmarksForCollectionWithDetails(collection_local_id = collectionId.toLong()) .asFlow() .mapToList(Dispatchers.IO) .map { list -> @@ -97,7 +99,9 @@ class CollectionBookmarksRepositoryImpl( ayah = record.ayah, collectionLocalId = record.collection_local_id, collectionRemoteId = record.collection_remote_id, - modifiedAt = record.modified_at, + membershipModifiedAt = record.modified_at, + bookmarkModifiedAt = record.bookmark_last_updated_at, + bookmarkCreatedAt = record.bookmark_added_at, localId = record.local_id, logMissingBookmark = false ) @@ -107,26 +111,26 @@ class CollectionBookmarksRepositoryImpl( } override suspend fun addBookmarkToCollection( - collectionLocalId: String, + collectionId: String, bookmark: AyahBookmark ): CollectionAyahBookmark { - return addBookmarkToCollectionWithTimestampMillis(collectionLocalId, bookmark, timestampMillis = null) + return addBookmarkToCollectionWithTimestampMillis(collectionId, bookmark, timestampMillis = null) } override suspend fun addBookmarkToCollection( - collectionLocalId: String, + collectionId: String, bookmark: AyahBookmark, timestamp: PlatformDateTime ): CollectionAyahBookmark { return addBookmarkToCollectionWithTimestampMillis( - collectionLocalId, + collectionId, bookmark, timestamp.toEpochMillisecondsOrNull() ) } private suspend fun addBookmarkToCollectionWithTimestampMillis( - collectionLocalId: String, + collectionId: String, bookmark: AyahBookmark, timestampMillis: Long? ): CollectionAyahBookmark { @@ -134,10 +138,10 @@ class CollectionBookmarksRepositoryImpl( var created: CollectionAyahBookmark? = null database.transaction { val row = requireNotNull( - bookmarkQueries.value.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOneOrNull() - ) { "Bookmark not found for localId=${bookmark.localId}." } + bookmarkQueries.value.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOneOrNull() + ) { "Bookmark not found for id=${bookmark.id}." } - if (collectionLocalId == DEFAULT_COLLECTION_ID) { + if (collectionId == DEFAULT_COLLECTION_ID) { bookmarkQueries.value.addAyahToDefaultCollection( ayah_id = getAyahId(bookmark.sura, bookmark.ayah).toLong(), sura = bookmark.sura.toLong(), @@ -152,9 +156,9 @@ class CollectionBookmarksRepositoryImpl( } val collection = collectionQueries.value - .getCollectionByLocalId(collectionLocalId.toLong()) + .getCollectionByLocalId(collectionId.toLong()) .executeAsOneOrNull() - requireNotNull(collection) { "Collection not found for localId=$collectionLocalId." } + requireNotNull(collection) { "Collection not found for id=$collectionId." } bookmarkCollectionQueries.value.addBookmarkToCollection( bookmark_local_id = row.local_id, @@ -173,7 +177,9 @@ class CollectionBookmarksRepositoryImpl( ayah = record.ayah, collectionLocalId = record.collection_local_id, collectionRemoteId = record.collection_remote_id, - modifiedAt = record.modified_at, + membershipModifiedAt = record.modified_at, + bookmarkModifiedAt = record.bookmark_last_updated_at, + bookmarkCreatedAt = record.bookmark_added_at, localId = record.local_id, logMissingBookmark = true ) @@ -183,21 +189,21 @@ class CollectionBookmarksRepositoryImpl( } override suspend fun addAyahBookmarkToCollection( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int ): CollectionAyahBookmark { - return addAyahBookmarkToCollectionWithTimestampMillis(collectionLocalId, sura, ayah, timestampMillis = null) + return addAyahBookmarkToCollectionWithTimestampMillis(collectionId, sura, ayah, timestampMillis = null) } override suspend fun addAyahBookmarkToCollection( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int, timestamp: PlatformDateTime ): CollectionAyahBookmark { return addAyahBookmarkToCollectionWithTimestampMillis( - collectionLocalId, + collectionId, sura, ayah, timestamp.toEpochMillisecondsOrNull() @@ -205,7 +211,7 @@ class CollectionBookmarksRepositoryImpl( } private suspend fun addAyahBookmarkToCollectionWithTimestampMillis( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int, timestampMillis: Long? @@ -214,7 +220,7 @@ class CollectionBookmarksRepositoryImpl( var created: CollectionAyahBookmark? = null database.transaction { val ayahId = getAyahId(sura, ayah).toLong() - if (collectionLocalId == DEFAULT_COLLECTION_ID) { + if (collectionId == DEFAULT_COLLECTION_ID) { bookmarkQueries.value.addAyahToDefaultCollection( ayah_id = ayahId, sura = sura.toLong(), @@ -240,9 +246,9 @@ class CollectionBookmarksRepositoryImpl( bookmarkQueries.value.getBookmarkForAyah(sura.toLong(), ayah.toLong()).executeAsOneOrNull() ) { "Expected ayah bookmark for $sura:$ayah after insert." } val collection = collectionQueries.value - .getCollectionByLocalId(collectionLocalId.toLong()) + .getCollectionByLocalId(collectionId.toLong()) .executeAsOneOrNull() - requireNotNull(collection) { "Collection not found for localId=$collectionLocalId." } + requireNotNull(collection) { "Collection not found for id=$collectionId." } bookmarkCollectionQueries.value.addBookmarkToCollection( bookmark_local_id = bookmark.local_id, @@ -261,7 +267,9 @@ class CollectionBookmarksRepositoryImpl( ayah = record.ayah, collectionLocalId = record.collection_local_id, collectionRemoteId = record.collection_remote_id, - modifiedAt = record.modified_at, + membershipModifiedAt = record.modified_at, + bookmarkModifiedAt = record.bookmark_last_updated_at, + bookmarkCreatedAt = record.bookmark_added_at, localId = record.local_id, logMissingBookmark = true ) @@ -271,20 +279,20 @@ class CollectionBookmarksRepositoryImpl( } override suspend fun removeBookmarkFromCollection( - collectionLocalId: String, + collectionId: String, bookmark: AyahBookmark ): Boolean { return withContext(Dispatchers.IO) { database.transaction { - if (collectionLocalId == DEFAULT_COLLECTION_ID) { + if (collectionId == DEFAULT_COLLECTION_ID) { bookmarkQueries.value.clearDefaultCollection( - local_id = bookmark.localId.toLong(), + local_id = bookmark.id.toLong(), timestamp = null ) } else { bookmarkCollectionQueries.value.markBookmarkCollectionDeleted( - bookmark_local_id = bookmark.localId.toLong(), - collection_local_id = collectionLocalId.toLong(), + bookmark_local_id = bookmark.id.toLong(), + collection_local_id = collectionId.toLong(), timestamp = null ) } @@ -297,15 +305,15 @@ class CollectionBookmarksRepositoryImpl( override suspend fun removeAyahBookmarkFromCollection(collectionAyahBookmark: CollectionAyahBookmark): Boolean { return withContext(Dispatchers.IO) { database.transaction { - if (collectionAyahBookmark.collectionLocalId == DEFAULT_COLLECTION_ID) { + if (collectionAyahBookmark.collectionId == DEFAULT_COLLECTION_ID) { bookmarkQueries.value.clearDefaultCollection( - local_id = collectionAyahBookmark.bookmarkLocalId.toLong(), + local_id = collectionAyahBookmark.bookmarkId.toLong(), timestamp = null ) } else { bookmarkCollectionQueries.value.markBookmarkCollectionDeleted( - bookmark_local_id = collectionAyahBookmark.bookmarkLocalId.toLong(), - collection_local_id = collectionAyahBookmark.collectionLocalId.toLong(), + bookmark_local_id = collectionAyahBookmark.bookmarkId.toLong(), + collection_local_id = collectionAyahBookmark.collectionId.toLong(), timestamp = null ) } @@ -564,7 +572,7 @@ class CollectionBookmarksRepositoryImpl( } } - override suspend fun fetchCollectionBookmarkByRemoteId(remoteId: String): CollectionAyahBookmark? { + override suspend fun fetchCollectionBookmarkByRemoteId(remoteId: String): LocalSyncCollectionAyahBookmark? { return withContext(Dispatchers.IO) { if (remoteId.startsWith("$DEFAULT_COLLECTION_ID-")) { val bookmarkRemoteId = remoteId.removePrefix("$DEFAULT_COLLECTION_ID-") @@ -573,7 +581,14 @@ class CollectionBookmarksRepositoryImpl( row.deleted == 0L && (row.is_in_default_collection == 1L || row.default_pending_op == "DELETED") ) { - return@withContext row.toDefaultCollectionBookmark(bookmarkRemoteId) + return@withContext defaultLocalSyncCollectionBookmark( + bookmarkLocalId = row.local_id, + bookmarkRemoteId = bookmarkRemoteId, + sura = row.sura, + ayah = row.ayah, + modifiedAt = row.default_modified_at ?: row.modified_at, + createdAt = row.created_at + ) } return@withContext null } @@ -587,7 +602,7 @@ class CollectionBookmarksRepositoryImpl( record.last_synced_collection_remote_id, record.last_synced_bookmark_remote_id ) == remoteId - toCollectionBookmark( + toLocalSyncCollectionBookmark( bookmarkLocalId = record.bookmark_local_id, bookmarkRemoteId = if (matchedSnapshot) { record.last_synced_bookmark_remote_id @@ -603,6 +618,7 @@ class CollectionBookmarksRepositoryImpl( record.collection_remote_id ?: record.last_synced_collection_remote_id }, modifiedAt = record.modified_at, + createdAt = record.created_at, localId = record.local_id, logMissingBookmark = false ) @@ -1172,7 +1188,9 @@ class CollectionBookmarksRepositoryImpl( ayah: Long?, collectionLocalId: Long, collectionRemoteId: String?, - modifiedAt: Long, + membershipModifiedAt: Long, + bookmarkModifiedAt: Long, + bookmarkCreatedAt: Long, localId: Long, logMissingBookmark: Boolean ): CollectionAyahBookmark? { @@ -1183,7 +1201,9 @@ class CollectionBookmarksRepositoryImpl( ayah = ayah, collectionLocalId = collectionLocalId, collectionRemoteId = collectionRemoteId, - modifiedAt = modifiedAt, + membershipModifiedAt = membershipModifiedAt, + bookmarkModifiedAt = bookmarkModifiedAt, + bookmarkCreatedAt = bookmarkCreatedAt, localId = localId, logMissingBookmark = logMissingBookmark )?.toCollectionBookmark() @@ -1208,7 +1228,9 @@ class CollectionBookmarksRepositoryImpl( ayah = ayah, collectionLocalId = collectionLocalId, collectionRemoteId = collectionRemoteId, - modifiedAt = modifiedAt, + membershipModifiedAt = modifiedAt, + bookmarkModifiedAt = modifiedAt, + bookmarkCreatedAt = modifiedAt, localId = localId, logMissingBookmark = logMissingBookmark )?.toLocalSyncCollectionBookmark( @@ -1223,11 +1245,15 @@ class CollectionBookmarksRepositoryImpl( ayah: Long?, collectionLocalId: Long, collectionRemoteId: String?, - modifiedAt: Long, + membershipModifiedAt: Long, + bookmarkModifiedAt: Long, + bookmarkCreatedAt: Long, localId: Long, logMissingBookmark: Boolean ): CollectionBookmarkFields? { - val updatedAt = Instant.fromEpochMilliseconds(modifiedAt).toPlatform() + val membershipUpdatedAt = Instant.fromEpochMilliseconds(membershipModifiedAt).toPlatform() + val bookmarkUpdatedAt = Instant.fromEpochMilliseconds(bookmarkModifiedAt).toPlatform() + val bookmarkAddedAt = Instant.fromEpochMilliseconds(bookmarkCreatedAt).toPlatform() val suraValue = sura?.toInt() val ayahValue = ayah?.toInt() if (suraValue == null || ayahValue == null) { @@ -1243,7 +1269,9 @@ class CollectionBookmarksRepositoryImpl( bookmarkRemoteId = bookmarkRemoteId, sura = suraValue, ayah = ayahValue, - lastUpdated = updatedAt, + membershipLastUpdated = membershipUpdatedAt, + bookmarkLastUpdated = bookmarkUpdatedAt, + bookmarkAddedDate = bookmarkAddedAt, localId = localId.toString() ) } @@ -1256,7 +1284,9 @@ class CollectionBookmarksRepositoryImpl( bookmarkRemoteId = relationBookmarkRemoteId, sura = sura, ayah = ayah, - modifiedAt = default_modified_at ?: modified_at + membershipModifiedAt = default_modified_at ?: modified_at, + bookmarkModifiedAt = modified_at, + bookmarkCreatedAt = created_at ) } @@ -1273,7 +1303,9 @@ class CollectionBookmarksRepositoryImpl( bookmarkRemoteId = bookmarkRemoteId, sura = sura, ayah = ayah, - modifiedAt = modifiedAt + membershipModifiedAt = modifiedAt, + bookmarkModifiedAt = modifiedAt, + bookmarkCreatedAt = createdAt ).toLocalSyncCollectionBookmark( createdAt = Instant.fromEpochMilliseconds(createdAt).toPlatform() ) @@ -1284,14 +1316,18 @@ class CollectionBookmarksRepositoryImpl( bookmarkRemoteId: String?, sura: Long?, ayah: Long?, - modifiedAt: Long + membershipModifiedAt: Long, + bookmarkModifiedAt: Long, + bookmarkCreatedAt: Long ): CollectionAyahBookmark { return defaultCollectionBookmarkFields( bookmarkLocalId = bookmarkLocalId, bookmarkRemoteId = bookmarkRemoteId, sura = sura, ayah = ayah, - modifiedAt = modifiedAt + membershipModifiedAt = membershipModifiedAt, + bookmarkModifiedAt = bookmarkModifiedAt, + bookmarkCreatedAt = bookmarkCreatedAt ).toCollectionBookmark() } @@ -1300,9 +1336,13 @@ class CollectionBookmarksRepositoryImpl( bookmarkRemoteId: String?, sura: Long?, ayah: Long?, - modifiedAt: Long + membershipModifiedAt: Long, + bookmarkModifiedAt: Long, + bookmarkCreatedAt: Long ): CollectionBookmarkFields { - val updatedAt = Instant.fromEpochMilliseconds(modifiedAt).toPlatform() + val membershipUpdatedAt = Instant.fromEpochMilliseconds(membershipModifiedAt).toPlatform() + val bookmarkUpdatedAt = Instant.fromEpochMilliseconds(bookmarkModifiedAt).toPlatform() + val bookmarkAddedAt = Instant.fromEpochMilliseconds(bookmarkCreatedAt).toPlatform() return CollectionBookmarkFields( collectionLocalId = DEFAULT_COLLECTION_ID, collectionRemoteId = DEFAULT_COLLECTION_ID, @@ -1310,21 +1350,21 @@ class CollectionBookmarksRepositoryImpl( bookmarkRemoteId = bookmarkRemoteId, sura = requireNotNull(sura).toInt(), ayah = requireNotNull(ayah).toInt(), - lastUpdated = updatedAt, + membershipLastUpdated = membershipUpdatedAt, + bookmarkLastUpdated = bookmarkUpdatedAt, + bookmarkAddedDate = bookmarkAddedAt, localId = defaultLocalId(bookmarkLocalId) ) } private fun CollectionBookmarkFields.toCollectionBookmark(): CollectionAyahBookmark { return CollectionAyahBookmark( - collectionLocalId = collectionLocalId, - collectionRemoteId = collectionRemoteId, - bookmarkLocalId = bookmarkLocalId, - bookmarkRemoteId = bookmarkRemoteId, + collectionId = collectionLocalId, + bookmarkId = bookmarkLocalId, sura = sura, ayah = ayah, - lastUpdated = lastUpdated, - localId = localId + bookmarkLastUpdated = bookmarkLastUpdated, + bookmarkAddedDate = bookmarkAddedDate ) } @@ -1338,7 +1378,7 @@ class CollectionBookmarksRepositoryImpl( bookmarkRemoteId = bookmarkRemoteId, sura = sura, ayah = ayah, - lastUpdated = lastUpdated, + lastUpdated = membershipLastUpdated, localId = localId, createdAt = createdAt ) @@ -1356,7 +1396,9 @@ private data class CollectionBookmarkFields( val bookmarkRemoteId: String?, val sura: Int, val ayah: Int, - val lastUpdated: PlatformDateTime, + val membershipLastUpdated: PlatformDateTime, + val bookmarkLastUpdated: PlatformDateTime, + val bookmarkAddedDate: PlatformDateTime, val localId: String ) diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksSynchronizationRepository.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksSynchronizationRepository.kt index 8853c2fd..614ee61c 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksSynchronizationRepository.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/collectionbookmark/repository/CollectionBookmarksSynchronizationRepository.kt @@ -5,7 +5,6 @@ import com.quran.shared.mutations.LocalMutationAck import com.quran.shared.mutations.RemoteModelMutation import com.quran.shared.persistence.input.LocalSyncCollectionAyahBookmark import com.quran.shared.persistence.input.RemoteCollectionBookmark -import com.quran.shared.persistence.model.CollectionAyahBookmark import com.quran.shared.persistence.repository.PersistenceWriteBoundaryGuard interface CollectionBookmarksSynchronizationRepository { @@ -35,5 +34,5 @@ interface CollectionBookmarksSynchronizationRepository { suspend fun remoteResourcesExist(remoteIDs: List): Map - suspend fun fetchCollectionBookmarkByRemoteId(remoteId: String): CollectionAyahBookmark? + suspend fun fetchCollectionBookmarkByRemoteId(remoteId: String): LocalSyncCollectionAyahBookmark? } diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/extension/NoteQueriesExtensions.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/extension/NoteQueriesExtensions.kt index 21b6ee84..6b0fe278 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/extension/NoteQueriesExtensions.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/extension/NoteQueriesExtensions.kt @@ -30,7 +30,7 @@ internal fun DatabaseNote.toNote(): Note { endSura = end.first, endAyah = end.second, lastUpdated = Instant.fromEpochMilliseconds(normalizedModifiedAt).toPlatform(), - localId = local_id.toString() + id = local_id.toString() ) } diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/repository/NotesRepository.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/repository/NotesRepository.kt index e1879f91..7ad75f24 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/repository/NotesRepository.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/repository/NotesRepository.kt @@ -25,12 +25,12 @@ interface NotesRepository { ): Note /** - * Update a note by its local ID. + * Update a note by its mobile-sync ID. */ - suspend fun updateNote(localId: String, body: String, startSura: Int, startAyah: Int, endSura: Int, endAyah: Int): Note + suspend fun updateNote(id: String, body: String, startSura: Int, startAyah: Int, endSura: Int, endAyah: Int): Note suspend fun updateNote( - localId: String, + id: String, body: String, startSura: Int, startAyah: Int, @@ -40,9 +40,9 @@ interface NotesRepository { ): Note /** - * Delete a note by its local ID. + * Delete a note by its mobile-sync ID. */ - suspend fun deleteNote(localId: String): Boolean + suspend fun deleteNote(id: String): Boolean /** * Observe the notes list as a Flow. diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/repository/NotesRepositoryImpl.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/repository/NotesRepositoryImpl.kt index ed577f27..532a0ece 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/repository/NotesRepositoryImpl.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/note/repository/NotesRepositoryImpl.kt @@ -107,7 +107,7 @@ class NotesRepositoryImpl( } override suspend fun updateNote( - localId: String, + id: String, body: String, startSura: Int, startAyah: Int, @@ -115,7 +115,7 @@ class NotesRepositoryImpl( endAyah: Int ): Note { return updateNoteWithTimestampMillis( - localId, + id, body, startSura, startAyah, @@ -126,7 +126,7 @@ class NotesRepositoryImpl( } override suspend fun updateNote( - localId: String, + id: String, body: String, startSura: Int, startAyah: Int, @@ -135,7 +135,7 @@ class NotesRepositoryImpl( timestamp: PlatformDateTime ): Note { return updateNoteWithTimestampMillis( - localId, + id, body, startSura, startAyah, @@ -146,7 +146,7 @@ class NotesRepositoryImpl( } private suspend fun updateNoteWithTimestampMillis( - localId: String, + id: String, body: String, startSura: Int, startAyah: Int, @@ -154,29 +154,30 @@ class NotesRepositoryImpl( endAyah: Int, timestampMillis: Long? ): Note { - logger.i { "Updating note localId=$localId" } + logger.i { "Updating note id=$id" } return withContext(Dispatchers.IO) { + val localId = id.toLong() val startAyahId = requireAyahId(startSura, startAyah) val endAyahId = requireAyahId(endSura, endAyah) notesQueries.value.updateNote( note = body, start_ayah_id = startAyahId.toLong(), end_ayah_id = endAyahId.toLong(), - id = localId.toLong(), + id = localId, timestamp = timestampMillis ) - val record = notesQueries.value.getNoteByLocalId(localId.toLong()) + val record = notesQueries.value.getNoteByLocalId(localId) .executeAsOneOrNull() - requireNotNull(record) { "Expected note localId=$localId after update." } + requireNotNull(record) { "Expected note id=$id after update." } record.toNote() } } - override suspend fun deleteNote(localId: String): Boolean { - logger.i { "Deleting note localId=$localId" } + override suspend fun deleteNote(id: String): Boolean { + logger.i { "Deleting note id=$id" } withContext(Dispatchers.IO) { notesQueries.value.deleteNote( - id = localId.toLong() + id = id.toLong() ) } return true diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingbookmark/extension/ReadingBookmarkQueriesExtensions.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingbookmark/extension/ReadingBookmarkQueriesExtensions.kt index 96797dae..c68d769c 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingbookmark/extension/ReadingBookmarkQueriesExtensions.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingbookmark/extension/ReadingBookmarkQueriesExtensions.kt @@ -14,18 +14,18 @@ import kotlin.time.Instant internal fun DatabaseBookmark.toReadingBookmark(): ReadingBookmark { val lastUpdated = Instant.fromEpochMilliseconds(reading_modified_at ?: modified_at).toPlatform() - val localId = local_id.toString() + val id = local_id.toString() return when (bookmark_type) { "AYAH" -> AyahReadingBookmark( sura = requireNotNull(sura).toInt(), ayah = requireNotNull(ayah).toInt(), lastUpdated = lastUpdated, - localId = localId + id = id ) "PAGE" -> PageReadingBookmark( page = requireNotNull(page).toInt(), lastUpdated = lastUpdated, - localId = localId + id = id ) else -> error("Unsupported reading bookmark type: $bookmark_type") } @@ -33,13 +33,13 @@ internal fun DatabaseBookmark.toReadingBookmark(): ReadingBookmark { internal fun DatabaseBookmark.toAyahReadingBookmark(): AyahReadingBookmark { val lastUpdated = Instant.fromEpochMilliseconds(reading_modified_at ?: modified_at).toPlatform() - val localId = local_id.toString() + val id = local_id.toString() return when (bookmark_type) { "AYAH" -> AyahReadingBookmark( sura = requireNotNull(sura).toInt(), ayah = requireNotNull(ayah).toInt(), lastUpdated = lastUpdated, - localId = localId + id = id ) else -> error("Unsupported reading bookmark type: $bookmark_type") } @@ -47,12 +47,12 @@ internal fun DatabaseBookmark.toAyahReadingBookmark(): AyahReadingBookmark { internal fun DatabaseBookmark.toPageReadingBookmark(): PageReadingBookmark { val lastUpdated = Instant.fromEpochMilliseconds(reading_modified_at ?: modified_at).toPlatform() - val localId = local_id.toString() + val id = local_id.toString() return when (bookmark_type) { "PAGE" -> PageReadingBookmark( page = requireNotNull(page).toInt(), lastUpdated = lastUpdated, - localId = localId + id = id ) else -> error("Unsupported reading bookmark type: $bookmark_type") } diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/extension/ReadingSessionsQueriesExtensions.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/extension/ReadingSessionsQueriesExtensions.kt index 39cff01b..76beb3f8 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/extension/ReadingSessionsQueriesExtensions.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/extension/ReadingSessionsQueriesExtensions.kt @@ -19,7 +19,7 @@ internal fun DatabaseReadingSession.toReadingSession(): ReadingSession { sura = chapter_number.toInt(), ayah = verse_number.toInt(), lastUpdated = Instant.fromEpochMilliseconds(modified_at).toPlatform(), - localId = local_id.toString() + id = local_id.toString() ) } diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/repository/ReadingSessionsRepository.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/repository/ReadingSessionsRepository.kt index 738a1013..a82c246b 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/repository/ReadingSessionsRepository.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/repository/ReadingSessionsRepository.kt @@ -29,19 +29,19 @@ interface ReadingSessionsRepository { suspend fun addReadingSession(sura: Int, ayah: Int, timestamp: PlatformDateTime): ReadingSession /** - * Update an existing active reading session by local ID. + * Update an existing active reading session by its mobile-sync ID. * - * @param localId the local ID of the session to update + * @param id the mobile-sync ID of the session to update * @param sura the new sura number of the session * @param ayah the new ayah number of the session * @return the updated [ReadingSession] */ @NativeCoroutines - suspend fun updateReadingSession(localId: String, sura: Int, ayah: Int): ReadingSession + suspend fun updateReadingSession(id: String, sura: Int, ayah: Int): ReadingSession @NativeCoroutines suspend fun updateReadingSession( - localId: String, + id: String, sura: Int, ayah: Int, timestamp: PlatformDateTime diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/repository/ReadingSessionsRepositoryImpl.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/repository/ReadingSessionsRepositoryImpl.kt index 5d5855a1..1d551c90 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/repository/ReadingSessionsRepositoryImpl.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/readingsession/repository/ReadingSessionsRepositoryImpl.kt @@ -106,58 +106,58 @@ class ReadingSessionsRepositoryImpl( } } - override suspend fun updateReadingSession(localId: String, sura: Int, ayah: Int): ReadingSession { - return updateReadingSessionWithTimestampMillis(localId, sura, ayah, timestampMillis = null) + override suspend fun updateReadingSession(id: String, sura: Int, ayah: Int): ReadingSession { + return updateReadingSessionWithTimestampMillis(id, sura, ayah, timestampMillis = null) } override suspend fun updateReadingSession( - localId: String, + id: String, sura: Int, ayah: Int, timestamp: PlatformDateTime ): ReadingSession { - return updateReadingSessionWithTimestampMillis(localId, sura, ayah, timestamp.toEpochMillisecondsOrNull()) + return updateReadingSessionWithTimestampMillis(id, sura, ayah, timestamp.toEpochMillisecondsOrNull()) } private suspend fun updateReadingSessionWithTimestampMillis( - localId: String, + id: String, sura: Int, ayah: Int, timestampMillis: Long? ): ReadingSession { - logger.i { "Updating reading session localId=$localId to $sura:$ayah" } + logger.i { "Updating reading session id=$id to $sura:$ayah" } return withContext(Dispatchers.IO) { - val id = localId.toLong() + val localId = id.toLong() val updatedAt = timestampMillis ?: currentTimeMillis() var updatedSession: ReadingSession? = null database.transaction { - val existing = readingSessionsQueries.value.getReadingSessionByLocalId(id) + val existing = readingSessionsQueries.value.getReadingSessionByLocalId(localId) .executeAsOneOrNull() require(existing?.deleted == 0L) { - "Expected active reading session localId=$localId before update." + "Expected active reading session id=$id before update." } val conflicting = readingSessionsQueries.value.getReadingSessionForChapterVerse( sura.toLong(), ayah.toLong() ).executeAsOneOrNull() - require(conflicting == null || conflicting.local_id == id) { + require(conflicting == null || conflicting.local_id == localId) { "Reading session already exists for $sura:$ayah." } readingSessionsQueries.value.updateReadingSession( - local_id = id, + local_id = localId, chapter_number = sura.toLong(), verse_number = ayah.toLong(), modified_at = updatedAt ) pruneOldReadingSessions() - val record = readingSessionsQueries.value.getReadingSessionByLocalId(id) + val record = readingSessionsQueries.value.getReadingSessionByLocalId(localId) .executeAsOneOrNull() - requireNotNull(record) { "Expected reading session localId=$localId after update." } - require(record.deleted == 0L) { "Expected active reading session localId=$localId after update." } + requireNotNull(record) { "Expected reading session id=$id after update." } + require(record.deleted == 0L) { "Expected active reading session id=$id after update." } updatedSession = record.toReadingSession() } diff --git a/persistence/src/commonMain/sqldelight/com/quran/shared/persistence/bookmark_collections.sq b/persistence/src/commonMain/sqldelight/com/quran/shared/persistence/bookmark_collections.sq index fee54023..55fd181e 100644 --- a/persistence/src/commonMain/sqldelight/com/quran/shared/persistence/bookmark_collections.sq +++ b/persistence/src/commonMain/sqldelight/com/quran/shared/persistence/bookmark_collections.sq @@ -89,7 +89,9 @@ getCollectionBookmarksWithDetails: b.bookmark_type AS bookmark_type, b.sura AS sura, b.ayah AS ayah, - b.page AS page + b.page AS page, + b.modified_at AS bookmark_last_updated_at, + b.created_at AS bookmark_added_at FROM bookmark_collection bc JOIN collection c ON c.local_id = bc.collection_local_id JOIN bookmark b ON b.local_id = bc.bookmark_local_id @@ -116,7 +118,9 @@ getCollectionBookmarksForCollectionWithDetails: b.bookmark_type AS bookmark_type, b.sura AS sura, b.ayah AS ayah, - b.page AS page + b.page AS page, + b.modified_at AS bookmark_last_updated_at, + b.created_at AS bookmark_added_at FROM bookmark_collection bc JOIN collection c ON c.local_id = bc.collection_local_id JOIN bookmark b ON b.local_id = bc.bookmark_local_id diff --git a/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/BookmarkSyncArchitectureTest.kt b/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/BookmarkSyncArchitectureTest.kt index fe935090..1752a806 100644 --- a/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/BookmarkSyncArchitectureTest.kt +++ b/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/BookmarkSyncArchitectureTest.kt @@ -56,7 +56,7 @@ class BookmarkSyncArchitectureTest { val bookmark = bookmarksRepository.addBookmark(2, 255) val row = database.bookmarksQueries.getBookmarkForAyah(2L, 255L).executeAsOne() - assertEquals(bookmark.localId.toLong(), row.local_id) + assertEquals(bookmark.id.toLong(), row.local_id) assertEquals(1L, row.is_in_default_collection) assertEquals("CREATED", row.default_pending_op) assertEquals(0L, database.bookmark_collectionsQueries.countAll().executeAsOne()) @@ -238,12 +238,12 @@ class BookmarkSyncArchitectureTest { @Test fun `deleteBookmark by local id returns false for reading-only bookmark without saved mutation`() = runTest { val readingBookmark = readingRepository.addAyahReadingBookmark(2, 255, at(100)) - val before = database.bookmarksQueries.getBookmarkByLocalId(readingBookmark.localId.toLong()).executeAsOne() + val before = database.bookmarksQueries.getBookmarkByLocalId(readingBookmark.id.toLong()).executeAsOne() val mutationsBefore = bookmarksRepository.fetchMutatedBookmarks() - val deleted = bookmarksRepository.deleteBookmark(readingBookmark.localId) + val deleted = bookmarksRepository.deleteBookmark(readingBookmark.id) - val after = database.bookmarksQueries.getBookmarkByLocalId(readingBookmark.localId.toLong()).executeAsOne() + val after = database.bookmarksQueries.getBookmarkByLocalId(readingBookmark.id.toLong()).executeAsOne() val mutationsAfter = bookmarksRepository.fetchMutatedBookmarks() assertFalse(deleted) assertEquals(before, after) @@ -332,8 +332,26 @@ class BookmarkSyncArchitectureTest { val defaults = collectionBookmarksRepository.getBookmarksForCollection(DEFAULT_COLLECTION_ID) assertEquals(listOf(12, 11), defaults.map { it.ayah }) - assertEquals(listOf(300L, 100L), defaults.map { it.lastUpdated.fromPlatform().toEpochMilliseconds() }) - assertTrue(defaults.all { it.collectionLocalId == DEFAULT_COLLECTION_ID }) + assertEquals(listOf(300L, 100L), defaults.map { it.bookmarkLastUpdated.fromPlatform().toEpochMilliseconds() }) + assertEquals(listOf(300L, 100L), defaults.map { it.bookmarkAddedDate.fromPlatform().toEpochMilliseconds() }) + assertTrue(defaults.all { it.collectionId == DEFAULT_COLLECTION_ID }) + } + + @Test + fun `custom collection bookmark exposes bookmark timestamps instead of membership timestamp`() = runTest { + val collectionId = createCollection("TimestampedLink", "remote-timestamped-link") + val bookmark = bookmarksRepository.addBookmark(2, 21, listOf(DEFAULT_COLLECTION_ID), at(100)) + + val linked = collectionBookmarksRepository.addBookmarkToCollection(collectionId, bookmark, at(300)) + val queried = collectionBookmarksRepository.getBookmarksForCollection(collectionId).single() + val linkRow = database.bookmark_collectionsQueries + .getCollectionBookmarkFor(bookmark.id.toLong(), collectionId.toLong()) + .executeAsOne() + + assertEquals(300L, linkRow.modified_at) + assertEquals(100L, linked.bookmarkLastUpdated.fromPlatform().toEpochMilliseconds()) + assertEquals(100L, linked.bookmarkAddedDate.fromPlatform().toEpochMilliseconds()) + assertEquals(linked, queried) } @Test @@ -386,21 +404,21 @@ class BookmarkSyncArchitectureTest { val bookmark = bookmarksRepository.addBookmark( sura = 2, ayah = 6, - collectionLocalIds = listOf(DEFAULT_COLLECTION_ID, keptCollectionId, removedCollectionId) + collectionIds = listOf(DEFAULT_COLLECTION_ID, keptCollectionId, removedCollectionId) ) val changed = bookmarksRepository.replaceBookmarkCollections( - localId = bookmark.localId, - collectionLocalIds = listOf(keptCollectionId, addedCollectionId) + id = bookmark.id, + collectionIds = listOf(keptCollectionId, addedCollectionId) ) - val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() val removedLink = database.bookmark_collectionsQueries - .getCollectionBookmarkFor(bookmark.localId.toLong(), removedCollectionId.toLong()) + .getCollectionBookmarkFor(bookmark.id.toLong(), removedCollectionId.toLong()) .executeAsOneOrNull() assertTrue(changed) assertEquals(0L, row.is_in_default_collection) - assertEquals(setOf(keptCollectionId, addedCollectionId), activeCustomCollectionIds(bookmark.localId)) + assertEquals(setOf(keptCollectionId, addedCollectionId), activeCustomCollectionIds(bookmark.id)) assertEquals(0L, removedLink?.is_active ?: 0L) } @@ -409,31 +427,31 @@ class BookmarkSyncArchitectureTest { val collectionId = createCollection("ReplaceEmpty", "remote-replace-empty") val bookmark = bookmarksRepository.addBookmark(2, 7, listOf(collectionId)) - val changed = bookmarksRepository.replaceBookmarkCollections(bookmark.localId, emptyList()) + val changed = bookmarksRepository.replaceBookmarkCollections(bookmark.id, emptyList()) - val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() assertTrue(changed) assertEquals(1L, row.is_in_default_collection) - assertEquals(emptySet(), activeCustomCollectionIds(bookmark.localId)) + assertEquals(emptySet(), activeCustomCollectionIds(bookmark.id)) } @Test fun `replaceBookmarkCollections returns false when memberships are unchanged`() = runTest { val collectionId = createCollection("ReplaceUnchanged", "remote-replace-unchanged") val bookmark = bookmarksRepository.addBookmark(2, 8, listOf(DEFAULT_COLLECTION_ID, collectionId)) - val rowBefore = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val rowBefore = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() val linkBefore = database.bookmark_collectionsQueries - .getCollectionBookmarkFor(bookmark.localId.toLong(), collectionId.toLong()) + .getCollectionBookmarkFor(bookmark.id.toLong(), collectionId.toLong()) .executeAsOne() val changed = bookmarksRepository.replaceBookmarkCollections( - localId = bookmark.localId, - collectionLocalIds = listOf(collectionId, DEFAULT_COLLECTION_ID) + id = bookmark.id, + collectionIds = listOf(collectionId, DEFAULT_COLLECTION_ID) ) - val rowAfter = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val rowAfter = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() val linkAfter = database.bookmark_collectionsQueries - .getCollectionBookmarkFor(bookmark.localId.toLong(), collectionId.toLong()) + .getCollectionBookmarkFor(bookmark.id.toLong(), collectionId.toLong()) .executeAsOne() assertFalse(changed) assertEquals(rowBefore, rowAfter) @@ -447,14 +465,14 @@ class BookmarkSyncArchitectureTest { val timestamp = at(4200) val changed = bookmarksRepository.replaceBookmarkCollections( - localId = bookmark.localId, - collectionLocalIds = listOf(collectionId), + id = bookmark.id, + collectionIds = listOf(collectionId), timestamp = timestamp ) - val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() val link = database.bookmark_collectionsQueries - .getCollectionBookmarkFor(bookmark.localId.toLong(), collectionId.toLong()) + .getCollectionBookmarkFor(bookmark.id.toLong(), collectionId.toLong()) .executeAsOne() assertTrue(changed) assertEquals(4200L, row.modified_at) @@ -471,15 +489,15 @@ class BookmarkSyncArchitectureTest { val result = bookmarksRepository.replaceAyahBookmarkCollections( sura = 2, ayah = 9, - collectionLocalIds = listOf(firstCollectionId, secondCollectionId) + collectionIds = listOf(firstCollectionId, secondCollectionId) ) - val row = database.bookmarksQueries.getBookmarkByLocalId(result.bookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(result.bookmark.id.toLong()).executeAsOne() assertTrue(result.changed) assertEquals(2, result.bookmark.sura) assertEquals(9, result.bookmark.ayah) assertEquals(0L, row.is_in_default_collection) - assertEquals(setOf(firstCollectionId, secondCollectionId), activeCustomCollectionIds(result.bookmark.localId)) + assertEquals(setOf(firstCollectionId, secondCollectionId), activeCustomCollectionIds(result.bookmark.id)) } @Test @@ -490,13 +508,13 @@ class BookmarkSyncArchitectureTest { val result = bookmarksRepository.replaceAyahBookmarkCollections( sura = 2, ayah = 14, - collectionLocalIds = listOf(collectionId), + collectionIds = listOf(collectionId), timestamp = timestamp ) - val row = database.bookmarksQueries.getBookmarkByLocalId(result.bookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(result.bookmark.id.toLong()).executeAsOne() val link = database.bookmark_collectionsQueries - .getCollectionBookmarkFor(result.bookmark.localId.toLong(), collectionId.toLong()) + .getCollectionBookmarkFor(result.bookmark.id.toLong(), collectionId.toLong()) .executeAsOne() assertTrue(result.changed) assertEquals(4300L, row.created_at) @@ -515,14 +533,14 @@ class BookmarkSyncArchitectureTest { val result = bookmarksRepository.replaceAyahBookmarkCollections( sura = 2, ayah = 10, - collectionLocalIds = listOf(addedCollectionId) + collectionIds = listOf(addedCollectionId) ) - val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() assertTrue(result.changed) - assertEquals(bookmark.localId, result.bookmark.localId) + assertEquals(bookmark.id, result.bookmark.id) assertEquals(0L, row.is_in_default_collection) - assertEquals(setOf(addedCollectionId), activeCustomCollectionIds(bookmark.localId)) + assertEquals(setOf(addedCollectionId), activeCustomCollectionIds(bookmark.id)) } @Test @@ -533,12 +551,12 @@ class BookmarkSyncArchitectureTest { val result = bookmarksRepository.replaceAyahBookmarkCollections( sura = 2, ayah = 11, - collectionLocalIds = listOf(collectionId, DEFAULT_COLLECTION_ID) + collectionIds = listOf(collectionId, DEFAULT_COLLECTION_ID) ) assertFalse(result.changed) - assertEquals(bookmark.localId, result.bookmark.localId) - assertEquals(setOf(DEFAULT_COLLECTION_ID, collectionId), activeCollectionIds(bookmark.localId)) + assertEquals(bookmark.id, result.bookmark.id) + assertEquals(setOf(DEFAULT_COLLECTION_ID, collectionId), activeCollectionIds(bookmark.id)) } @Test @@ -549,15 +567,15 @@ class BookmarkSyncArchitectureTest { val result = bookmarksRepository.replaceAyahBookmarkCollections( sura = 2, ayah = 12, - collectionLocalIds = listOf(collectionId) + collectionIds = listOf(collectionId) ) - val row = database.bookmarksQueries.getBookmarkByLocalId(readingBookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(readingBookmark.id.toLong()).executeAsOne() assertTrue(result.changed) - assertEquals(readingBookmark.localId, result.bookmark.localId) + assertEquals(readingBookmark.id, result.bookmark.id) assertEquals(1L, row.is_reading) assertEquals(0L, row.is_in_default_collection) - assertEquals(setOf(collectionId), activeCustomCollectionIds(readingBookmark.localId)) + assertEquals(setOf(collectionId), activeCustomCollectionIds(readingBookmark.id)) } @Test @@ -687,7 +705,7 @@ class BookmarkSyncArchitectureTest { assertEquals(1L, row.is_reading) assertEquals(200L, row.reading_modified_at) assertNull(row.reading_pending_op) - assertEquals(row.local_id.toString(), readingRepository.getReadingBookmark()?.localId) + assertEquals(row.local_id.toString(), readingRepository.getReadingBookmark()?.id) } @Test @@ -775,7 +793,7 @@ class BookmarkSyncArchitectureTest { ) val row = database.bookmarksQueries.getBookmarkForAyah(4L, 1L).executeAsOne() - assertEquals(bookmark.localId.toLong(), row.local_id) + assertEquals(bookmark.id.toLong(), row.local_id) assertEquals("remote-bookmark-4-1", row.remote_id) assertNull(row.default_pending_op) } @@ -797,7 +815,7 @@ class BookmarkSyncArchitectureTest { localMutationsToClear = listOf(unprovenRelationAck) ) - val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() assertNull(row.remote_id) assertNull(row.default_pending_op) assertNotNull( @@ -826,7 +844,7 @@ class BookmarkSyncArchitectureTest { localMutationsToClear = listOf(unprovenRelationAck) ) - val bookmarkRow = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val bookmarkRow = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() val link = database.bookmark_collectionsQueries .getCollectionBookmarkByLocalId(localMutation.localID.toLong()) .executeAsOne() @@ -857,7 +875,7 @@ class BookmarkSyncArchitectureTest { localMutationsToClear = listOf(unprovenRelationAck) ) - val bookmarkRow = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val bookmarkRow = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() val link = database.bookmark_collectionsQueries .getCollectionBookmarkByLocalId(createMutation.localID.toLong()) .executeAsOne() @@ -2456,7 +2474,7 @@ class BookmarkSyncArchitectureTest { localMutationsToClear = listOf(staleReadingClear) ) - val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOne() + val row = database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOne() assertEquals(1L, row.is_reading) assertEquals("CREATED", row.reading_pending_op) assertEquals(Mutation.CREATED, bookmarksRepository.fetchMutatedBookmarks().single().mutation) @@ -2743,7 +2761,7 @@ class BookmarkSyncArchitectureTest { PersistenceImportRepositoryImpl(database).importData(PersistenceImportData(), deleteExisting = true) val tombstoneBeforeAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals(1L, tombstoneBeforeAck.deleted) assertEquals(0L, tombstoneBeforeAck.is_reading) @@ -2764,7 +2782,7 @@ class BookmarkSyncArchitectureTest { ) val tombstoneAfterAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals("remote-import-reading-create", tombstoneAfterAck.remote_id) assertEquals(1L, tombstoneAfterAck.deleted) @@ -2782,7 +2800,7 @@ class BookmarkSyncArchitectureTest { PersistenceImportRepositoryImpl(database).importData(PersistenceImportData(), deleteExisting = true) val tombstoneBeforeAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals(1L, tombstoneBeforeAck.deleted) assertEquals(0L, tombstoneBeforeAck.is_in_default_collection) @@ -2803,7 +2821,7 @@ class BookmarkSyncArchitectureTest { ) val tombstoneAfterAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals("remote-import-default-create", tombstoneAfterAck.remote_id) assertEquals("DELETED", tombstoneAfterAck.default_pending_op) @@ -2896,7 +2914,7 @@ class BookmarkSyncArchitectureTest { val collectionId = createCollection("NeverPushedCustomDeleteCollection", "remote-never-pushed-delete-collection") val bookmark = bookmarksRepository.addBookmark(8, 16, listOf(collectionId), at(100)) val bookmarkRow = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() val linkBeforeDelete = database.bookmark_collectionsQueries .getCollectionBookmarkFor(bookmarkRow.local_id, collectionId.toLong()) @@ -3000,7 +3018,7 @@ class BookmarkSyncArchitectureTest { collectionBookmarksRepository.removeBookmarkFromCollection(DEFAULT_COLLECTION_ID, bookmark) - assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOneOrNull()) + assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOneOrNull()) assertTrue(collectionBookmarksRepository.fetchMutatedCollectionBookmarks().none()) } @@ -3029,7 +3047,7 @@ class BookmarkSyncArchitectureTest { collectionBookmarksRepository.removeBookmarkFromCollection(DEFAULT_COLLECTION_ID, bookmark) - assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOneOrNull()) + assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOneOrNull()) assertTrue(collectionBookmarksRepository.fetchMutatedCollectionBookmarks().none()) } @@ -3044,14 +3062,14 @@ class BookmarkSyncArchitectureTest { collectionBookmarksRepository.rollbackMutatedCollectionBookmarksInFlight(marked) val rowAfterRollback = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals("CREATED", rowAfterRollback.default_pending_op) assertEquals(1L, rowAfterRollback.default_pending_version) collectionBookmarksRepository.removeBookmarkFromCollection(DEFAULT_COLLECTION_ID, bookmark) - assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOneOrNull()) + assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOneOrNull()) assertTrue(collectionBookmarksRepository.fetchMutatedCollectionBookmarks().none()) } @@ -3066,7 +3084,7 @@ class BookmarkSyncArchitectureTest { collectionBookmarksRepository.removeBookmarkFromCollection(DEFAULT_COLLECTION_ID, bookmark) collectionBookmarksRepository.rollbackMutatedCollectionBookmarksInFlight(marked) - assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOneOrNull()) + assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOneOrNull()) assertTrue(collectionBookmarksRepository.fetchMutatedCollectionBookmarks().none()) } @@ -3131,7 +3149,7 @@ class BookmarkSyncArchitectureTest { .getCollectionBookmarkByLocalId(linkCreate.localID.toLong()) .executeAsOneOrNull() ) - assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOneOrNull()) + assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOneOrNull()) assertTrue(collectionBookmarksRepository.fetchMutatedCollectionBookmarks().none()) } @@ -3144,7 +3162,7 @@ class BookmarkSyncArchitectureTest { at(100) ) val row = database.bookmarksQueries - .getBookmarkByLocalId(defaultBookmark.bookmarkLocalId.toLong()) + .getBookmarkByLocalId(defaultBookmark.bookmarkId.toLong()) .executeAsOne() assertEquals("CREATED", row.default_pending_op) assertEquals(1L, row.default_pending_version) @@ -3153,7 +3171,7 @@ class BookmarkSyncArchitectureTest { assertNull( database.bookmarksQueries - .getBookmarkByLocalId(defaultBookmark.bookmarkLocalId.toLong()) + .getBookmarkByLocalId(defaultBookmark.bookmarkId.toLong()) .executeAsOneOrNull() ) assertTrue(collectionBookmarksRepository.fetchMutatedCollectionBookmarks().none()) @@ -3169,7 +3187,7 @@ class BookmarkSyncArchitectureTest { bookmarksRepository.addBookmark(9, 19, listOf(DEFAULT_COLLECTION_ID), at(150)) val beforeAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals(1L, beforeAck.is_in_default_collection) assertEquals("DELETED", beforeAck.default_pending_op) @@ -3188,7 +3206,7 @@ class BookmarkSyncArchitectureTest { ) val afterAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals(1L, afterAck.is_in_default_collection) assertEquals("remote-default-readd", afterAck.remote_id) @@ -3207,7 +3225,7 @@ class BookmarkSyncArchitectureTest { collectionBookmarksRepository.removeBookmarkFromCollection(DEFAULT_COLLECTION_ID, bookmark) val beforeAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals(0L, beforeAck.is_in_default_collection) assertEquals("DELETED", beforeAck.default_pending_op) @@ -3227,7 +3245,7 @@ class BookmarkSyncArchitectureTest { ) val afterAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals("remote-default-readd-remove", afterAck.remote_id) assertEquals("DELETED", afterAck.default_pending_op) @@ -3257,7 +3275,7 @@ class BookmarkSyncArchitectureTest { ) val afterAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertNull(afterAck.remote_id) assertNull(afterAck.default_pending_op) @@ -3276,7 +3294,7 @@ class BookmarkSyncArchitectureTest { assertEquals("remote-composite-default-bookmark", deleteMutation.model.bookmarkRemoteId) assertNull( database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() .remote_id ) @@ -3291,7 +3309,7 @@ class BookmarkSyncArchitectureTest { collectionBookmarksRepository.removeBookmarkFromCollection(DEFAULT_COLLECTION_ID, localOnlyBookmark) val localOnlyBeforeAck = database.bookmarksQueries - .getBookmarkByLocalId(localOnlyBookmark.localId.toLong()) + .getBookmarkByLocalId(localOnlyBookmark.id.toLong()) .executeAsOne() assertEquals(0L, localOnlyBeforeAck.is_in_default_collection) assertEquals("DELETED", localOnlyBeforeAck.default_pending_op) @@ -3326,7 +3344,7 @@ class BookmarkSyncArchitectureTest { ) val remoteBackedBookmark = bookmarksRepository.addBookmark(9, 4, listOf(DEFAULT_COLLECTION_ID), at(150)) val remoteBackedCreate = collectionBookmarksRepository.fetchMutatedCollectionBookmarks().single { - it.localID == "default:${remoteBackedBookmark.localId}" + it.localID == "default:${remoteBackedBookmark.id}" } collectionBookmarksRepository.markMutatedCollectionBookmarksInFlight(listOf(assertNotNull(remoteBackedCreate.ack))) @@ -3337,12 +3355,12 @@ class BookmarkSyncArchitectureTest { ) val remoteBackedRow = database.bookmarksQueries - .getBookmarkByLocalId(remoteBackedBookmark.localId.toLong()) + .getBookmarkByLocalId(remoteBackedBookmark.id.toLong()) .executeAsOne() assertEquals("remote-pending-default-backed", remoteBackedRow.remote_id) assertEquals("DELETED", remoteBackedRow.default_pending_op) val remoteBackedDelete = collectionBookmarksRepository.fetchMutatedCollectionBookmarks().single { - it.localID == "default:${remoteBackedBookmark.localId}" + it.localID == "default:${remoteBackedBookmark.id}" } assertEquals(Mutation.DELETED, remoteBackedDelete.mutation) assertEquals("remote-pending-default-backed", remoteBackedDelete.model.bookmarkRemoteId) @@ -3357,7 +3375,7 @@ class BookmarkSyncArchitectureTest { assertTrue(readingRepository.deleteReadingBookmark()) val tombstoneBeforeAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals(1L, tombstoneBeforeAck.deleted) assertEquals(0L, tombstoneBeforeAck.is_reading) @@ -3379,7 +3397,7 @@ class BookmarkSyncArchitectureTest { ) val tombstoneAfterAck = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals("remote-pending-reading-create", tombstoneAfterAck.remote_id) assertEquals(1L, tombstoneAfterAck.deleted) @@ -3397,7 +3415,7 @@ class BookmarkSyncArchitectureTest { assertTrue(readingRepository.deleteReadingBookmark()) val readingClear = bookmarksRepository.fetchMutatedBookmarks().single { - it.localID == bookmark.localId + it.localID == bookmark.id } val ack = assertNotNull(readingClear.ack) assertEquals(Mutation.CREATED, readingClear.mutation) @@ -3427,7 +3445,7 @@ class BookmarkSyncArchitectureTest { ) val row = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals("remote-cleared-saved-reading", row.remote_id) assertEquals(0L, row.is_reading) @@ -3435,7 +3453,7 @@ class BookmarkSyncArchitectureTest { assertNull(row.reading_pending_op) assertEquals(markedAck.observedPendingVersion, row.reading_pending_version) assertTrue(bookmarksRepository.fetchMutatedBookmarks().none { - it.localID == bookmark.localId + it.localID == bookmark.id }) } @@ -3444,7 +3462,7 @@ class BookmarkSyncArchitectureTest { val bookmark = readingRepository.addAyahReadingBookmark(9, 22, at(100)) bookmarksRepository.addBookmark(9, 22, at(125)) val readingCreate = bookmarksRepository.fetchMutatedBookmarks().single { - it.localID == bookmark.localId + it.localID == bookmark.id } val ack = assertNotNull(readingCreate.ack) @@ -3473,7 +3491,7 @@ class BookmarkSyncArchitectureTest { ) val row = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() assertEquals("remote-inflight-saved-reading", row.remote_id) assertEquals(0L, row.is_reading) @@ -3481,7 +3499,7 @@ class BookmarkSyncArchitectureTest { assertEquals(markedAck.observedPendingVersion + 1, row.reading_pending_version) val finalReadingState = bookmarksRepository.fetchMutatedBookmarks().single { - it.localID == bookmark.localId + it.localID == bookmark.id } assertEquals(Mutation.CREATED, finalReadingState.mutation) assertEquals(false, finalReadingState.model.isReading) @@ -3497,7 +3515,7 @@ class BookmarkSyncArchitectureTest { assertTrue(readingRepository.deleteReadingBookmark()) bookmarksRepository.rollbackMutatedBookmarksInFlight(marked) - assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOneOrNull()) + assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOneOrNull()) assertTrue(bookmarksRepository.fetchMutatedBookmarks().none()) } @@ -3509,7 +3527,7 @@ class BookmarkSyncArchitectureTest { assertTrue(readingRepository.deleteReadingBookmark()) - assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.localId.toLong()).executeAsOneOrNull()) + assertNull(database.bookmarksQueries.getBookmarkByLocalId(bookmark.id.toLong()).executeAsOneOrNull()) assertTrue(bookmarksRepository.fetchMutatedBookmarks().none()) } @@ -3584,7 +3602,7 @@ class BookmarkSyncArchitectureTest { ) val row = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() val deleteMutation = bookmarksRepository.fetchMutatedBookmarks().single() assertEquals("remote-replayed-reading-bookmark", row.remote_id) @@ -3617,7 +3635,7 @@ class BookmarkSyncArchitectureTest { ) val row = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() val deleteMutation = collectionBookmarksRepository.fetchMutatedCollectionBookmarks().single() assertEquals("remote-replayed-default-bookmark", row.remote_id) @@ -3651,7 +3669,7 @@ class BookmarkSyncArchitectureTest { ) val row = database.bookmarksQueries - .getBookmarkByLocalId(bookmark.localId.toLong()) + .getBookmarkByLocalId(bookmark.id.toLong()) .executeAsOne() val link = database.bookmark_collectionsQueries .getCollectionBookmarkByLocalId(customCreate.localID.toLong()) diff --git a/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/CollectionsRepositoryTest.kt b/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/CollectionsRepositoryTest.kt index 37a62698..f7a91cf8 100644 --- a/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/CollectionsRepositoryTest.kt +++ b/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/CollectionsRepositoryTest.kt @@ -44,7 +44,7 @@ class CollectionsRepositoryTest { @Test fun `addCollection respects explicit timestamp`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1234L)) - val record = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val record = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() assertEquals(1234L, collection.lastUpdated.fromPlatform().toEpochMilliseconds()) assertEquals(1234L, record.created_at) @@ -54,7 +54,7 @@ class CollectionsRepositoryTest { @Test fun `addCollection advances pending version for fresh create`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1234L)) - val record = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val record = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() val mutation = repository.fetchMutatedCollections().single() assertEquals(1L, record.pending_version) @@ -66,8 +66,8 @@ class CollectionsRepositoryTest { fun `updateCollection respects explicit timestamp and preserves created_at`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1000L)) - val updated = repository.updateCollection(collection.localId, "Updated", timestamp(2345L)) - val record = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val updated = repository.updateCollection(collection.id, "Updated", timestamp(2345L)) + val record = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() assertEquals(2345L, updated.lastUpdated.fromPlatform().toEpochMilliseconds()) assertEquals(1000L, record.created_at) @@ -77,7 +77,7 @@ class CollectionsRepositoryTest { @Test fun `fetchMutatedCollections carries created_at separately from modified_at`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1000L)) - repository.updateCollection(collection.localId, "Updated", timestamp(2345L)) + repository.updateCollection(collection.id, "Updated", timestamp(2345L)) val mutation = repository.fetchMutatedCollections().single() @@ -110,13 +110,13 @@ class CollectionsRepositoryTest { @Test fun `updateCollection rejects deleted collection without renaming tombstone`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1000L)) - repository.deleteCollection(collection.localId) + repository.deleteCollection(collection.id) assertFailsWith { - repository.updateCollection(collection.localId, "Renamed", timestamp(2000L)) + repository.updateCollection(collection.id, "Renamed", timestamp(2000L)) } - val tombstone = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val tombstone = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() assertEquals("Favorites", tombstone.name) assertEquals(1L, tombstone.deleted) assertEquals(emptyList(), repository.getAllCollections()) @@ -287,7 +287,7 @@ class CollectionsRepositoryTest { fun `stale created collection ACK binds remote id and leaves newer rename pending`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1000L)) val staleMutation = repository.fetchMutatedCollections().single() - repository.updateCollection(collection.localId, "Renamed", timestamp(2000L)) + repository.updateCollection(collection.id, "Renamed", timestamp(2000L)) repository.applyRemoteChanges( updatesToPersist = listOf( @@ -301,12 +301,12 @@ class CollectionsRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val record = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedCollections().single() assertEquals("remote-created-collection-id", record.remote_id) assertEquals("Renamed", record.name) assertEquals(1L, record.is_edited) - assertEquals(collection.localId, remaining.localID) + assertEquals(collection.id, remaining.localID) assertEquals("remote-created-collection-id", remaining.remoteID) assertEquals(Mutation.MODIFIED, remaining.mutation) } @@ -315,7 +315,7 @@ class CollectionsRepositoryTest { fun `stale created collection ACK binds remote id and leaves delete pending`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1000L)) val staleMutation = repository.fetchMutatedCollections().single() - repository.deleteCollection(collection.localId) + repository.deleteCollection(collection.id) assertEquals(emptyList(), repository.getAllCollections()) assertEquals(emptyList(), repository.fetchMutatedCollections()) @@ -331,12 +331,12 @@ class CollectionsRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val record = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedCollections().single() assertEquals(emptyList(), repository.getAllCollections()) assertEquals("remote-created-collection-id", record.remote_id) assertEquals(1L, record.deleted) - assertEquals(collection.localId, remaining.localID) + assertEquals(collection.id, remaining.localID) assertEquals("remote-created-collection-id", remaining.remoteID) assertEquals(Mutation.DELETED, remaining.mutation) } @@ -358,11 +358,11 @@ class CollectionsRepositoryTest { deleteExisting = true ) - val tombstone = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val tombstone = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() val imported = database.collectionsQueries.getCollectionByName("Favorites").executeAsOne() assertEquals(null, tombstone.remote_id) assertEquals(1L, tombstone.deleted) - assertEquals(collection.localId.toLong(), tombstone.local_id) + assertEquals(collection.id.toLong(), tombstone.local_id) assertEquals(1, repository.getAllCollections().size) assertEquals(1, repository.fetchMutatedCollections().size) assertEquals("Favorites", imported.name) @@ -382,10 +382,10 @@ class CollectionsRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val record = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedCollections() - val tombstoneDelete = remaining.single { it.localID == collection.localId } - val importedCreate = remaining.single { it.localID != collection.localId } + val tombstoneDelete = remaining.single { it.localID == collection.id } + val importedCreate = remaining.single { it.localID != collection.id } assertEquals("remote-created-collection-id", record.remote_id) assertEquals(1L, record.deleted) assertEquals("remote-created-collection-id", tombstoneDelete.remoteID) @@ -422,7 +422,7 @@ class CollectionsRepositoryTest { localMutationsToClear = listOf(staleCreate) ) val staleDelete = repository.fetchMutatedCollections() - .single { it.localID == collection.localId } + .single { it.localID == collection.id } repository.applyRemoteChanges( updatesToPersist = listOf( @@ -437,10 +437,10 @@ class CollectionsRepositoryTest { val active = repository.getAllCollections().single() val remainingMutation = repository.fetchMutatedCollections().single() - assertNull(database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOneOrNull()) + assertNull(database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOneOrNull()) assertEquals("Favorites", active.name) assertEquals(Mutation.CREATED, remainingMutation.mutation) - assertEquals(active.localId, remainingMutation.localID) + assertEquals(active.id, remainingMutation.localID) } @Test @@ -472,7 +472,7 @@ class CollectionsRepositoryTest { fun `remote created collection without ACK does not bind stale planned local create by name`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1000L)) val staleMutation = repository.fetchMutatedCollections().single() - repository.updateCollection(collection.localId, "Renamed", timestamp(2000L)) + repository.updateCollection(collection.id, "Renamed", timestamp(2000L)) repository.applyRemoteChanges( updatesToPersist = listOf( @@ -485,14 +485,14 @@ class CollectionsRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val localRecord = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val localRecord = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() val remoteRecord = database.collectionsQueries.getCollectionByRemoteId("remote-created-collection-id") .executeAsOne() val remaining = repository.fetchMutatedCollections().single() assertEquals(null, localRecord.remote_id) assertEquals("Renamed", localRecord.name) assertEquals("Favorites", remoteRecord.name) - assertEquals(collection.localId, remaining.localID) + assertEquals(collection.id, remaining.localID) assertEquals(Mutation.CREATED, remaining.mutation) } @@ -511,7 +511,7 @@ class CollectionsRepositoryTest { localMutationsToClear = emptyList() ) - val record = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val record = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() assertEquals("remote-created-collection-id", record.remote_id) assertEquals(0L, record.is_edited) assertEquals(1, repository.getAllCollections().size) @@ -521,7 +521,7 @@ class CollectionsRepositoryTest { @Test fun `remote created collection without ACK binds deleted pending create by name and leaves delete pending`() = runTest { val collection = repository.addCollection("Favorites", timestamp(1000L)) - repository.deleteCollection(collection.localId) + repository.deleteCollection(collection.id) assertEquals(emptyList(), repository.getAllCollections()) assertEquals(emptyList(), repository.fetchMutatedCollections()) @@ -536,12 +536,12 @@ class CollectionsRepositoryTest { localMutationsToClear = emptyList() ) - val record = database.collectionsQueries.getCollectionByLocalId(collection.localId.toLong()).executeAsOne() + val record = database.collectionsQueries.getCollectionByLocalId(collection.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedCollections().single() assertEquals(emptyList(), repository.getAllCollections()) assertEquals("remote-created-collection-id", record.remote_id) assertEquals(1L, record.deleted) - assertEquals(collection.localId, remaining.localID) + assertEquals(collection.id, remaining.localID) assertEquals("remote-created-collection-id", remaining.remoteID) assertEquals(Mutation.DELETED, remaining.mutation) } @@ -575,14 +575,14 @@ class CollectionsRepositoryTest { localMutationsToClear = emptyList() ) - val deletedRecord = database.collectionsQueries.getCollectionByLocalId(deleted.localId.toLong()).executeAsOne() + val deletedRecord = database.collectionsQueries.getCollectionByLocalId(deleted.id.toLong()).executeAsOne() val active = repository.getAllCollections().single() val remaining = repository.fetchMutatedCollections() assertEquals("remote-created-collection-id", deletedRecord.remote_id) assertEquals(1L, deletedRecord.deleted) assertEquals("Favorites", active.name) - assertEquals(deleted.localId, remaining.single { it.mutation == Mutation.DELETED }.localID) - assertEquals(active.localId, remaining.single { it.mutation == Mutation.CREATED }.localID) + assertEquals(deleted.id, remaining.single { it.mutation == Mutation.DELETED }.localID) + assertEquals(active.id, remaining.single { it.mutation == Mutation.CREATED }.localID) } @Test @@ -601,9 +601,9 @@ class CollectionsRepositoryTest { deleteExisting = true ) val second = repository.getAllCollections().single() - repository.deleteCollection(second.localId) + repository.deleteCollection(second.id) assertEquals(2, database.collectionsQueries.getPendingCreatedCollectionsByName("Favorites").executeAsList().size) - assertEquals(1L, database.collectionsQueries.getCollectionByLocalId(first.localId.toLong()).executeAsOne().deleted) + assertEquals(1L, database.collectionsQueries.getCollectionByLocalId(first.id.toLong()).executeAsOne().deleted) assertFailsWith { repository.applyRemoteChanges( @@ -648,11 +648,11 @@ class CollectionsRepositoryTest { val record = database.collectionsQueries.getCollectionByRemoteId("remote-collection-id").executeAsOne() val remaining = repository.fetchMutatedCollections().single() - assertEquals(listOf(readded.localId), repository.getAllCollections().map { it.localId }) + assertEquals(listOf(readded.id), repository.getAllCollections().map { it.id }) assertEquals(0L, record.deleted) assertEquals(1L, record.is_edited) assertEquals(Mutation.MODIFIED, remaining.mutation) - assertEquals(readded.localId, remaining.localID) + assertEquals(readded.id, remaining.localID) assertEquals("remote-collection-id", remaining.remoteID) } diff --git a/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/NotesRepositoryTest.kt b/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/NotesRepositoryTest.kt index f655bb6d..1bb0468a 100644 --- a/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/NotesRepositoryTest.kt +++ b/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/NotesRepositoryTest.kt @@ -56,7 +56,7 @@ class NotesRepositoryTest { endAyah = 13, timestamp = timestamp(1_700_000_001_234L) ) - val record = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val record = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() assertEquals(1_700_000_001_234L, note.lastUpdated.fromPlatform().toEpochMilliseconds()) assertEquals(1_700_000_001_234L, record.created_at) @@ -68,7 +68,7 @@ class NotesRepositoryTest { val note = repository.addNote("test note", 2, 13, 2, 13, timestamp(1_700_000_001_000L)) val updated = repository.updateNote( - note.localId, + note.id, "updated note", 2, 14, @@ -76,7 +76,7 @@ class NotesRepositoryTest { 14, timestamp(1_700_000_002_345L) ) - val record = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val record = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() assertEquals(1_700_000_002_345L, updated.lastUpdated.fromPlatform().toEpochMilliseconds()) assertEquals(1_700_000_001_000L, record.created_at) @@ -87,7 +87,7 @@ class NotesRepositoryTest { fun `fetchMutatedNotes carries created_at separately from modified_at`() = runTest { val note = repository.addNote("test note", 2, 13, 2, 13, timestamp(1_700_000_001_000L)) repository.updateNote( - note.localId, + note.id, "updated note", 2, 14, @@ -260,7 +260,7 @@ class NotesRepositoryTest { ) val staleMutation = repository.fetchMutatedNotes(0).single() repository.updateNote( - note.localId, + note.id, "newer note", 2, 14, @@ -286,13 +286,13 @@ class NotesRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val record = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedNotes(0).single() assertEquals("remote-created-note-id", record.remote_id) assertEquals("newer note", record.note) assertEquals(QuranData.getAyahId(2, 14).toLong(), record.start_ayah_id) assertEquals(1L, record.is_edited) - assertEquals(note.localId, remaining.localID) + assertEquals(note.id, remaining.localID) assertEquals("remote-created-note-id", remaining.remoteID) assertEquals(Mutation.MODIFIED, remaining.mutation) } @@ -308,7 +308,7 @@ class NotesRepositoryTest { timestamp = timestamp(1_700_000_001_000L) ) val staleMutation = repository.fetchMutatedNotes(0).single() - repository.deleteNote(note.localId) + repository.deleteNote(note.id) assertEquals(emptyList(), repository.getAllNotes()) assertEquals(emptyList(), repository.fetchMutatedNotes(0)) @@ -329,13 +329,13 @@ class NotesRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val record = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedNotes(0).single() assertEquals(emptyList(), repository.getAllNotes()) assertEquals("remote-created-note-id", record.remote_id) assertEquals(1L, record.deleted) assertEquals("uploaded note", record.note) - assertEquals(note.localId, remaining.localID) + assertEquals(note.id, remaining.localID) assertEquals("remote-created-note-id", remaining.remoteID) assertEquals(Mutation.DELETED, remaining.mutation) } @@ -356,7 +356,7 @@ class NotesRepositoryTest { deleteExisting = true ) - val tombstone = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val tombstone = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() assertEquals(null, tombstone.remote_id) assertEquals(1L, tombstone.deleted) assertEquals(emptyList(), repository.getAllNotes()) @@ -379,11 +379,11 @@ class NotesRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val record = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedNotes(0).single() assertEquals("remote-created-note-id", record.remote_id) assertEquals(1L, record.deleted) - assertEquals(note.localId, remaining.localID) + assertEquals(note.id, remaining.localID) assertEquals("remote-created-note-id", remaining.remoteID) assertEquals(Mutation.DELETED, remaining.mutation) } @@ -415,7 +415,7 @@ class NotesRepositoryTest { localMutationsToClear = emptyList() ) - val localRecord = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val localRecord = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() assertEquals("remote-created-note-id", localRecord.remote_id) assertEquals(1, repository.getAllNotes().size) assertEquals(emptyList(), repository.fetchMutatedNotes(0)) @@ -457,11 +457,11 @@ class NotesRepositoryTest { ) val existingRemote = database.notesQueries.getNoteByRemoteId("remote-created-note-id").executeAsOne() - val duplicateRecord = database.notesQueries.getNoteByLocalId(duplicate.localId.toLong()).executeAsOne() + val duplicateRecord = database.notesQueries.getNoteByLocalId(duplicate.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedNotes(0).single() assertEquals("remote-created-note-id", existingRemote.remote_id) assertEquals(null, duplicateRecord.remote_id) - assertEquals(duplicate.localId, remaining.localID) + assertEquals(duplicate.id, remaining.localID) assertEquals(Mutation.CREATED, remaining.mutation) } @@ -475,7 +475,7 @@ class NotesRepositoryTest { endAyah = 13, timestamp = timestamp(1_700_000_001_000L) ) - repository.deleteNote(note.localId) + repository.deleteNote(note.id) assertEquals(emptyList(), repository.getAllNotes()) assertEquals(emptyList(), repository.fetchMutatedNotes(0)) @@ -495,12 +495,12 @@ class NotesRepositoryTest { localMutationsToClear = emptyList() ) - val localRecord = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val localRecord = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() val remaining = repository.fetchMutatedNotes(0).single() assertEquals(emptyList(), repository.getAllNotes()) assertEquals("remote-created-note-id", localRecord.remote_id) assertEquals(1L, localRecord.deleted) - assertEquals(note.localId, remaining.localID) + assertEquals(note.id, remaining.localID) assertEquals("remote-created-note-id", remaining.remoteID) assertEquals(Mutation.DELETED, remaining.mutation) } @@ -515,7 +515,7 @@ class NotesRepositoryTest { endAyah = 13, timestamp = timestamp(1_700_000_001_000L) ) - repository.deleteNote(deleted.localId) + repository.deleteNote(deleted.id) val active = repository.addNote( body = "same note", startSura = 2, @@ -541,8 +541,8 @@ class NotesRepositoryTest { localMutationsToClear = emptyList() ) - val deletedRecord = database.notesQueries.getNoteByLocalId(deleted.localId.toLong()).executeAsOne() - val activeRecord = database.notesQueries.getNoteByLocalId(active.localId.toLong()).executeAsOne() + val deletedRecord = database.notesQueries.getNoteByLocalId(deleted.id.toLong()).executeAsOne() + val activeRecord = database.notesQueries.getNoteByLocalId(active.id.toLong()).executeAsOne() val remoteRecord = database.notesQueries.getNoteByRemoteId("remote-created-note-id").executeAsOne() val remaining = repository.fetchMutatedNotes(0) assertEquals(null, deletedRecord.remote_id) @@ -551,7 +551,7 @@ class NotesRepositoryTest { assertEquals(0L, activeRecord.deleted) assertEquals("remote-created-note-id", remoteRecord.remote_id) assertEquals(2, repository.getAllNotes().size) - assertEquals(listOf(active.localId), remaining.map { it.localID }) + assertEquals(listOf(active.id), remaining.map { it.localID }) assertEquals(Mutation.CREATED, remaining.single().mutation) } @@ -559,8 +559,8 @@ class NotesRepositoryTest { fun `ambiguous deleted note replay candidates persist remote note separately`() = runTest { val first = repository.addNote("same note", 2, 13, 2, 13, timestamp(1_700_000_001_000L)) val second = repository.addNote("same note", 2, 13, 2, 13, timestamp(1_700_000_002_000L)) - repository.deleteNote(first.localId) - repository.deleteNote(second.localId) + repository.deleteNote(first.id) + repository.deleteNote(second.id) repository.applyRemoteChanges( updatesToPersist = listOf( @@ -578,8 +578,8 @@ class NotesRepositoryTest { localMutationsToClear = emptyList() ) - val firstRecord = database.notesQueries.getNoteByLocalId(first.localId.toLong()).executeAsOne() - val secondRecord = database.notesQueries.getNoteByLocalId(second.localId.toLong()).executeAsOne() + val firstRecord = database.notesQueries.getNoteByLocalId(first.id.toLong()).executeAsOne() + val secondRecord = database.notesQueries.getNoteByLocalId(second.id.toLong()).executeAsOne() val remoteRecord = database.notesQueries.getNoteByRemoteId("remote-created-note-id").executeAsOne() assertEquals(null, firstRecord.remote_id) assertEquals(1L, firstRecord.deleted) @@ -618,7 +618,7 @@ class NotesRepositoryTest { localMutationsToClear = emptyList() ) - val localRecord = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val localRecord = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() val remoteRecord = database.notesQueries.getNoteByRemoteId("remote-created-note-id").executeAsOne() assertEquals(null, localRecord.remote_id) assertEquals("remote-created-note-id", remoteRecord.remote_id) @@ -693,7 +693,7 @@ class NotesRepositoryTest { localMutationsToClear = emptyList() ) - val localRecord = database.notesQueries.getNoteByLocalId(note.localId.toLong()).executeAsOne() + val localRecord = database.notesQueries.getNoteByLocalId(note.id.toLong()).executeAsOne() val remoteRecord = database.notesQueries.getNoteByRemoteId("remote-created-note-id").executeAsOne() assertEquals(null, localRecord.remote_id) assertEquals("remote-created-note-id", remoteRecord.remote_id) diff --git a/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/ReadingSessionsRepositoryTest.kt b/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/ReadingSessionsRepositoryTest.kt index f6535182..163e988a 100644 --- a/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/ReadingSessionsRepositoryTest.kt +++ b/persistence/src/commonTest/kotlin/com/quran/shared/persistence/repository/ReadingSessionsRepositoryTest.kt @@ -83,7 +83,7 @@ class ReadingSessionsRepositoryTest { @Test fun `fetchMutatedReadingSessions carries created_at separately from modified_at`() = runTest { val readingSession = repository.addReadingSession(2, 255, timestamp(1000L)) - repository.updateReadingSession(readingSession.localId, 3, 10, timestamp(2345L)) + repository.updateReadingSession(readingSession.id, 3, 10, timestamp(2345L)) val mutation = repository.fetchMutatedReadingSessions().single() @@ -244,7 +244,7 @@ class ReadingSessionsRepositoryTest { val record = database.reading_sessionsQueries.getReadingSessionByLocalId(original.local_id) .executeAsOne() - assertEquals(original.local_id.toString(), updated.localId) + assertEquals(original.local_id.toString(), updated.id) assertEquals(original.local_id, record.local_id) assertEquals("remote-reading-session-id", record.remote_id) assertEquals(1000L, record.created_at) @@ -259,8 +259,8 @@ class ReadingSessionsRepositoryTest { val readingSession = repository.addReadingSession(2, 255) now = 1234567890123L - val updated = repository.updateReadingSession(readingSession.localId, 3, 10) - val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.localId.toLong()) + val updated = repository.updateReadingSession(readingSession.id, 3, 10) + val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() assertEquals(1234567890123L, updated.lastUpdated.fromPlatform().toEpochMilliseconds()) @@ -272,8 +272,8 @@ class ReadingSessionsRepositoryTest { repository = ReadingSessionsRepositoryImpl(database) { 9999L } val readingSession = repository.addReadingSession(2, 255, timestamp(1000L)) - val updated = repository.updateReadingSession(readingSession.localId, 3, 10, timestamp(3456L)) - val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.localId.toLong()) + val updated = repository.updateReadingSession(readingSession.id, 3, 10, timestamp(3456L)) + val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() assertEquals(3456L, updated.lastUpdated.fromPlatform().toEpochMilliseconds()) @@ -311,9 +311,9 @@ class ReadingSessionsRepositoryTest { val readingSession = repository.addReadingSession(2, 255) now = 2000L - repository.updateReadingSession(readingSession.localId, 3, 10) + repository.updateReadingSession(readingSession.id, 3, 10) - val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.localId.toLong()) + val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() val localMutation = repository.fetchMutatedReadingSessions().single() assertNull(record.remote_id) @@ -328,16 +328,16 @@ class ReadingSessionsRepositoryTest { val target = repository.addReadingSession(3, 10) assertFailsWith { - repository.updateReadingSession(source.localId, 3, 10) + repository.updateReadingSession(source.id, 3, 10) } - val sourceRecord = database.reading_sessionsQueries.getReadingSessionByLocalId(source.localId.toLong()) + val sourceRecord = database.reading_sessionsQueries.getReadingSessionByLocalId(source.id.toLong()) .executeAsOne() val targetRecord = database.reading_sessionsQueries.getReadingSessionForChapterVerse(3L, 10L) .executeAsOne() assertEquals(2L, sourceRecord.chapter_number) assertEquals(255L, sourceRecord.verse_number) - assertEquals(target.localId.toLong(), targetRecord.local_id) + assertEquals(target.id.toLong(), targetRecord.local_id) } @Test @@ -380,15 +380,15 @@ class ReadingSessionsRepositoryTest { val created = repository.addReadingSession(2, 255) assertTrue(repository.deleteReadingSession(2, 255)) val tombstone = database.reading_sessionsQueries - .getReadingSessionByLocalId(created.localId.toLong()) + .getReadingSessionByLocalId(created.id.toLong()) .executeAsOne() assertFailsWith { - repository.updateReadingSession(created.localId, 3, 10) + repository.updateReadingSession(created.id, 3, 10) } val unchanged = database.reading_sessionsQueries - .getReadingSessionByLocalId(created.localId.toLong()) + .getReadingSessionByLocalId(created.id.toLong()) .executeAsOne() assertEquals(1L, unchanged.deleted) assertEquals(0L, unchanged.is_edited) @@ -427,7 +427,7 @@ class ReadingSessionsRepositoryTest { } val pruned = database.reading_sessionsQueries - .getReadingSessionByLocalId(oldest.localId.toLong()) + .getReadingSessionByLocalId(oldest.id.toLong()) .executeAsOne() assertEquals(1L, pruned.deleted) assertEquals((21 downTo 2).toList(), repository.getReadingSessions().map { it.ayah }) @@ -489,7 +489,7 @@ class ReadingSessionsRepositoryTest { assertEquals((20 downTo 1).toList(), receiveEmission().map { it.ayah }) now = 21000L - repository.updateReadingSession(oldest.localId, 2, 21) + repository.updateReadingSession(oldest.id, 2, 21) assertEquals((21 downTo 2).toList(), receiveEmission().map { it.ayah }) @@ -563,7 +563,7 @@ class ReadingSessionsRepositoryTest { assertEquals(1, readingSessions.size) assertEquals(2, readingSessions.single().sura) assertEquals(255, readingSessions.single().ayah) - assertEquals(localReadingSession.localId, readingSessions.single().localId) + assertEquals(localReadingSession.id, readingSessions.single().id) assertEquals(emptyList(), repository.fetchMutatedReadingSessions()) } @@ -631,7 +631,7 @@ class ReadingSessionsRepositoryTest { assertFailsWith { repository.applyRemoteChanges( updatesToPersist = emptyList(), - localMutationIdsToClear = listOf(readingSession.localId) + localMutationIdsToClear = listOf(readingSession.id) ) } } @@ -744,7 +744,7 @@ class ReadingSessionsRepositoryTest { fun `stale created reading session ACK binds remote id and leaves newer move pending`() = runTest { val readingSession = repository.addReadingSession(2, 255, timestamp(1000L)) val staleMutation = repository.fetchMutatedReadingSessions().single() - repository.updateReadingSession(readingSession.localId, 3, 10, timestamp(2000L)) + repository.updateReadingSession(readingSession.id, 3, 10, timestamp(2000L)) repository.applyRemoteChangesForMutations( updatesToPersist = listOf( @@ -762,14 +762,14 @@ class ReadingSessionsRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.localId.toLong()) + val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() val remaining = repository.fetchMutatedReadingSessions().single() assertEquals("remote-created-reading-session-id", record.remote_id) assertEquals(3L, record.chapter_number) assertEquals(10L, record.verse_number) assertEquals(1L, record.is_edited) - assertEquals(readingSession.localId, remaining.localID) + assertEquals(readingSession.id, remaining.localID) assertEquals("remote-created-reading-session-id", remaining.remoteID) assertEquals(Mutation.MODIFIED, remaining.mutation) } @@ -798,13 +798,13 @@ class ReadingSessionsRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.localId.toLong()) + val record = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() val remaining = repository.fetchMutatedReadingSessions().single() assertEquals(emptyList(), repository.getReadingSessions()) assertEquals("remote-created-reading-session-id", record.remote_id) assertEquals(1L, record.deleted) - assertEquals(readingSession.localId, remaining.localID) + assertEquals(readingSession.id, remaining.localID) assertEquals("remote-created-reading-session-id", remaining.remoteID) assertEquals(Mutation.DELETED, remaining.mutation) } @@ -827,14 +827,14 @@ class ReadingSessionsRepositoryTest { ) val tombstone = database.reading_sessionsQueries - .getReadingSessionByLocalId(readingSession.localId.toLong()) + .getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() val imported = database.reading_sessionsQueries .getReadingSessionForChapterVerse(2L, 255L) .executeAsOne() assertEquals(null, tombstone.remote_id) assertEquals(1L, tombstone.deleted) - assertEquals(readingSession.localId.toLong(), tombstone.local_id) + assertEquals(readingSession.id.toLong(), tombstone.local_id) assertEquals(1, repository.getReadingSessions().size) assertEquals(1, repository.fetchMutatedReadingSessions().size) assertEquals(0L, imported.deleted) @@ -858,11 +858,11 @@ class ReadingSessionsRepositoryTest { ) val record = database.reading_sessionsQueries - .getReadingSessionByLocalId(readingSession.localId.toLong()) + .getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() val remaining = repository.fetchMutatedReadingSessions() - val tombstoneDelete = remaining.single { it.localID == readingSession.localId } - val importedCreate = remaining.single { it.localID != readingSession.localId } + val tombstoneDelete = remaining.single { it.localID == readingSession.id } + val importedCreate = remaining.single { it.localID != readingSession.id } assertEquals("remote-created-reading-session-id", record.remote_id) assertEquals(1L, record.deleted) assertEquals("remote-created-reading-session-id", tombstoneDelete.remoteID) @@ -903,7 +903,7 @@ class ReadingSessionsRepositoryTest { localMutationsToClear = listOf(staleCreate) ) val staleDelete = repository.fetchMutatedReadingSessions() - .single { it.localID == readingSession.localId } + .single { it.localID == readingSession.id } repository.applyRemoteChangesForMutations( updatesToPersist = listOf( @@ -924,13 +924,13 @@ class ReadingSessionsRepositoryTest { val remainingMutation = repository.fetchMutatedReadingSessions().single() assertNull( database.reading_sessionsQueries - .getReadingSessionByLocalId(readingSession.localId.toLong()) + .getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOneOrNull() ) assertEquals(2, active.sura) assertEquals(255, active.ayah) assertEquals(Mutation.CREATED, remainingMutation.mutation) - assertEquals(active.localId, remainingMutation.localID) + assertEquals(active.id, remainingMutation.localID) } @Test @@ -946,11 +946,11 @@ class ReadingSessionsRepositoryTest { } } val staleMutation = repository.fetchMutatedReadingSessions() - .single { it.localID == oldest.localId } + .single { it.localID == oldest.id } now = 21_000L repository.addReadingSession(2, 21) - assertTrue(repository.fetchMutatedReadingSessions().none { it.localID == oldest.localId }) + assertTrue(repository.fetchMutatedReadingSessions().none { it.localID == oldest.id }) repository.applyRemoteChangesForMutations( updatesToPersist = listOf( @@ -968,10 +968,10 @@ class ReadingSessionsRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val record = database.reading_sessionsQueries.getReadingSessionByLocalId(oldest.localId.toLong()) + val record = database.reading_sessionsQueries.getReadingSessionByLocalId(oldest.id.toLong()) .executeAsOne() val remaining = repository.fetchMutatedReadingSessions() - .single { it.localID == oldest.localId } + .single { it.localID == oldest.id } assertEquals((21 downTo 2).toList(), repository.getReadingSessions().map { it.ayah }) assertEquals("remote-pruned-reading-session-id", record.remote_id) assertEquals(1L, record.deleted) @@ -992,7 +992,7 @@ class ReadingSessionsRepositoryTest { } } val oldestCreate = repository.fetchMutatedReadingSessions() - .single { it.localID == oldest.localId } + .single { it.localID == oldest.id } val observedPendingVersion = oldestCreate.ack!!.observedPendingVersion repository.applyRemoteChangesForMutations( @@ -1011,7 +1011,7 @@ class ReadingSessionsRepositoryTest { ) val pruned = database.reading_sessionsQueries - .getReadingSessionByLocalId(oldest.localId.toLong()) + .getReadingSessionByLocalId(oldest.id.toLong()) .executeAsOne() assertEquals(1L, pruned.deleted) assertEquals(observedPendingVersion, pruned.pending_version) @@ -1038,13 +1038,13 @@ class ReadingSessionsRepositoryTest { ) val localRecord = database.reading_sessionsQueries - .getReadingSessionByLocalId(readingSession.localId.toLong()) + .getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() val readingSessions = repository.getReadingSessions() assertEquals("remote-created-reading-session-id", localRecord.remote_id) assertEquals(0L, localRecord.is_edited) assertEquals(1, readingSessions.size) - assertEquals(readingSession.localId, readingSessions.single().localId) + assertEquals(readingSession.id, readingSessions.single().id) assertEquals(emptyList(), repository.fetchMutatedReadingSessions()) } @@ -1071,13 +1071,13 @@ class ReadingSessionsRepositoryTest { ) val localRecord = database.reading_sessionsQueries - .getReadingSessionByLocalId(readingSession.localId.toLong()) + .getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() val remaining = repository.fetchMutatedReadingSessions().single() assertEquals(emptyList(), repository.getReadingSessions()) assertEquals("remote-created-reading-session-id", localRecord.remote_id) assertEquals(1L, localRecord.deleted) - assertEquals(readingSession.localId, remaining.localID) + assertEquals(readingSession.id, remaining.localID) assertEquals("remote-created-reading-session-id", remaining.remoteID) assertEquals(Mutation.DELETED, remaining.mutation) } @@ -1104,20 +1104,20 @@ class ReadingSessionsRepositoryTest { ) val tombstone = database.reading_sessionsQueries - .getReadingSessionByLocalId(original.localId.toLong()) + .getReadingSessionByLocalId(original.id.toLong()) .executeAsOne() val active = database.reading_sessionsQueries - .getReadingSessionByLocalId(readded.localId.toLong()) + .getReadingSessionByLocalId(readded.id.toLong()) .executeAsOne() val visible = repository.getReadingSessions().single() val remaining = repository.fetchMutatedReadingSessions() - val pendingDelete = remaining.single { it.localID == original.localId } - val pendingCreate = remaining.single { it.localID == readded.localId } + val pendingDelete = remaining.single { it.localID == original.id } + val pendingCreate = remaining.single { it.localID == readded.id } assertEquals("remote-created-reading-session-id", tombstone.remote_id) assertEquals(1L, tombstone.deleted) assertEquals(null, active.remote_id) assertEquals(0L, active.deleted) - assertEquals(readded.localId, visible.localId) + assertEquals(readded.id, visible.id) assertEquals("remote-created-reading-session-id", pendingDelete.remoteID) assertEquals(Mutation.DELETED, pendingDelete.mutation) assertEquals(null, pendingCreate.remoteID) @@ -1157,7 +1157,7 @@ class ReadingSessionsRepositoryTest { val tombstones = database.reading_sessionsQueries .getDeletedPendingCreatedReadingSessionsForChapterVerse(2L, 255L) .executeAsList() - assertEquals(listOf(first.localId.toLong(), second.localId.toLong()), tombstones.map { it.local_id }) + assertEquals(listOf(first.id.toLong(), second.id.toLong()), tombstones.map { it.local_id }) assertEquals(emptyList(), repository.getReadingSessions()) } @@ -1165,7 +1165,7 @@ class ReadingSessionsRepositoryTest { fun `remote created reading session without ACK does not bind stale planned local create by position`() = runTest { val readingSession = repository.addReadingSession(2, 255, timestamp(1000L)) val staleMutation = repository.fetchMutatedReadingSessions().single() - repository.updateReadingSession(readingSession.localId, 3, 10, timestamp(2000L)) + repository.updateReadingSession(readingSession.id, 3, 10, timestamp(2000L)) repository.applyRemoteChangesForMutations( updatesToPersist = listOf( @@ -1182,7 +1182,7 @@ class ReadingSessionsRepositoryTest { localMutationsToClear = listOf(staleMutation) ) - val localRecord = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.localId.toLong()) + val localRecord = database.reading_sessionsQueries.getReadingSessionByLocalId(readingSession.id.toLong()) .executeAsOne() val remoteRecord = database.reading_sessionsQueries .getReadingSessionByRemoteId("remote-created-reading-session-id") @@ -1193,7 +1193,7 @@ class ReadingSessionsRepositoryTest { assertEquals(10L, localRecord.verse_number) assertEquals(2L, remoteRecord.chapter_number) assertEquals(255L, remoteRecord.verse_number) - assertEquals(readingSession.localId, remaining.localID) + assertEquals(readingSession.id, remaining.localID) assertEquals(Mutation.CREATED, remaining.mutation) } diff --git a/sync-pipelines/src/commonMain/kotlin/com/quran/shared/pipeline/QuranDataService.kt b/sync-pipelines/src/commonMain/kotlin/com/quran/shared/pipeline/QuranDataService.kt index 67c96122..050f320e 100644 --- a/sync-pipelines/src/commonMain/kotlin/com/quran/shared/pipeline/QuranDataService.kt +++ b/sync-pipelines/src/commonMain/kotlin/com/quran/shared/pipeline/QuranDataService.kt @@ -126,10 +126,10 @@ class QuranDataService internal constructor( private val syncClientFactory: QuranDataServiceSynchronizationClientFactory ) { /** - * Stable local identifier for the virtual default bookmark collection. + * Stable mobile-sync identifier for the virtual default bookmark collection. * * The default collection is not persisted as a collection row. Read APIs synthesize it from - * bookmark default-membership state so callers can treat it like other collections by local ID. + * bookmark default-membership state so callers can treat it like other collections by ID. */ val defaultCollectionId: String = DEFAULT_COLLECTION_ID @@ -195,7 +195,7 @@ class QuranDataService internal constructor( collections .filterNot { it.isDefault } .map { collection -> - collectionBookmarksRepository.getBookmarksForCollectionFlow(collection.localId) + collectionBookmarksRepository.getBookmarksForCollectionFlow(collection.id) .map { bookmarks: List -> CollectionWithAyahBookmarks(collection, bookmarks) } @@ -398,9 +398,9 @@ class QuranDataService internal constructor( } @NativeCoroutines - suspend fun addBookmark(sura: Int, ayah: Int, collectionLocalIds: List): AyahBookmark { + suspend fun addBookmark(sura: Int, ayah: Int, collectionIds: List): AyahBookmark { return mutatingCall("Failed to add ayah bookmark with collection memberships") { - bookmarksRepository.addBookmark(sura, ayah, collectionLocalIds) + bookmarksRepository.addBookmark(sura, ayah, collectionIds) } } @@ -408,18 +408,18 @@ class QuranDataService internal constructor( suspend fun addBookmark( sura: Int, ayah: Int, - collectionLocalIds: List, + collectionIds: List, timestamp: PlatformDateTime ): AyahBookmark { return mutatingCall("Failed to add ayah bookmark with collection memberships") { - bookmarksRepository.addBookmark(sura, ayah, collectionLocalIds, timestamp) + bookmarksRepository.addBookmark(sura, ayah, collectionIds, timestamp) } } @NativeCoroutines - suspend fun replaceBookmarkCollections(localId: String, collectionLocalIds: List): Boolean { + suspend fun replaceBookmarkCollections(id: String, collectionIds: List): Boolean { return mutatingCall("Failed to replace bookmark collection memberships", triggerAfter = false) { - val changed = bookmarksRepository.replaceBookmarkCollections(localId, collectionLocalIds) + val changed = bookmarksRepository.replaceBookmarkCollections(id, collectionIds) if (changed) { triggerSync() } @@ -433,12 +433,12 @@ class QuranDataService internal constructor( */ @NativeCoroutines suspend fun replaceBookmarkCollections( - localId: String, - collectionLocalIds: List, + id: String, + collectionIds: List, timestamp: PlatformDateTime ): Boolean { return mutatingCall("Failed to replace bookmark collection memberships", triggerAfter = false) { - val changed = bookmarksRepository.replaceBookmarkCollections(localId, collectionLocalIds, timestamp) + val changed = bookmarksRepository.replaceBookmarkCollections(id, collectionIds, timestamp) if (changed) { triggerSync() } @@ -450,10 +450,10 @@ class QuranDataService internal constructor( suspend fun replaceAyahBookmarkCollections( sura: Int, ayah: Int, - collectionLocalIds: List + collectionIds: List ): AyahBookmark { return mutatingCall("Failed to replace ayah bookmark collection memberships", triggerAfter = false) { - val result = bookmarksRepository.replaceAyahBookmarkCollections(sura, ayah, collectionLocalIds) + val result = bookmarksRepository.replaceAyahBookmarkCollections(sura, ayah, collectionIds) if (result.changed) { triggerSync() } @@ -469,11 +469,11 @@ class QuranDataService internal constructor( suspend fun replaceAyahBookmarkCollections( sura: Int, ayah: Int, - collectionLocalIds: List, + collectionIds: List, timestamp: PlatformDateTime ): AyahBookmark { return mutatingCall("Failed to replace ayah bookmark collection memberships", triggerAfter = false) { - val result = bookmarksRepository.replaceAyahBookmarkCollections(sura, ayah, collectionLocalIds, timestamp) + val result = bookmarksRepository.replaceAyahBookmarkCollections(sura, ayah, collectionIds, timestamp) if (result.changed) { triggerSync() } @@ -534,21 +534,21 @@ class QuranDataService internal constructor( } @NativeCoroutines - suspend fun updateReadingSession(localId: String, sura: Int, ayah: Int): ReadingSession { + suspend fun updateReadingSession(id: String, sura: Int, ayah: Int): ReadingSession { return mutatingCall("Failed to update reading session") { - readingSessionsRepository.updateReadingSession(localId, sura, ayah) + readingSessionsRepository.updateReadingSession(id, sura, ayah) } } @NativeCoroutines suspend fun updateReadingSession( - localId: String, + id: String, sura: Int, ayah: Int, timestamp: PlatformDateTime ): ReadingSession { return mutatingCall("Failed to update reading session") { - readingSessionsRepository.updateReadingSession(localId, sura, ayah, timestamp) + readingSessionsRepository.updateReadingSession(id, sura, ayah, timestamp) } } @@ -589,9 +589,9 @@ class QuranDataService internal constructor( } @NativeCoroutines - suspend fun deleteBookmark(localId: String): Boolean { + suspend fun deleteBookmark(id: String): Boolean { return deleteBookmarkResult("Failed to delete bookmark") { - bookmarksRepository.deleteBookmark(localId) + bookmarksRepository.deleteBookmark(id) } } @@ -632,43 +632,43 @@ class QuranDataService internal constructor( /** * Updates a collection name and schedules sync for the local mutation. * - * @param localId the local identifier of the collection to update. + * @param id the mobile-sync identifier of the collection to update. * @param name the new collection name. * @return the updated collection. - * Returns the unchanged virtual default collection when [localId] is [DEFAULT_COLLECTION_ID]. - * @throws IllegalArgumentException when [localId] does not identify an active collection. + * Returns the unchanged virtual default collection when [id] is [DEFAULT_COLLECTION_ID]. + * @throws IllegalArgumentException when [id] does not identify an active collection. */ @NativeCoroutines - suspend fun updateCollection(localId: String, name: String): Collection { - if (localId == DEFAULT_COLLECTION_ID) { + suspend fun updateCollection(id: String, name: String): Collection { + if (id == DEFAULT_COLLECTION_ID) { return mutatingCall("Failed to update collection", triggerAfter = false) { defaultCollection(collectionBookmarksRepository.getBookmarksForCollection(DEFAULT_COLLECTION_ID)) } } return mutatingCall("Failed to update collection") { - collectionsRepository.updateCollection(localId, name) + collectionsRepository.updateCollection(id, name) } } /** * Updates a collection name with an explicit mutation timestamp and schedules sync. * - * @param localId the local identifier of the collection to update. + * @param id the mobile-sync identifier of the collection to update. * @param name the new collection name. * @param timestamp the timestamp to persist for the mutation. * @return the updated collection. - * Returns the unchanged virtual default collection when [localId] is [DEFAULT_COLLECTION_ID]. - * @throws IllegalArgumentException when [localId] does not identify an active collection. + * Returns the unchanged virtual default collection when [id] is [DEFAULT_COLLECTION_ID]. + * @throws IllegalArgumentException when [id] does not identify an active collection. */ @NativeCoroutines - suspend fun updateCollection(localId: String, name: String, timestamp: PlatformDateTime): Collection { - if (localId == DEFAULT_COLLECTION_ID) { + suspend fun updateCollection(id: String, name: String, timestamp: PlatformDateTime): Collection { + if (id == DEFAULT_COLLECTION_ID) { return mutatingCall("Failed to update collection", triggerAfter = false) { defaultCollection(collectionBookmarksRepository.getBookmarksForCollection(DEFAULT_COLLECTION_ID)) } } return mutatingCall("Failed to update collection") { - collectionsRepository.updateCollection(localId, name, timestamp) + collectionsRepository.updateCollection(id, name, timestamp) } } @@ -679,14 +679,14 @@ class QuranDataService internal constructor( * `false` without touching persistence or scheduling sync. */ @NativeCoroutines - suspend fun deleteCollection(localId: String): Boolean { - if (localId == DEFAULT_COLLECTION_ID) { + suspend fun deleteCollection(id: String): Boolean { + if (id == DEFAULT_COLLECTION_ID) { return mutatingCall("Failed to delete collection", triggerAfter = false) { false } } return mutatingCall("Failed to delete collection", triggerAfter = false) { - val deleted = collectionsRepository.deleteCollection(localId) + val deleted = collectionsRepository.deleteCollection(id) if (deleted) { triggerSync() } @@ -695,50 +695,50 @@ class QuranDataService internal constructor( } @NativeCoroutines - suspend fun addBookmarkToCollection(collectionLocalId: String, bookmark: AyahBookmark) { + suspend fun addBookmarkToCollection(collectionId: String, bookmark: AyahBookmark) { mutatingCall("Failed to add bookmark to collection") { - collectionBookmarksRepository.addBookmarkToCollection(collectionLocalId, bookmark) + collectionBookmarksRepository.addBookmarkToCollection(collectionId, bookmark) } } @NativeCoroutines suspend fun addBookmarkToCollection( - collectionLocalId: String, + collectionId: String, bookmark: AyahBookmark, timestamp: PlatformDateTime ) { mutatingCall("Failed to add bookmark to collection") { - collectionBookmarksRepository.addBookmarkToCollection(collectionLocalId, bookmark, timestamp) + collectionBookmarksRepository.addBookmarkToCollection(collectionId, bookmark, timestamp) } } @NativeCoroutines suspend fun addAyahBookmarkToCollection( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int ): CollectionAyahBookmark { return mutatingCall("Failed to add ayah bookmark to collection") { - collectionBookmarksRepository.addAyahBookmarkToCollection(collectionLocalId, sura, ayah) + collectionBookmarksRepository.addAyahBookmarkToCollection(collectionId, sura, ayah) } } @NativeCoroutines suspend fun addAyahBookmarkToCollection( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int, timestamp: PlatformDateTime ): CollectionAyahBookmark { return mutatingCall("Failed to add ayah bookmark to collection") { - collectionBookmarksRepository.addAyahBookmarkToCollection(collectionLocalId, sura, ayah, timestamp) + collectionBookmarksRepository.addAyahBookmarkToCollection(collectionId, sura, ayah, timestamp) } } @NativeCoroutines - suspend fun removeBookmarkFromCollection(collectionLocalId: String, bookmark: AyahBookmark) { + suspend fun removeBookmarkFromCollection(collectionId: String, bookmark: AyahBookmark) { mutatingCall("Failed to remove bookmark from collection") { - collectionBookmarksRepository.removeBookmarkFromCollection(collectionLocalId, bookmark) + collectionBookmarksRepository.removeBookmarkFromCollection(collectionId, bookmark) } } @@ -773,7 +773,7 @@ class QuranDataService internal constructor( /** * Updates an existing note and schedules sync for the local mutation. * - * @param localId the local identifier of the note to update. + * @param id the mobile-sync identifier of the note to update. * @param body the updated note body. * @param startSura the first sura covered by the note. * @param startAyah the first ayah covered by the note. @@ -782,7 +782,7 @@ class QuranDataService internal constructor( */ @NativeCoroutines suspend fun updateNote( - localId: String, + id: String, body: String, startSura: Int, startAyah: Int, @@ -790,26 +790,26 @@ class QuranDataService internal constructor( endAyah: Int ) { mutatingCall("Failed to update note") { - notesRepository.updateNote(localId, body, startSura, startAyah, endSura, endAyah) + notesRepository.updateNote(id, body, startSura, startAyah, endSura, endAyah) } } @NativeCoroutines - suspend fun deleteNote(localId: String) { + suspend fun deleteNote(id: String) { mutatingCall("Failed to delete note") { - notesRepository.deleteNote(localId) + notesRepository.deleteNote(id) } } @NativeCoroutines - fun getBookmarksForCollectionFlow(collectionLocalId: String): Flow> = - collectionBookmarksRepository.getBookmarksForCollectionFlow(collectionLocalId) + fun getBookmarksForCollectionFlow(collectionId: String): Flow> = + collectionBookmarksRepository.getBookmarksForCollectionFlow(collectionId) private fun defaultCollection(bookmarks: List = emptyList()): Collection = Collection( name = DEFAULT_COLLECTION_NAME, - lastUpdated = bookmarks.firstOrNull()?.lastUpdated ?: emptyDefaultCollectionTimestamp, - localId = DEFAULT_COLLECTION_ID + lastUpdated = bookmarks.firstOrNull()?.bookmarkLastUpdated ?: emptyDefaultCollectionTimestamp, + id = DEFAULT_COLLECTION_ID ) } diff --git a/sync-pipelines/src/commonMain/kotlin/com/quran/shared/pipeline/SyncEnginePipeline.kt b/sync-pipelines/src/commonMain/kotlin/com/quran/shared/pipeline/SyncEnginePipeline.kt index 11454560..4ea95b18 100644 --- a/sync-pipelines/src/commonMain/kotlin/com/quran/shared/pipeline/SyncEnginePipeline.kt +++ b/sync-pipelines/src/commonMain/kotlin/com/quran/shared/pipeline/SyncEnginePipeline.kt @@ -15,7 +15,6 @@ import com.quran.shared.persistence.input.RemoteCollection import com.quran.shared.persistence.input.RemoteCollectionBookmark import com.quran.shared.persistence.input.RemoteNote import com.quran.shared.persistence.input.RemoteReadingSession -import com.quran.shared.persistence.model.CollectionAyahBookmark import com.quran.shared.persistence.model.ReadingSession import com.quran.shared.persistence.repository.PersistenceWriteBoundaryGuard import com.quran.shared.persistence.repository.bookmark.repository.BookmarksSynchronizationRepository @@ -562,17 +561,6 @@ private fun SyncCollection.toRemoteInput(): RemoteCollection { ) } -private fun CollectionAyahBookmark.toSyncEngine(): SyncCollectionBookmark { - val collectionId = requireNotNull(collectionRemoteId) { "Collection remote ID is required for sync." } - return SyncCollectionBookmark.AyahBookmark( - collectionId = collectionId, - sura = sura, - ayah = ayah, - lastModified = lastUpdated.fromPlatform(), - bookmarkId = bookmarkRemoteId - ) -} - private fun LocalSyncCollectionAyahBookmark.toSyncEngine(): SyncCollectionBookmark { val collectionId = requireNotNull(collectionRemoteId) { "Collection remote ID is required for sync." } return SyncCollectionBookmark.AyahBookmark( @@ -706,7 +694,7 @@ private fun ayahIdToSuraAyah(ayahId: Long): NoteAyah? { private fun ReadingSession.toSyncEngine(): SyncReadingSession { return SyncReadingSession( - id = localId, + id = id, chapterNumber = sura, verseNumber = ayah, lastModified = lastUpdated.fromPlatform() diff --git a/sync-pipelines/src/commonTest/kotlin/com/quran/shared/pipeline/QuranDataServiceLifecycleTest.kt b/sync-pipelines/src/commonTest/kotlin/com/quran/shared/pipeline/QuranDataServiceLifecycleTest.kt index 1ef3cf9c..f00513be 100644 --- a/sync-pipelines/src/commonTest/kotlin/com/quran/shared/pipeline/QuranDataServiceLifecycleTest.kt +++ b/sync-pipelines/src/commonTest/kotlin/com/quran/shared/pipeline/QuranDataServiceLifecycleTest.kt @@ -569,7 +569,7 @@ class QuranDataServiceLifecycleTest { val fixture = quranDataServiceFixture(useRecordingSyncClient = true) val defaultBookmark = bookmarkLink( DEFAULT_COLLECTION_ID, - lastUpdated = Instant.fromEpochMilliseconds(77).toPlatform() + bookmarkLastUpdated = Instant.fromEpochMilliseconds(77).toPlatform() ) fixture.collectionBookmarksRepository.setBookmarksForCollection( DEFAULT_COLLECTION_ID, @@ -579,9 +579,9 @@ class QuranDataServiceLifecycleTest { val result = fixture.service.updateCollection(DEFAULT_COLLECTION_ID, "Renamed default") - assertEquals(DEFAULT_COLLECTION_ID, result.localId) + assertEquals(DEFAULT_COLLECTION_ID, result.id) assertEquals(true, result.isDefault) - assertEquals(defaultBookmark.lastUpdated, result.lastUpdated) + assertEquals(defaultBookmark.bookmarkLastUpdated, result.lastUpdated) assertEquals(emptyList(), fixture.collectionsRepository.updateCalls) assertEquals(0, fixture.syncClient.localDataUpdatedCount) fixture.clearAndJoin() @@ -594,7 +594,7 @@ class QuranDataServiceLifecycleTest { val timestamp = Instant.fromEpochMilliseconds(42).toPlatform() val defaultBookmark = bookmarkLink( DEFAULT_COLLECTION_ID, - lastUpdated = Instant.fromEpochMilliseconds(88).toPlatform() + bookmarkLastUpdated = Instant.fromEpochMilliseconds(88).toPlatform() ) fixture.collectionBookmarksRepository.setBookmarksForCollection( DEFAULT_COLLECTION_ID, @@ -604,9 +604,9 @@ class QuranDataServiceLifecycleTest { val result = fixture.service.updateCollection(DEFAULT_COLLECTION_ID, "Renamed default", timestamp) - assertEquals(DEFAULT_COLLECTION_ID, result.localId) + assertEquals(DEFAULT_COLLECTION_ID, result.id) assertEquals(true, result.isDefault) - assertEquals(defaultBookmark.lastUpdated, result.lastUpdated) + assertEquals(defaultBookmark.bookmarkLastUpdated, result.lastUpdated) assertEquals(emptyList(), fixture.collectionsRepository.updateCalls) assertEquals(0, fixture.syncClient.localDataUpdatedCount) fixture.clearAndJoin() @@ -636,7 +636,7 @@ class QuranDataServiceLifecycleTest { fun `collectionsWithBookmarks includes virtual default collection with default bookmarks`() = runTest(dispatcher) { val fixture = quranDataServiceFixture() - val defaultBookmark = bookmarkLink(DEFAULT_COLLECTION_ID, bookmarkLocalId = "default-bookmark") + val defaultBookmark = bookmarkLink(DEFAULT_COLLECTION_ID, bookmarkId = "default-bookmark") fixture.collectionBookmarksRepository.setBookmarksForCollection( DEFAULT_COLLECTION_ID, listOf(defaultBookmark) @@ -645,7 +645,7 @@ class QuranDataServiceLifecycleTest { val result = fixture.service.collectionsWithBookmarks.first() - assertEquals(listOf(DEFAULT_COLLECTION_ID), result.map { it.collection.localId }) + assertEquals(listOf(DEFAULT_COLLECTION_ID), result.map { it.collection.id }) assertEquals(true, result.single().collection.isDefault) assertEquals(listOf(defaultBookmark), result.single().bookmarks) assertEquals(listOf(DEFAULT_COLLECTION_ID), fixture.collectionBookmarksRepository.flowRequests) @@ -657,8 +657,8 @@ class QuranDataServiceLifecycleTest { runTest(dispatcher) { val fixture = quranDataServiceFixture() val customCollection = Collection("Favorites", testTimestamp(), "7") - val defaultBookmark = bookmarkLink(DEFAULT_COLLECTION_ID, bookmarkLocalId = "default-bookmark") - val customBookmark = bookmarkLink("7", bookmarkLocalId = "custom-bookmark") + val defaultBookmark = bookmarkLink(DEFAULT_COLLECTION_ID, bookmarkId = "default-bookmark") + val customBookmark = bookmarkLink("7", bookmarkId = "custom-bookmark") fixture.collectionsRepository.collections.value = listOf(customCollection) fixture.collectionBookmarksRepository.setBookmarksForCollection( DEFAULT_COLLECTION_ID, @@ -669,9 +669,9 @@ class QuranDataServiceLifecycleTest { val result = fixture.service.collectionsWithBookmarks.first() - assertEquals(listOf(DEFAULT_COLLECTION_ID, "7"), result.map { it.collection.localId }) + assertEquals(listOf(DEFAULT_COLLECTION_ID, "7"), result.map { it.collection.id }) assertEquals(listOf(defaultBookmark), result.first { it.collection.isDefault }.bookmarks) - assertEquals(listOf(customBookmark), result.first { it.collection.localId == "7" }.bookmarks) + assertEquals(listOf(customBookmark), result.first { it.collection.id == "7" }.bookmarks) fixture.clearAndJoin() } @@ -741,7 +741,7 @@ class QuranDataServiceLifecycleTest { assertFalse(result) assertEquals( - listOf(BookmarkLocalIdDeleteCall("bookmark-local-id")), + listOf(BookmarkIdDeleteCall("bookmark-local-id")), fixture.bookmarksRepository.deleteCalls ) assertEquals(0, fixture.syncClient.localDataUpdatedCount) @@ -852,8 +852,8 @@ class QuranDataServiceLifecycleTest { advanceUntilIdle() val result = fixture.service.replaceBookmarkCollections( - localId = "bookmark-local-id", - collectionLocalIds = listOf("collection-a"), + id = "bookmark-local-id", + collectionIds = listOf("collection-a"), timestamp = timestamp ) @@ -879,7 +879,7 @@ class QuranDataServiceLifecycleTest { AyahBookmark( sura = 2, ayah = 255, - localId = "bookmark-replaced", + id = "bookmark-replaced", lastUpdated = testTimestamp() ), result @@ -905,7 +905,7 @@ class QuranDataServiceLifecycleTest { AyahBookmark( sura = 2, ayah = 255, - localId = "bookmark-replaced", + id = "bookmark-replaced", lastUpdated = testTimestamp() ), result @@ -925,7 +925,7 @@ class QuranDataServiceLifecycleTest { val result = fixture.service.replaceAyahBookmarkCollections( sura = 2, ayah = 255, - collectionLocalIds = listOf("collection-a"), + collectionIds = listOf("collection-a"), timestamp = timestamp ) @@ -933,7 +933,7 @@ class QuranDataServiceLifecycleTest { AyahBookmark( sura = 2, ayah = 255, - localId = "bookmark-replaced", + id = "bookmark-replaced", lastUpdated = timestamp ), result @@ -1311,51 +1311,51 @@ private class ServiceBookmarksRepository : BookmarksRepository, BookmarksSynchro AyahBookmark( sura = sura, ayah = ayah, - localId = "bookmark-${++addCount}", + id = "bookmark-${++addCount}", lastUpdated = testTimestamp() ) override suspend fun addBookmark(sura: Int, ayah: Int, timestamp: com.quran.shared.persistence.util.PlatformDateTime): AyahBookmark = addBookmark(sura, ayah) - override suspend fun addBookmark(sura: Int, ayah: Int, collectionLocalIds: List): AyahBookmark = + override suspend fun addBookmark(sura: Int, ayah: Int, collectionIds: List): AyahBookmark = addBookmark(sura, ayah) override suspend fun addBookmark( sura: Int, ayah: Int, - collectionLocalIds: List, + collectionIds: List, timestamp: com.quran.shared.persistence.util.PlatformDateTime ): AyahBookmark = addBookmark(sura, ayah) override suspend fun replaceBookmarkCollections( - localId: String, - collectionLocalIds: List + id: String, + collectionIds: List ): Boolean { - replaceCalls += BookmarkCollectionsReplaceCall(localId, collectionLocalIds) + replaceCalls += BookmarkCollectionsReplaceCall(id, collectionIds) return replaceResult } override suspend fun replaceBookmarkCollections( - localId: String, - collectionLocalIds: List, + id: String, + collectionIds: List, timestamp: com.quran.shared.persistence.util.PlatformDateTime ): Boolean { - replaceCalls += BookmarkCollectionsReplaceCall(localId, collectionLocalIds, timestamp) + replaceCalls += BookmarkCollectionsReplaceCall(id, collectionIds, timestamp) return replaceResult } override suspend fun replaceAyahBookmarkCollections( sura: Int, ayah: Int, - collectionLocalIds: List + collectionIds: List ): BookmarkCollectionsReplacementResult { - replaceAyahCalls += BookmarkAyahCollectionsReplaceCall(sura, ayah, collectionLocalIds) + replaceAyahCalls += BookmarkAyahCollectionsReplaceCall(sura, ayah, collectionIds) return BookmarkCollectionsReplacementResult( bookmark = AyahBookmark( sura = sura, ayah = ayah, - localId = "bookmark-replaced", + id = "bookmark-replaced", lastUpdated = testTimestamp() ), changed = replaceAyahResultChanged @@ -1365,15 +1365,15 @@ private class ServiceBookmarksRepository : BookmarksRepository, BookmarksSynchro override suspend fun replaceAyahBookmarkCollections( sura: Int, ayah: Int, - collectionLocalIds: List, + collectionIds: List, timestamp: com.quran.shared.persistence.util.PlatformDateTime ): BookmarkCollectionsReplacementResult { - replaceAyahCalls += BookmarkAyahCollectionsReplaceCall(sura, ayah, collectionLocalIds, timestamp) + replaceAyahCalls += BookmarkAyahCollectionsReplaceCall(sura, ayah, collectionIds, timestamp) return BookmarkCollectionsReplacementResult( bookmark = AyahBookmark( sura = sura, ayah = ayah, - localId = "bookmark-replaced", + id = "bookmark-replaced", lastUpdated = timestamp ), changed = replaceAyahResultChanged @@ -1388,8 +1388,8 @@ private class ServiceBookmarksRepository : BookmarksRepository, BookmarksSynchro deleteCalls += BookmarkModelDeleteCall(bookmark) return deleteResult } - override suspend fun deleteBookmark(localId: String): Boolean { - deleteCalls += BookmarkLocalIdDeleteCall(localId) + override suspend fun deleteBookmark(id: String): Boolean { + deleteCalls += BookmarkIdDeleteCall(id) return deleteResult } override suspend fun fetchMutatedBookmarks(): List> = emptyList() @@ -1406,15 +1406,15 @@ private class ServiceBookmarksRepository : BookmarksRepository, BookmarksSynchro } private data class BookmarkCollectionsReplaceCall( - val localId: String, - val collectionLocalIds: List, + val id: String, + val collectionIds: List, val timestamp: PlatformDateTime? = null ) private data class BookmarkAyahCollectionsReplaceCall( val sura: Int, val ayah: Int, - val collectionLocalIds: List, + val collectionIds: List, val timestamp: PlatformDateTime? = null ) @@ -1425,8 +1425,8 @@ private data class BookmarkAyahDeleteCall( val ayah: Int ) : BookmarkDeleteCall -private data class BookmarkLocalIdDeleteCall( - val localId: String +private data class BookmarkIdDeleteCall( + val id: String ) : BookmarkDeleteCall private data class BookmarkModelDeleteCall( @@ -1453,7 +1453,7 @@ private class ServiceReadingBookmarksRepository : ReadingBookmarksRepository { } private data class CollectionUpdateCall( - val localId: String, + val id: String, val name: String, val timestamp: PlatformDateTime? ) @@ -1464,7 +1464,7 @@ private data class CollectionAddCall( ) private data class CollectionDeleteCall( - val localId: String + val id: String ) private class ServiceCollectionsRepository : CollectionsRepository, CollectionsSynchronizationRepository { @@ -1486,20 +1486,20 @@ private class ServiceCollectionsRepository : CollectionsRepository, CollectionsS addCalls += CollectionAddCall(name, timestamp) return Collection(name, timestamp, "collection-${addCalls.size}") } - override suspend fun updateCollection(localId: String, name: String): Collection { - updateCalls += CollectionUpdateCall(localId, name, null) - return Collection(name, testTimestamp(), localId) + override suspend fun updateCollection(id: String, name: String): Collection { + updateCalls += CollectionUpdateCall(id, name, null) + return Collection(name, testTimestamp(), id) } override suspend fun updateCollection( - localId: String, + id: String, name: String, timestamp: com.quran.shared.persistence.util.PlatformDateTime ): Collection { - updateCalls += CollectionUpdateCall(localId, name, timestamp) - return Collection(name, timestamp, localId) + updateCalls += CollectionUpdateCall(id, name, timestamp) + return Collection(name, timestamp, id) } - override suspend fun deleteCollection(localId: String): Boolean { - deleteCalls += CollectionDeleteCall(localId) + override suspend fun deleteCollection(id: String): Boolean { + deleteCalls += CollectionDeleteCall(id) return deleteResult } override fun getCollectionsFlow(): Flow> = collections @@ -1519,37 +1519,37 @@ private class ServiceCollectionBookmarksRepository : val flowRequests = mutableListOf() private val bookmarksByCollectionId = mutableMapOf>>() - fun setBookmarksForCollection(collectionLocalId: String, bookmarks: List) { - bookmarksFlow(collectionLocalId).value = bookmarks + fun setBookmarksForCollection(collectionId: String, bookmarks: List) { + bookmarksFlow(collectionId).value = bookmarks } - override suspend fun getBookmarksForCollection(collectionLocalId: String): List = - bookmarksFlow(collectionLocalId).value + override suspend fun getBookmarksForCollection(collectionId: String): List = + bookmarksFlow(collectionId).value override suspend fun addBookmarkToCollection( - collectionLocalId: String, + collectionId: String, bookmark: AyahBookmark - ): CollectionAyahBookmark = bookmarkLink(collectionLocalId) + ): CollectionAyahBookmark = bookmarkLink(collectionId) override suspend fun addBookmarkToCollection( - collectionLocalId: String, + collectionId: String, bookmark: AyahBookmark, timestamp: com.quran.shared.persistence.util.PlatformDateTime - ): CollectionAyahBookmark = bookmarkLink(collectionLocalId) + ): CollectionAyahBookmark = bookmarkLink(collectionId) override suspend fun addAyahBookmarkToCollection( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int - ): CollectionAyahBookmark = bookmarkLink(collectionLocalId) + ): CollectionAyahBookmark = bookmarkLink(collectionId) override suspend fun addAyahBookmarkToCollection( - collectionLocalId: String, + collectionId: String, sura: Int, ayah: Int, timestamp: com.quran.shared.persistence.util.PlatformDateTime - ): CollectionAyahBookmark = bookmarkLink(collectionLocalId) - override suspend fun removeBookmarkFromCollection(collectionLocalId: String, bookmark: AyahBookmark): Boolean = true + ): CollectionAyahBookmark = bookmarkLink(collectionId) + override suspend fun removeBookmarkFromCollection(collectionId: String, bookmark: AyahBookmark): Boolean = true override suspend fun removeAyahBookmarkFromCollection(collectionAyahBookmark: CollectionAyahBookmark): Boolean = true - override fun getBookmarksForCollectionFlow(collectionLocalId: String): Flow> { - flowRequests += collectionLocalId - return bookmarksFlow(collectionLocalId) + override fun getBookmarksForCollectionFlow(collectionId: String): Flow> { + flowRequests += collectionId + return bookmarksFlow(collectionId) } override suspend fun fetchMutatedCollectionBookmarks(): List> = emptyList() @@ -1563,10 +1563,10 @@ private class ServiceCollectionBookmarksRepository : ) = writeBoundaryGuard.checkWriteBoundary() override suspend fun remoteResourcesExist(remoteIDs: List): Map = remoteIDs.associateWith { false } - override suspend fun fetchCollectionBookmarkByRemoteId(remoteId: String): CollectionAyahBookmark? = null + override suspend fun fetchCollectionBookmarkByRemoteId(remoteId: String): LocalSyncCollectionAyahBookmark? = null - private fun bookmarksFlow(collectionLocalId: String): MutableStateFlow> = - bookmarksByCollectionId.getOrPut(collectionLocalId) { MutableStateFlow(emptyList()) } + private fun bookmarksFlow(collectionId: String): MutableStateFlow> = + bookmarksByCollectionId.getOrPut(collectionId) { MutableStateFlow(emptyList()) } } private class ServiceNotesRepository : NotesRepository, NotesSynchronizationRepository { @@ -1582,7 +1582,7 @@ private class ServiceNotesRepository : NotesRepository, NotesSynchronizationRepo timestamp: com.quran.shared.persistence.util.PlatformDateTime ): Note = addNote(body, startSura, startAyah, endSura, endAyah) override suspend fun updateNote( - localId: String, + id: String, body: String, startSura: Int, startAyah: Int, @@ -1590,7 +1590,7 @@ private class ServiceNotesRepository : NotesRepository, NotesSynchronizationRepo endAyah: Int ): Note = addNote(body, startSura, startAyah, endSura, endAyah) override suspend fun updateNote( - localId: String, + id: String, body: String, startSura: Int, startAyah: Int, @@ -1598,7 +1598,7 @@ private class ServiceNotesRepository : NotesRepository, NotesSynchronizationRepo endAyah: Int, timestamp: com.quran.shared.persistence.util.PlatformDateTime ): Note = addNote(body, startSura, startAyah, endSura, endAyah) - override suspend fun deleteNote(localId: String): Boolean = true + override suspend fun deleteNote(id: String): Boolean = true override fun getNotesFlow(): Flow> = MutableStateFlow(emptyList()) override suspend fun fetchMutatedNotes(lastModified: Long): List> = emptyList() override suspend fun applyRemoteChanges( @@ -1627,10 +1627,10 @@ private class ServiceReadingSessionsRepository : ReadingSessionsRepository, Read ayah: Int, timestamp: com.quran.shared.persistence.util.PlatformDateTime ): ReadingSession = addReadingSession(sura, ayah) - override suspend fun updateReadingSession(localId: String, sura: Int, ayah: Int): ReadingSession = + override suspend fun updateReadingSession(id: String, sura: Int, ayah: Int): ReadingSession = addReadingSession(sura, ayah) override suspend fun updateReadingSession( - localId: String, + id: String, sura: Int, ayah: Int, timestamp: com.quran.shared.persistence.util.PlatformDateTime @@ -1657,19 +1657,18 @@ private class ServiceReadingSessionsRepository : ReadingSessionsRepository, Read } private fun bookmarkLink( - collectionLocalId: String, - bookmarkLocalId: String = "bookmark", - lastUpdated: PlatformDateTime = testTimestamp() + collectionId: String, + bookmarkId: String = "bookmark", + bookmarkLastUpdated: PlatformDateTime = testTimestamp(), + bookmarkAddedDate: PlatformDateTime = bookmarkLastUpdated ): CollectionAyahBookmark = CollectionAyahBookmark( - collectionLocalId = collectionLocalId, - collectionRemoteId = null, - bookmarkLocalId = bookmarkLocalId, - bookmarkRemoteId = null, + collectionId = collectionId, + bookmarkId = bookmarkId, sura = 2, ayah = 255, - lastUpdated = lastUpdated, - localId = "collection-bookmark" + bookmarkLastUpdated = bookmarkLastUpdated, + bookmarkAddedDate = bookmarkAddedDate ) private fun testTimestamp(): com.quran.shared.persistence.util.PlatformDateTime =