diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/DatabaseTypes.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/DatabaseTypes.kt index a89583a..cd7168d 100644 --- a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/DatabaseTypes.kt +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/DatabaseTypes.kt @@ -5,9 +5,11 @@ import com.quran.shared.persistence.Bookmark_collection import com.quran.shared.persistence.Collection import com.quran.shared.persistence.Note import com.quran.shared.persistence.Page_bookmark +import com.quran.shared.persistence.Recent_page internal typealias DatabasePageBookmark = Page_bookmark internal typealias DatabaseAyahBookmark = Ayah_bookmark internal typealias DatabaseCollection = Collection internal typealias DatabaseBookmarkCollection = Bookmark_collection internal typealias DatabaseNote = Note +internal typealias DatabaseRecentPage = Recent_page diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/RecentPage.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/RecentPage.kt new file mode 100644 index 0000000..979b897 --- /dev/null +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/model/RecentPage.kt @@ -0,0 +1,9 @@ +package com.quran.shared.persistence.model + +import com.quran.shared.persistence.util.PlatformDateTime + +data class RecentPage( + val page: Int, + val lastUpdated: PlatformDateTime, + val localId: String +) diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/RecentPagesRepositoryFactory.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/RecentPagesRepositoryFactory.kt new file mode 100644 index 0000000..e5c95cd --- /dev/null +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/RecentPagesRepositoryFactory.kt @@ -0,0 +1,25 @@ +package com.quran.shared.persistence.repository.recentpage + +import com.quran.shared.persistence.DriverFactory +import com.quran.shared.persistence.makeDatabase +import com.quran.shared.persistence.repository.recentpage.repository.RecentPagesRepository +import com.quran.shared.persistence.repository.recentpage.repository.RecentPagesRepositoryImpl + +/** + * Factory for creating RecentPagesRepository instances. + * This factory hides the details of database creation and provides a clean interface + * for obtaining repository instances. + */ +object RecentPagesRepositoryFactory { + /** + * Creates a new instance of RecentPagesRepository. + * The repository is backed by a SQLite database created using the provided driver factory. + * + * @param driverFactory The driver factory to use for database creation + * @return RecentPagesRepository A new repository instance + */ + fun createRepository(driverFactory: DriverFactory): RecentPagesRepository { + val database = makeDatabase(driverFactory) + return RecentPagesRepositoryImpl(database) + } +} diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/extension/RecentPagesQueriesExtensions.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/extension/RecentPagesQueriesExtensions.kt new file mode 100644 index 0000000..fe9d385 --- /dev/null +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/extension/RecentPagesQueriesExtensions.kt @@ -0,0 +1,17 @@ +@file:OptIn(ExperimentalTime::class) + +package com.quran.shared.persistence.repository.recentpage.extension + +import com.quran.shared.persistence.model.DatabaseRecentPage +import com.quran.shared.persistence.model.RecentPage +import com.quran.shared.persistence.util.toPlatform +import kotlin.time.ExperimentalTime +import kotlin.time.Instant + +internal fun DatabaseRecentPage.toRecentPage(): RecentPage { + return RecentPage( + page = page.toInt(), + lastUpdated = Instant.fromEpochMilliseconds(modified_at).toPlatform(), + localId = local_id.toString() + ) +} diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/repository/RecentPagesRepository.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/repository/RecentPagesRepository.kt new file mode 100644 index 0000000..f55188a --- /dev/null +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/repository/RecentPagesRepository.kt @@ -0,0 +1,30 @@ +package com.quran.shared.persistence.repository.recentpage.repository + +import com.quran.shared.persistence.model.RecentPage +import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + +interface RecentPagesRepository { + /** + * Fetch and returns all recent pages. + * + * @return List the current list of recent pages + */ + @NativeCoroutines + suspend fun getRecentPages(): List + + /** + * Add a page to the recent pages list. + * + * @return the [RecentPage] + */ + @NativeCoroutines + suspend fun addRecentPage(page: Int): RecentPage + + /** + * Delete a page from the recent pages list. + * + * @return a boolean denoting success + */ + @NativeCoroutines + suspend fun deleteRecentPage(page: Int): Boolean +} diff --git a/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/repository/RecentPagesRepositoryImpl.kt b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/repository/RecentPagesRepositoryImpl.kt new file mode 100644 index 0000000..733d8cb --- /dev/null +++ b/persistence/src/commonMain/kotlin/com/quran/shared/persistence/repository/recentpage/repository/RecentPagesRepositoryImpl.kt @@ -0,0 +1,44 @@ +package com.quran.shared.persistence.repository.recentpage.repository + +import co.touchlab.kermit.Logger +import com.quran.shared.persistence.QuranDatabase +import com.quran.shared.persistence.model.RecentPage +import com.quran.shared.persistence.repository.recentpage.extension.toRecentPage +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.withContext + +class RecentPagesRepositoryImpl( + private val database: QuranDatabase +) : RecentPagesRepository { + + private val logger = Logger.withTag("RecentPagesRepository") + private val recentPagesQueries = lazy { database.recent_pagesQueries } + + override suspend fun getRecentPages(): List { + return withContext(Dispatchers.IO) { + recentPagesQueries.value.getRecentPages() + .executeAsList() + .map { it.toRecentPage() } + } + } + + override suspend fun addRecentPage(page: Int): RecentPage { + logger.i { "Adding recent page $page" } + return withContext(Dispatchers.IO) { + recentPagesQueries.value.addRecentPage(page.toLong()) + val record = recentPagesQueries.value.getRecentPageForPage(page.toLong()) + .executeAsOneOrNull() + requireNotNull(record) { "Expected recent page for page $page after insert." } + record.toRecentPage() + } + } + + override suspend fun deleteRecentPage(page: Int): Boolean { + logger.i { "Deleting recent page for page $page" } + withContext(Dispatchers.IO) { + recentPagesQueries.value.deleteRecentPage(page.toLong()) + } + return true + } +} diff --git a/persistence/src/commonMain/sqldelight/com/quran/shared/persistence/recent_pages.sq b/persistence/src/commonMain/sqldelight/com/quran/shared/persistence/recent_pages.sq index 298bf14..ed8385d 100644 --- a/persistence/src/commonMain/sqldelight/com/quran/shared/persistence/recent_pages.sq +++ b/persistence/src/commonMain/sqldelight/com/quran/shared/persistence/recent_pages.sq @@ -2,8 +2,37 @@ CREATE TABLE IF NOT EXISTS recent_page( local_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, remote_id TEXT, page INTEGER NOT NULL UNIQUE, - created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL, - modified_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL, + created_at INTEGER DEFAULT (CAST(strftime('%s', 'now') AS INTEGER) * 1000) NOT NULL, + modified_at INTEGER DEFAULT (CAST(strftime('%s', 'now') AS INTEGER) * 1000) NOT NULL, deleted INTEGER NOT NULL DEFAULT 0, CHECK (deleted IN (0, 1)) -); \ No newline at end of file +); + +CREATE INDEX IF NOT EXISTS recent_page_remote_id_idx ON recent_page(remote_id); + +getRecentPages: + SELECT * FROM recent_page WHERE deleted = 0 ORDER BY modified_at DESC; + +getRecentPageForPage: + SELECT * FROM recent_page WHERE page = ? LIMIT 1; + +getRecentPageByLocalId: + SELECT * FROM recent_page WHERE local_id = ? LIMIT 1; + +addRecentPage { + INSERT OR IGNORE INTO recent_page (remote_id, page, deleted) + VALUES (NULL, :page, 0); + UPDATE recent_page + SET deleted = 0, + created_at = CAST(strftime('%s', 'now') AS INTEGER) * 1000, + modified_at = CAST(strftime('%s', 'now') AS INTEGER) * 1000 + WHERE page = :page; +} + +deleteRecentPage { + DELETE FROM recent_page WHERE page = :page AND remote_id IS NULL; + UPDATE recent_page + SET deleted = 1, + modified_at = CAST(strftime('%s', 'now') AS INTEGER) * 1000 + WHERE page = :page AND remote_id IS NOT NULL; +}