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:
		@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user