mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-16 13:48:09 -07:00
Read extension stores once at startup
Refreshing stores now fetches them all first and writes them in one transaction.
This commit is contained in:
@@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.app.di.appGraph
|
import mihon.app.di.appGraph
|
||||||
@@ -115,7 +116,7 @@ internal object ExtensionLoader {
|
|||||||
): List<Extension.Installed> {
|
): List<Extension.Installed> {
|
||||||
val trustExtension = context.appGraph.trustExtension
|
val trustExtension = context.appGraph.trustExtension
|
||||||
val sourcePreferences = context.appGraph.sourcePreferences
|
val sourcePreferences = context.appGraph.sourcePreferences
|
||||||
val stores = context.appGraph.extensionStoreRepository.getAll()
|
val stores = context.appGraph.extensionStoreRepository.getAllAsFlow().first()
|
||||||
val enabledContentWarnings = sourcePreferences.enabledContentWarnings.get()
|
val enabledContentWarnings = sourcePreferences.enabledContentWarnings.get()
|
||||||
val applyContentWarningsToInstalled = sourcePreferences.applyContentWarningsToInstalled.get()
|
val applyContentWarningsToInstalled = sourcePreferences.applyContentWarningsToInstalled.get()
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ dependencies {
|
|||||||
implementation(projects.sourceApi)
|
implementation(projects.sourceApi)
|
||||||
implementation(projects.domain)
|
implementation(projects.domain)
|
||||||
implementation(projects.core.common)
|
implementation(projects.core.common)
|
||||||
|
implementation(projects.core.metro)
|
||||||
|
|
||||||
implementation(libs.metro.runtime)
|
implementation(libs.metro.runtime)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
|||||||
+43
-15
@@ -7,11 +7,18 @@ import dev.zacsweers.metro.ContributesBinding
|
|||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import dev.zacsweers.metro.SingleIn
|
import dev.zacsweers.metro.SingleIn
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.WhileSubscribed
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import mihon.core.metro.AppCoroutineScope
|
||||||
import mihon.data.extension.service.ExtensionStoreService
|
import mihon.data.extension.service.ExtensionStoreService
|
||||||
import mihon.domain.extension.model.ExtensionStore
|
import mihon.domain.extension.model.ExtensionStore
|
||||||
import mihon.domain.extension.repository.ExtensionStoreRepository
|
import mihon.domain.extension.repository.ExtensionStoreRepository
|
||||||
@@ -19,14 +26,33 @@ import tachiyomi.core.common.util.system.logcat
|
|||||||
import tachiyomi.data.Database
|
import tachiyomi.data.Database
|
||||||
import tachiyomi.data.subscribeToList
|
import tachiyomi.data.subscribeToList
|
||||||
import tachiyomi.data.subscribeToOne
|
import tachiyomi.data.subscribeToOne
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@SingleIn(AppScope::class)
|
@SingleIn(AppScope::class)
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class ExtensionStoreRepositoryImpl(
|
class ExtensionStoreRepositoryImpl(
|
||||||
|
@AppCoroutineScope scope: CoroutineScope,
|
||||||
private val service: ExtensionStoreService,
|
private val service: ExtensionStoreService,
|
||||||
private val database: Database,
|
private val database: Database,
|
||||||
) : ExtensionStoreRepository {
|
) : ExtensionStoreRepository {
|
||||||
|
|
||||||
|
private val stores = database.extension_storeQueries.getAll(::extensionStoreMapper)
|
||||||
|
.subscribeToList()
|
||||||
|
.catch {
|
||||||
|
logcat(LogPriority.ERROR, it) { "Failed to query extension stores" }
|
||||||
|
emit(emptyList())
|
||||||
|
}
|
||||||
|
.shareIn(
|
||||||
|
scope = scope,
|
||||||
|
started = SharingStarted.WhileSubscribed(
|
||||||
|
stopTimeout = 5.seconds,
|
||||||
|
replayExpiration = Duration.ZERO,
|
||||||
|
),
|
||||||
|
replay = 1,
|
||||||
|
)
|
||||||
|
|
||||||
override suspend fun insert(indexUrl: String): Result<Unit> {
|
override suspend fun insert(indexUrl: String): Result<Unit> {
|
||||||
return service.fetch(indexUrl).mapCatching { upsert(it) }
|
return service.fetch(indexUrl).mapCatching { upsert(it) }
|
||||||
}
|
}
|
||||||
@@ -46,24 +72,26 @@ class ExtensionStoreRepositoryImpl(
|
|||||||
|
|
||||||
override suspend fun refreshAll() {
|
override suspend fun refreshAll() {
|
||||||
try {
|
try {
|
||||||
database.extension_storeQueries.getAll().awaitAsList().forEach { store ->
|
val refreshed = stores.first().mapNotNull { store ->
|
||||||
service.fetch(store.index_url)
|
service.fetch(store.indexUrl)
|
||||||
.mapCatching {
|
|
||||||
database.transaction {
|
|
||||||
if (!database.extension_storeQueries.contains(store.index_url).awaitAsOne()) {
|
|
||||||
return@transaction
|
|
||||||
}
|
|
||||||
upsert(it)
|
|
||||||
if (store.index_url != it.indexUrl) {
|
|
||||||
database.extension_storeQueries.delete(store.index_url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onFailure {
|
.onFailure {
|
||||||
logcat(LogPriority.ERROR, it) {
|
logcat(LogPriority.ERROR, it) {
|
||||||
"Failed to refresh extension store '${store.name} (${store.index_url})'"
|
"Failed to refresh extension store '${store.name} (${store.indexUrl})'"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.getOrNull()
|
||||||
|
?.let { store.indexUrl to it }
|
||||||
|
}
|
||||||
|
database.transaction {
|
||||||
|
refreshed.forEach { (indexUrl, store) ->
|
||||||
|
if (!database.extension_storeQueries.contains(indexUrl).awaitAsOne()) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
upsert(store)
|
||||||
|
if (indexUrl != store.indexUrl) {
|
||||||
|
database.extension_storeQueries.delete(indexUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
@@ -109,7 +137,7 @@ class ExtensionStoreRepositoryImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllAsFlow(): Flow<List<ExtensionStore>> {
|
override fun getAllAsFlow(): Flow<List<ExtensionStore>> {
|
||||||
return database.extension_storeQueries.getAll(::extensionStoreMapper).subscribeToList()
|
return stores
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCountAsFlow(): Flow<Long> {
|
override fun getCountAsFlow(): Flow<Long> {
|
||||||
|
|||||||
Reference in New Issue
Block a user