mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Use Flow in ExtensionManager and SourceManager (#7547)
- Replace ExtensionManager relay and observable with Flow
- Inverse SourceManager dependency
    - SourceManager observers ExtensionManager flow
- Separate SourceData from SourceRepository as it created a circular dependency
			
			
This commit is contained in:
		@@ -0,0 +1,23 @@
 | 
			
		||||
package eu.kanade.data.source
 | 
			
		||||
 | 
			
		||||
import eu.kanade.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.domain.source.model.SourceData
 | 
			
		||||
import eu.kanade.domain.source.repository.SourceDataRepository
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
 | 
			
		||||
class SourceDataRepositoryImpl(
 | 
			
		||||
    private val handler: DatabaseHandler,
 | 
			
		||||
) : SourceDataRepository {
 | 
			
		||||
 | 
			
		||||
    override fun subscribeAll(): Flow<List<SourceData>> {
 | 
			
		||||
        return handler.subscribeToList { sourcesQueries.findAll(sourceDataMapper) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getSourceData(id: Long): SourceData? {
 | 
			
		||||
        return handler.awaitOneOrNull { sourcesQueries.findOne(id, sourceDataMapper) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun upsertSourceData(id: Long, lang: String, name: String) {
 | 
			
		||||
        handler.await { sourcesQueries.upsert(id, lang, name) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,6 @@ package eu.kanade.data.source
 | 
			
		||||
 | 
			
		||||
import eu.kanade.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.domain.source.model.Source
 | 
			
		||||
import eu.kanade.domain.source.model.SourceData
 | 
			
		||||
import eu.kanade.domain.source.repository.SourceRepository
 | 
			
		||||
import eu.kanade.tachiyomi.source.LocalSource
 | 
			
		||||
import eu.kanade.tachiyomi.source.SourceManager
 | 
			
		||||
@@ -50,12 +49,4 @@ class SourceRepositoryImpl(
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getSourceData(id: Long): SourceData? {
 | 
			
		||||
        return handler.awaitOneOrNull { sourcesQueries.getSourceData(id, sourceDataMapper) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun upsertSourceData(id: Long, lang: String, name: String) {
 | 
			
		||||
        handler.await { sourcesQueries.upsert(id, lang, name) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import eu.kanade.data.category.CategoryRepositoryImpl
 | 
			
		||||
import eu.kanade.data.chapter.ChapterRepositoryImpl
 | 
			
		||||
import eu.kanade.data.history.HistoryRepositoryImpl
 | 
			
		||||
import eu.kanade.data.manga.MangaRepositoryImpl
 | 
			
		||||
import eu.kanade.data.source.SourceDataRepositoryImpl
 | 
			
		||||
import eu.kanade.data.source.SourceRepositoryImpl
 | 
			
		||||
import eu.kanade.data.track.TrackRepositoryImpl
 | 
			
		||||
import eu.kanade.domain.category.interactor.CreateCategoryWithName
 | 
			
		||||
@@ -47,14 +48,13 @@ import eu.kanade.domain.manga.interactor.UpdateManga
 | 
			
		||||
import eu.kanade.domain.manga.repository.MangaRepository
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetEnabledSources
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetSourceData
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
 | 
			
		||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
 | 
			
		||||
import eu.kanade.domain.source.interactor.ToggleLanguage
 | 
			
		||||
import eu.kanade.domain.source.interactor.ToggleSource
 | 
			
		||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
 | 
			
		||||
import eu.kanade.domain.source.interactor.UpsertSourceData
 | 
			
		||||
import eu.kanade.domain.source.repository.SourceDataRepository
 | 
			
		||||
import eu.kanade.domain.source.repository.SourceRepository
 | 
			
		||||
import eu.kanade.domain.track.interactor.DeleteTrack
 | 
			
		||||
import eu.kanade.domain.track.interactor.GetTracks
 | 
			
		||||
@@ -120,15 +120,14 @@ class DomainModule : InjektModule {
 | 
			
		||||
        addFactory { GetExtensionLanguages(get(), get()) }
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
 | 
			
		||||
        addSingletonFactory<SourceDataRepository> { SourceDataRepositoryImpl(get()) }
 | 
			
		||||
        addFactory { GetEnabledSources(get(), get()) }
 | 
			
		||||
        addFactory { GetLanguagesWithSources(get(), get()) }
 | 
			
		||||
        addFactory { GetSourceData(get()) }
 | 
			
		||||
        addFactory { GetSourcesWithFavoriteCount(get(), get()) }
 | 
			
		||||
        addFactory { GetSourcesWithNonLibraryManga(get()) }
 | 
			
		||||
        addFactory { SetMigrateSorting(get()) }
 | 
			
		||||
        addFactory { ToggleLanguage(get()) }
 | 
			
		||||
        addFactory { ToggleSource(get()) }
 | 
			
		||||
        addFactory { ToggleSourcePin(get()) }
 | 
			
		||||
        addFactory { UpsertSourceData(get()) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ class GetExtensionLanguages(
 | 
			
		||||
    fun subscribe(): Flow<List<String>> {
 | 
			
		||||
        return combine(
 | 
			
		||||
            preferences.enabledLanguages().asFlow(),
 | 
			
		||||
            extensionManager.getAvailableExtensionsObservable().asFlow(),
 | 
			
		||||
            extensionManager.getAvailableExtensionsFlow(),
 | 
			
		||||
        ) { enabledLanguage, availableExtensions ->
 | 
			
		||||
            availableExtensions
 | 
			
		||||
                .map { it.lang }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
package eu.kanade.domain.extension.interactor
 | 
			
		||||
 | 
			
		||||
import eu.kanade.core.util.asFlow
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.extension.ExtensionManager
 | 
			
		||||
import eu.kanade.tachiyomi.extension.model.Extension
 | 
			
		||||
@@ -15,7 +14,7 @@ class GetExtensionUpdates(
 | 
			
		||||
    fun subscribe(): Flow<List<Extension.Installed>> {
 | 
			
		||||
        val showNsfwSources = preferences.showNsfwSource().get()
 | 
			
		||||
 | 
			
		||||
        return extensionManager.getInstalledExtensionsObservable().asFlow()
 | 
			
		||||
        return extensionManager.getInstalledExtensionsFlow()
 | 
			
		||||
            .map { installed ->
 | 
			
		||||
                installed
 | 
			
		||||
                    .filter { it.hasUpdate && (showNsfwSources || it.isNsfw.not()) }
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,9 @@ class GetExtensions(
 | 
			
		||||
 | 
			
		||||
        return combine(
 | 
			
		||||
            preferences.enabledLanguages().asFlow(),
 | 
			
		||||
            extensionManager.getInstalledExtensionsObservable().asFlow(),
 | 
			
		||||
            extensionManager.getUntrustedExtensionsObservable().asFlow(),
 | 
			
		||||
            extensionManager.getAvailableExtensionsObservable().asFlow(),
 | 
			
		||||
            extensionManager.getInstalledExtensionsFlow(),
 | 
			
		||||
            extensionManager.getUntrustedExtensionsFlow(),
 | 
			
		||||
            extensionManager.getAvailableExtensionsFlow(),
 | 
			
		||||
        ) { _activeLanguages, _installed, _untrusted, _available ->
 | 
			
		||||
 | 
			
		||||
            val installed = _installed
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
package eu.kanade.domain.source.interactor
 | 
			
		||||
 | 
			
		||||
import eu.kanade.domain.source.model.SourceData
 | 
			
		||||
import eu.kanade.domain.source.repository.SourceRepository
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.logcat
 | 
			
		||||
import logcat.LogPriority
 | 
			
		||||
 | 
			
		||||
class GetSourceData(
 | 
			
		||||
    private val repository: SourceRepository,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    suspend fun await(id: Long): SourceData? {
 | 
			
		||||
        return try {
 | 
			
		||||
            repository.getSourceData(id)
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            logcat(LogPriority.ERROR, e)
 | 
			
		||||
            null
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
package eu.kanade.domain.source.interactor
 | 
			
		||||
 | 
			
		||||
import eu.kanade.domain.source.model.SourceData
 | 
			
		||||
import eu.kanade.domain.source.repository.SourceRepository
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.logcat
 | 
			
		||||
import logcat.LogPriority
 | 
			
		||||
 | 
			
		||||
class UpsertSourceData(
 | 
			
		||||
    private val repository: SourceRepository,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    suspend fun await(sourceData: SourceData) {
 | 
			
		||||
        try {
 | 
			
		||||
            repository.upsertSourceData(sourceData.id, sourceData.lang, sourceData.name)
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            logcat(LogPriority.ERROR, e)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
package eu.kanade.domain.source.repository
 | 
			
		||||
 | 
			
		||||
import eu.kanade.domain.source.model.SourceData
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
 | 
			
		||||
interface SourceDataRepository {
 | 
			
		||||
    fun subscribeAll(): Flow<List<SourceData>>
 | 
			
		||||
 | 
			
		||||
    suspend fun getSourceData(id: Long): SourceData?
 | 
			
		||||
 | 
			
		||||
    suspend fun upsertSourceData(id: Long, lang: String, name: String)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package eu.kanade.domain.source.repository
 | 
			
		||||
 | 
			
		||||
import eu.kanade.domain.source.model.Source
 | 
			
		||||
import eu.kanade.domain.source.model.SourceData
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source as LoadedSource
 | 
			
		||||
 | 
			
		||||
@@ -14,8 +13,4 @@ interface SourceRepository {
 | 
			
		||||
    fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>>
 | 
			
		||||
 | 
			
		||||
    fun getSourcesWithNonLibraryManga(): Flow<List<Pair<LoadedSource, Long>>>
 | 
			
		||||
 | 
			
		||||
    suspend fun getSourceData(id: Long): SourceData?
 | 
			
		||||
 | 
			
		||||
    suspend fun upsertSourceData(id: Long, lang: String, name: String)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -87,10 +87,10 @@ class AppModule(val app: Application) : InjektModule {
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory { NetworkHelper(app) }
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory { SourceManager(app).also { get<ExtensionManager>().init(it) } }
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory { ExtensionManager(app) }
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory { SourceManager(app, get(), get()) }
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory { DownloadManager(app) }
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory { TrackManager(app) }
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@ import eu.kanade.tachiyomi.extension.util.ExtensionInstallReceiver
 | 
			
		||||
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
 | 
			
		||||
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.util.lang.launchNow
 | 
			
		||||
import eu.kanade.tachiyomi.util.preference.plusAssign
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.logcat
 | 
			
		||||
@@ -88,22 +87,23 @@ class ExtensionManager(
 | 
			
		||||
        return null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Relay used to notify the available extensions.
 | 
			
		||||
     */
 | 
			
		||||
    private val availableExtensionsRelay = BehaviorRelay.create<List<Extension.Available>>()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * List of the currently available extensions.
 | 
			
		||||
     */
 | 
			
		||||
    var availableExtensions = emptyList<Extension.Available>()
 | 
			
		||||
        private set(value) {
 | 
			
		||||
            field = value
 | 
			
		||||
            availableExtensionsRelay.call(value)
 | 
			
		||||
            availableExtensionsFlow.value = field
 | 
			
		||||
            updatedInstalledExtensionsStatuses(value)
 | 
			
		||||
            setupAvailableExtensionsSourcesDataMap(value)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private val availableExtensionsFlow = MutableStateFlow(availableExtensions)
 | 
			
		||||
 | 
			
		||||
    fun getAvailableExtensionsFlow(): StateFlow<List<Extension.Available>> {
 | 
			
		||||
        return availableExtensionsFlow.asStateFlow()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var availableExtensionsSourcesData: Map<Long, SourceData> = mapOf()
 | 
			
		||||
 | 
			
		||||
    private fun setupAvailableExtensionsSourcesDataMap(extensions: List<Extension.Available>) {
 | 
			
		||||
@@ -115,30 +115,22 @@ class ExtensionManager(
 | 
			
		||||
 | 
			
		||||
    fun getSourceData(id: Long) = availableExtensionsSourcesData[id]
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Relay used to notify the untrusted extensions.
 | 
			
		||||
     */
 | 
			
		||||
    private val untrustedExtensionsRelay = BehaviorRelay.create<List<Extension.Untrusted>>()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * List of the currently untrusted extensions.
 | 
			
		||||
     */
 | 
			
		||||
    var untrustedExtensions = emptyList<Extension.Untrusted>()
 | 
			
		||||
        private set(value) {
 | 
			
		||||
            field = value
 | 
			
		||||
            untrustedExtensionsRelay.call(value)
 | 
			
		||||
            untrustedExtensionsFlow.value = field
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The source manager where the sources of the extensions are added.
 | 
			
		||||
     */
 | 
			
		||||
    private lateinit var sourceManager: SourceManager
 | 
			
		||||
    private val untrustedExtensionsFlow = MutableStateFlow(untrustedExtensions)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initializes this manager with the given source manager.
 | 
			
		||||
     */
 | 
			
		||||
    fun init(sourceManager: SourceManager) {
 | 
			
		||||
        this.sourceManager = sourceManager
 | 
			
		||||
    fun getUntrustedExtensionsFlow(): StateFlow<List<Extension.Untrusted>> {
 | 
			
		||||
        return untrustedExtensionsFlow.asStateFlow()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        initExtensions()
 | 
			
		||||
        ExtensionInstallReceiver(InstallationListener()).register(context)
 | 
			
		||||
    }
 | 
			
		||||
@@ -152,36 +144,12 @@ class ExtensionManager(
 | 
			
		||||
        installedExtensions = extensions
 | 
			
		||||
            .filterIsInstance<LoadResult.Success>()
 | 
			
		||||
            .map { it.extension }
 | 
			
		||||
        installedExtensions
 | 
			
		||||
            .flatMap { it.sources }
 | 
			
		||||
            .forEach { sourceManager.registerSource(it) }
 | 
			
		||||
 | 
			
		||||
        untrustedExtensions = extensions
 | 
			
		||||
            .filterIsInstance<LoadResult.Untrusted>()
 | 
			
		||||
            .map { it.extension }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the relay of the installed extensions as an observable.
 | 
			
		||||
     */
 | 
			
		||||
    fun getInstalledExtensionsObservable(): Observable<List<Extension.Installed>> {
 | 
			
		||||
        return installedExtensionsRelay.asObservable()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the relay of the available extensions as an observable.
 | 
			
		||||
     */
 | 
			
		||||
    fun getAvailableExtensionsObservable(): Observable<List<Extension.Available>> {
 | 
			
		||||
        return availableExtensionsRelay.asObservable()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the relay of the untrusted extensions as an observable.
 | 
			
		||||
     */
 | 
			
		||||
    fun getUntrustedExtensionsObservable(): Observable<List<Extension.Untrusted>> {
 | 
			
		||||
        return untrustedExtensionsRelay.asObservable()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Finds the available extensions in the [api] and updates [availableExtensions].
 | 
			
		||||
     */
 | 
			
		||||
@@ -324,7 +292,6 @@ class ExtensionManager(
 | 
			
		||||
     */
 | 
			
		||||
    private fun registerNewExtension(extension: Extension.Installed) {
 | 
			
		||||
        installedExtensions += extension
 | 
			
		||||
        extension.sources.forEach { sourceManager.registerSource(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -338,11 +305,9 @@ class ExtensionManager(
 | 
			
		||||
        val oldExtension = mutInstalledExtensions.find { it.pkgName == extension.pkgName }
 | 
			
		||||
        if (oldExtension != null) {
 | 
			
		||||
            mutInstalledExtensions -= oldExtension
 | 
			
		||||
            extension.sources.forEach { sourceManager.unregisterSource(it) }
 | 
			
		||||
        }
 | 
			
		||||
        mutInstalledExtensions += extension
 | 
			
		||||
        installedExtensions = mutInstalledExtensions
 | 
			
		||||
        extension.sources.forEach { sourceManager.registerSource(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -355,7 +320,6 @@ class ExtensionManager(
 | 
			
		||||
        val installedExtension = installedExtensions.find { it.pkgName == pkgName }
 | 
			
		||||
        if (installedExtension != null) {
 | 
			
		||||
            installedExtensions -= installedExtension
 | 
			
		||||
            installedExtension.sources.forEach { sourceManager.unregisterSource(it) }
 | 
			
		||||
        }
 | 
			
		||||
        val untrustedExtension = untrustedExtensions.find { it.pkgName == pkgName }
 | 
			
		||||
        if (untrustedExtension != null) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,42 +1,72 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetSourceData
 | 
			
		||||
import eu.kanade.domain.source.interactor.UpsertSourceData
 | 
			
		||||
import eu.kanade.domain.source.model.SourceData
 | 
			
		||||
import eu.kanade.domain.source.repository.SourceDataRepository
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.extension.ExtensionManager
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.Page
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SChapter
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.HttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.util.lang.launchIO
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.MutableStateFlow
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import kotlinx.coroutines.flow.map
 | 
			
		||||
import kotlinx.coroutines.flow.update
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.runBlocking
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import tachiyomi.source.model.ChapterInfo
 | 
			
		||||
import tachiyomi.source.model.MangaInfo
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class SourceManager(private val context: Context) {
 | 
			
		||||
class SourceManager(
 | 
			
		||||
    private val context: Context,
 | 
			
		||||
    private val extensionManager: ExtensionManager,
 | 
			
		||||
    private val sourceRepository: SourceDataRepository,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    private val extensionManager: ExtensionManager by injectLazy()
 | 
			
		||||
    private val getSourceData: GetSourceData by injectLazy()
 | 
			
		||||
    private val upsertSourceData: UpsertSourceData by injectLazy()
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.IO)
 | 
			
		||||
 | 
			
		||||
    private var sourcesMap = emptyMap<Long, Source>()
 | 
			
		||||
        set(value) {
 | 
			
		||||
            field = value
 | 
			
		||||
            sourcesMapFlow.value = field
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private val sourcesMapFlow = MutableStateFlow(sourcesMap)
 | 
			
		||||
 | 
			
		||||
    private val sourcesMap = mutableMapOf<Long, Source>()
 | 
			
		||||
    private val stubSourcesMap = mutableMapOf<Long, StubSource>()
 | 
			
		||||
 | 
			
		||||
    private val _catalogueSources: MutableStateFlow<List<CatalogueSource>> = MutableStateFlow(listOf())
 | 
			
		||||
    val catalogueSources: Flow<List<CatalogueSource>> = _catalogueSources
 | 
			
		||||
    val onlineSources: Flow<List<HttpSource>> =
 | 
			
		||||
        _catalogueSources.map { sources -> sources.filterIsInstance<HttpSource>() }
 | 
			
		||||
    val catalogueSources: Flow<List<CatalogueSource>> = sourcesMapFlow.map { it.values.filterIsInstance<CatalogueSource>() }
 | 
			
		||||
    val onlineSources: Flow<List<HttpSource>> = catalogueSources.map { sources -> sources.filterIsInstance<HttpSource>() }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        createInternalSources().forEach { registerSource(it) }
 | 
			
		||||
        scope.launch {
 | 
			
		||||
            extensionManager.getInstalledExtensionsFlow()
 | 
			
		||||
                .collectLatest { extensions ->
 | 
			
		||||
                    val mutableMap = mutableMapOf<Long, Source>(LocalSource.ID to LocalSource(context))
 | 
			
		||||
                    extensions.forEach { extension ->
 | 
			
		||||
                        extension.sources.forEach {
 | 
			
		||||
                            mutableMap[it.id] = it
 | 
			
		||||
                            registerStubSource(it.toSourceData())
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    sourcesMap = mutableMap
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        scope.launch {
 | 
			
		||||
            sourceRepository.subscribeAll()
 | 
			
		||||
                .collectLatest { sources ->
 | 
			
		||||
                    val mutableMap = stubSourcesMap.toMutableMap()
 | 
			
		||||
                    sources.forEach {
 | 
			
		||||
                        mutableMap[it.id] = StubSource(it)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun get(sourceKey: Long): Source? {
 | 
			
		||||
@@ -58,44 +88,15 @@ class SourceManager(private val context: Context) {
 | 
			
		||||
        return stubSourcesMap.values.filterNot { it.id in onlineSourceIds }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun registerSource(source: Source) {
 | 
			
		||||
        if (!sourcesMap.containsKey(source.id)) {
 | 
			
		||||
            sourcesMap[source.id] = source
 | 
			
		||||
        }
 | 
			
		||||
        registerStubSource(source.toSourceData())
 | 
			
		||||
        triggerCatalogueSources()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun registerStubSource(sourceData: SourceData) {
 | 
			
		||||
        launchIO {
 | 
			
		||||
            val dbSourceData = getSourceData.await(sourceData.id)
 | 
			
		||||
 | 
			
		||||
            if (dbSourceData != sourceData) {
 | 
			
		||||
                upsertSourceData.await(sourceData)
 | 
			
		||||
            }
 | 
			
		||||
            if (stubSourcesMap[sourceData.id]?.toSourceData() != sourceData) {
 | 
			
		||||
                stubSourcesMap[sourceData.id] = StubSource(sourceData)
 | 
			
		||||
            }
 | 
			
		||||
        scope.launch {
 | 
			
		||||
            val (id, lang, name) = sourceData
 | 
			
		||||
            sourceRepository.upsertSourceData(id, lang, name)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun unregisterSource(source: Source) {
 | 
			
		||||
        sourcesMap.remove(source.id)
 | 
			
		||||
        triggerCatalogueSources()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun triggerCatalogueSources() {
 | 
			
		||||
        _catalogueSources.update {
 | 
			
		||||
            sourcesMap.values.filterIsInstance<CatalogueSource>()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun createInternalSources(): List<Source> = listOf(
 | 
			
		||||
        LocalSource(context),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private suspend fun createStubSource(id: Long): StubSource {
 | 
			
		||||
        getSourceData.await(id)?.let {
 | 
			
		||||
        sourceRepository.getSourceData(id)?.let {
 | 
			
		||||
            return StubSource(it)
 | 
			
		||||
        }
 | 
			
		||||
        extensionManager.getSourceData(id)?.let {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,11 @@ CREATE TABLE sources(
 | 
			
		||||
    name TEXT NOT NULL
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
getSourceData:
 | 
			
		||||
findAll:
 | 
			
		||||
SELECT *
 | 
			
		||||
FROM sources;
 | 
			
		||||
 | 
			
		||||
findOne:
 | 
			
		||||
SELECT *
 | 
			
		||||
FROM sources
 | 
			
		||||
WHERE _id = :id;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user