diff --git a/app/src/main/java/yokai/data/manga/MangaRepositoryImpl.kt b/app/src/main/java/yokai/data/manga/MangaRepositoryImpl.kt index c67790ff55..a1efba5a17 100644 --- a/app/src/main/java/yokai/data/manga/MangaRepositoryImpl.kt +++ b/app/src/main/java/yokai/data/manga/MangaRepositoryImpl.kt @@ -30,6 +30,9 @@ class MangaRepositoryImpl(private val handler: DatabaseHandler) : MangaRepositor override fun getMangaListAsFlow(): Flow> = handler.subscribeToList { mangasQueries.findAll(Manga::mapper) } + override fun getMangaByUrlAndSourceAsFlow(url: String, source: Long): Flow = + handler.subscribeToFirstOrNull { mangasQueries.findByUrlAndSource(url, source, Manga::mapper) } + override suspend fun getLibraryManga(): List = handler.awaitList { library_viewQueries.findAll(LibraryManga::mapper) } diff --git a/app/src/main/java/yokai/domain/manga/MangaRepository.kt b/app/src/main/java/yokai/domain/manga/MangaRepository.kt index eda4861a13..c81f8ad478 100644 --- a/app/src/main/java/yokai/domain/manga/MangaRepository.kt +++ b/app/src/main/java/yokai/domain/manga/MangaRepository.kt @@ -9,6 +9,7 @@ import yokai.domain.manga.models.MangaUpdate interface MangaRepository { suspend fun getMangaList(): List suspend fun getMangaByUrlAndSource(url: String, source: Long): Manga? + fun getMangaByUrlAndSourceAsFlow(url: String, source: Long): Flow suspend fun getMangaById(id: Long): Manga? suspend fun getFavorites(): List suspend fun getReadNotFavorites(): List diff --git a/app/src/main/java/yokai/domain/manga/interactor/GetManga.kt b/app/src/main/java/yokai/domain/manga/interactor/GetManga.kt index 588044c9ed..9a89581fda 100644 --- a/app/src/main/java/yokai/domain/manga/interactor/GetManga.kt +++ b/app/src/main/java/yokai/domain/manga/interactor/GetManga.kt @@ -7,6 +7,7 @@ class GetManga ( ) { suspend fun awaitAll() = mangaRepository.getMangaList() fun subscribeAll() = mangaRepository.getMangaListAsFlow() + fun subscribeByUrlAndSource(url: String, source: Long) = mangaRepository.getMangaByUrlAndSourceAsFlow(url, source) suspend fun awaitByUrlAndSource(url: String, source: Long) = mangaRepository.getMangaByUrlAndSource(url, source) suspend fun awaitById(id: Long) = mangaRepository.getMangaById(id) diff --git a/data/src/androidMain/kotlin/yokai/data/AndroidDatabaseHandler.kt b/data/src/androidMain/kotlin/yokai/data/AndroidDatabaseHandler.kt index b0d578fa36..ed8cbe3b1f 100644 --- a/data/src/androidMain/kotlin/yokai/data/AndroidDatabaseHandler.kt +++ b/data/src/androidMain/kotlin/yokai/data/AndroidDatabaseHandler.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.withContext import yokai.data.util.executeAsFirstOrNull +import yokai.data.util.mapToFirstOrNull class AndroidDatabaseHandler( val db: Database, @@ -80,6 +81,10 @@ class AndroidDatabaseHandler( return block(db).asFlow().mapToOneOrNull(queryDispatcher) } + override fun subscribeToFirstOrNull(block: Database.() -> Query): Flow { + return block(db).asFlow().mapToFirstOrNull(queryDispatcher) + } + /* override fun subscribeToPagingSource( countQuery: Database.() -> Query, diff --git a/data/src/commonMain/kotlin/yokai/data/DatabaseHandler.kt b/data/src/commonMain/kotlin/yokai/data/DatabaseHandler.kt index a73171dc29..59d261ed31 100644 --- a/data/src/commonMain/kotlin/yokai/data/DatabaseHandler.kt +++ b/data/src/commonMain/kotlin/yokai/data/DatabaseHandler.kt @@ -43,6 +43,8 @@ interface DatabaseHandler { fun subscribeToOneOrNull(block: Database.() -> Query): Flow + fun subscribeToFirstOrNull(block: Database.() -> Query): Flow + /* fun subscribeToPagingSource( countQuery: Database.() -> Query, diff --git a/data/src/commonMain/kotlin/yokai/data/util/SqlDelightUtil.kt b/data/src/commonMain/kotlin/yokai/data/util/SqlDelightUtil.kt index d469d7402e..b4d2369dc3 100644 --- a/data/src/commonMain/kotlin/yokai/data/util/SqlDelightUtil.kt +++ b/data/src/commonMain/kotlin/yokai/data/util/SqlDelightUtil.kt @@ -1,7 +1,12 @@ package yokai.data.util import app.cash.sqldelight.ExecutableQuery +import app.cash.sqldelight.Query import app.cash.sqldelight.db.QueryResult +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.withContext fun ExecutableQuery.executeAsFirst(): T { return executeAsFirstOrNull() ?: throw NullPointerException("ResultSet returned null for $this") @@ -11,3 +16,33 @@ fun ExecutableQuery.executeAsFirstOrNull(): T? = execute { cursor - if (!cursor.next().value) return@execute QueryResult.Value(null) QueryResult.Value(mapper(cursor)) }.value + +suspend fun ExecutableQuery.awaitAsFirst(): T { + return awaitAsFirstOrNull() + ?: throw NullPointerException("ResultSet returned null for $this") +} + +suspend fun ExecutableQuery.awaitAsFirstOrNull(): T? = execute { cursor -> + // If the cursor isn't async, we want to preserve the blocking semantics and execute it synchronously + when (val next = cursor.next()) { + is QueryResult.AsyncValue -> { + QueryResult.AsyncValue { + if (!next.await()) return@AsyncValue null + mapper(cursor) + } + } + + is QueryResult.Value -> { + if (!next.value) return@execute QueryResult.Value(null) + QueryResult.Value(mapper(cursor)) + } + } +}.await() + +fun Flow>.mapToFirstOrNull( + context: CoroutineContext, +): Flow = map { + withContext(context) { + it.awaitAsFirstOrNull() + } +}