Make a coroutine presenter

This commit is contained in:
Jobobby04 2020-12-20 18:56:09 -05:00
parent d302a0fbc7
commit 644140b617
4 changed files with 71 additions and 32 deletions

View File

@ -181,7 +181,7 @@ private suspend fun <T> Observable<T>.awaitOne(): T = suspendCancellableCoroutin
internal fun <T> CancellableContinuation<T>.unsubscribeOnCancellation(sub: Subscription) = internal fun <T> CancellableContinuation<T>.unsubscribeOnCancellation(sub: Subscription) =
invokeOnCancellation { sub.unsubscribe() } invokeOnCancellation { sub.unsubscribe() }
fun <T : Any> Observable<T>.asFlow(): Flow<T> = callbackFlow { fun <T : Any?> Observable<T>.asFlow(): Flow<T> = callbackFlow {
val observer = object : Observer<T> { val observer = object : Observer<T> {
override fun onNext(t: T) { override fun onNext(t: T) {
offer(t) offer(t)
@ -199,7 +199,7 @@ fun <T : Any> Observable<T>.asFlow(): Flow<T> = callbackFlow {
awaitClose { subscription.unsubscribe() } awaitClose { subscription.unsubscribe() }
} }
fun <T : Any> Flow<T>.asObservable(backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE): Observable<T> { fun <T : Any?> Flow<T>.asObservable(backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE): Observable<T> {
return Observable.create( return Observable.create(
{ emitter -> { emitter ->
/* /*

View File

@ -0,0 +1,41 @@
package exh.ui.base
import androidx.annotation.CallSuper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import nucleus.presenter.Presenter
@Suppress("DEPRECATION")
open class CoroutinePresenter<V> : Presenter<V>() {
val scope = CoroutineScope(Job() + Dispatchers.Main)
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use launchInView")
override fun getView(): V? {
return super.getView()
}
fun launchInView(block: (CoroutineScope, V) -> Unit) = scope.launch(Dispatchers.Main) {
view?.let { block.invoke(this, it) }
}
fun <F> Flow<F>.onEachView(block: (V, F) -> Unit) = onEach {
view?.let { view -> block(view, it) }
}
fun <F, P> Flow<F>.mapView(block: (V, F) -> P): Flow<P> = mapNotNull {
view?.let { view -> block(view, it) }
}
@CallSuper
override fun destroy() {
super.destroy()
scope.cancel()
}
}

View File

@ -11,12 +11,9 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.MetadataViewControllerBinding import eu.kanade.tachiyomi.databinding.MetadataViewControllerBinding
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.source.getMainSource
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -74,13 +71,6 @@ class MetadataViewController : NucleusController<MetadataViewControllerBinding,
binding.recycler.setHasFixedSize(true) binding.recycler.setHasFixedSize(true)
} }
fun onNextMetaInfo(flatMetadata: FlatMetadata) {
val mainSource = presenter.source.getMainSource()
if (mainSource is MetadataSource<*, *>) {
presenter.meta = flatMetadata.raise(mainSource.metaClass)
}
}
fun onNextMangaInfo(meta: RaisedSearchMetadata?) { fun onNextMangaInfo(meta: RaisedSearchMetadata?) {
val context = view?.context ?: return val context = view?.context ?: return
data = meta?.getExtraInfoPairs(context).orEmpty() data = meta?.getExtraInfoPairs(context).orEmpty()

View File

@ -1,17 +1,23 @@
package exh.ui.metadata package exh.ui.metadata
import android.os.Bundle import android.os.Bundle
import com.elvishew.xlog.XLog
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.util.lang.asFlow
import exh.metadata.metadata.base.FlatMetadata import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import rx.Observable import exh.source.getMainSource
import rx.android.schedulers.AndroidSchedulers import exh.ui.base.CoroutinePresenter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -20,29 +26,31 @@ class MetadataViewPresenter(
val source: Source, val source: Source,
val preferences: PreferencesHelper = Injekt.get(), val preferences: PreferencesHelper = Injekt.get(),
private val db: DatabaseHelper = Injekt.get() private val db: DatabaseHelper = Injekt.get()
) : BasePresenter<MetadataViewController>() { ) : CoroutinePresenter<MetadataViewController>() {
var meta: RaisedSearchMetadata? = null val meta = MutableStateFlow<RaisedSearchMetadata?>(null)
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
getMangaMetaObservable().subscribeLatestCache({ view, flatMetadata -> if (flatMetadata != null) view.onNextMetaInfo(flatMetadata) else XLog.tag("MetadataViewPresenter").disableStackTrace().d("Invalid metadata") }) getMangaMetaObservable()
.onEach {
if (it == null) return@onEach
val mainSource = source.getMainSource()
if (mainSource is MetadataSource<*, *>) {
meta.value = it.raise(mainSource.metaClass)
}
}
.launchIn(scope + Dispatchers.IO)
getMangaObservable() meta
.observeOn(AndroidSchedulers.mainThread()) .onEachView { view, metadata ->
.subscribeLatestCache({ view, _ -> view.onNextMangaInfo(meta) }) view.onNextMangaInfo(metadata)
}
.launchIn(scope)
} }
private fun getMangaObservable(): Observable<Manga> { private fun getMangaMetaObservable(): Flow<FlatMetadata?> {
return db.getManga(manga.url, manga.source).asRxObservable() return db.getFlatMetadataForManga(manga.id!!).asRxObservable().asFlow()
}
private fun getMangaMetaObservable(): Observable<FlatMetadata?> {
val mangaId = manga.id
return if (mangaId != null) {
db.getFlatMetadataForManga(mangaId).asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
} else Observable.just(null)
} }
} }