Refactor BasePresenter

This commit is contained in:
Koitharu
2020-10-11 13:16:38 +03:00
parent 5293a8d209
commit 693f568b8e
4 changed files with 82 additions and 138 deletions

View File

@@ -1,7 +1,44 @@
package org.koitharu.kotatsu.ui.common package org.koitharu.kotatsu.ui.common
import kotlinx.coroutines.*
import moxy.MvpPresenter import moxy.MvpPresenter
import moxy.MvpView import moxy.presenterScope
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koitharu.kotatsu.BuildConfig
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
abstract class BasePresenter<V : MvpView> : MvpPresenter<V>(), KoinComponent abstract class BasePresenter<V : BaseMvpView> : MvpPresenter<V>(), KoinComponent {
protected fun launchJob(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
) {
presenterScope.launch(context + createErrorHandler(), start, block)
}
protected fun launchLoadingJob(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
) {
presenterScope.launch(context + createErrorHandler(), start) {
viewState.onLoadingStateChanged(isLoading = true)
try {
block()
} finally {
viewState.onLoadingStateChanged(isLoading = false)
}
}
}
private fun createErrorHandler() = CoroutineExceptionHandler { _, throwable ->
if (BuildConfig.DEBUG) {
throwable.printStackTrace()
}
if (throwable !is CancellationException) {
viewState.onError(throwable)
}
}
}

View File

@@ -1,13 +1,8 @@
package org.koitharu.kotatsu.ui.list.favourites.categories package org.koitharu.kotatsu.ui.list.favourites.categories
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import moxy.InjectViewState import moxy.InjectViewState
import moxy.presenterScope
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.domain.favourites.FavouritesRepository import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.ui.common.BasePresenter import org.koitharu.kotatsu.ui.common.BasePresenter
@@ -27,130 +22,59 @@ class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
} }
fun loadAllCategories() { fun loadAllCategories() {
presenterScope.launch { launchJob {
try { val categories = repository.getAllCategories()
val categories = withContext(Dispatchers.IO) { viewState.onCategoriesChanged(categories)
repository.getAllCategories()
}
viewState.onCategoriesChanged(categories)
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
}
} }
} }
fun loadMangaCategories(manga: Manga) { fun loadMangaCategories(manga: Manga) {
presenterScope.launch { launchJob {
try { val categories = repository.getCategories(manga.id)
val categories = withContext(Dispatchers.IO) { viewState.onCheckedCategoriesChanged(categories.map { it.id.toInt() }.toSet())
repository.getCategories(manga.id)
}
viewState.onCheckedCategoriesChanged(categories.map { it.id.toInt() }.toSet())
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
}
} }
} }
fun createCategory(name: String) { fun createCategory(name: String) {
presenterScope.launch { launchJob {
try { repository.addCategory(name)
val categories = withContext(Dispatchers.IO) { val categories = repository.getAllCategories()
repository.addCategory(name) viewState.onCategoriesChanged(categories)
repository.getAllCategories()
}
viewState.onCategoriesChanged(categories)
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
}
} }
} }
fun renameCategory(id: Long, name: String) { fun renameCategory(id: Long, name: String) {
presenterScope.launch { launchJob {
try { repository.renameCategory(id, name)
val categories = withContext(Dispatchers.IO) {
repository.renameCategory(id, name)
repository.getAllCategories()
}
viewState.onCategoriesChanged(categories)
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
}
} }
} }
fun deleteCategory(id: Long) { fun deleteCategory(id: Long) {
presenterScope.launch { launchJob {
try { repository.removeCategory(id)
val categories = withContext(Dispatchers.IO) { val categories = repository.getAllCategories()
repository.removeCategory(id) viewState.onCategoriesChanged(categories)
repository.getAllCategories()
}
viewState.onCategoriesChanged(categories)
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
}
} }
} }
fun storeCategoriesOrder(orderedIds: List<Long>) { fun storeCategoriesOrder(orderedIds: List<Long>) {
presenterScope.launch { launchJob {
try { reorderMutex.withLock {
reorderMutex.withLock { repository.reorderCategories(orderedIds)
repository.reorderCategories(orderedIds)
}
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
} }
} }
} }
fun addToCategory(manga: Manga, categoryId: Long) { fun addToCategory(manga: Manga, categoryId: Long) {
presenterScope.launch { launchJob {
try { repository.addToCategory(manga, categoryId)
withContext(Dispatchers.IO) {
repository.addToCategory(manga,categoryId)
}
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
}
} }
} }
fun removeFromCategory(manga: Manga, categoryId: Long) { fun removeFromCategory(manga: Manga, categoryId: Long) {
presenterScope.launch { launchJob {
try { repository.removeFromCategory(manga, categoryId)
withContext(Dispatchers.IO) {
repository.removeFromCategory(manga, categoryId)
}
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
}
} }
} }
} }

View File

@@ -1,12 +1,12 @@
package org.koitharu.kotatsu.ui.list.favourites.categories package org.koitharu.kotatsu.ui.list.favourites.categories
import moxy.MvpView
import moxy.viewstate.strategy.AddToEndSingleStrategy import moxy.viewstate.strategy.AddToEndSingleStrategy
import moxy.viewstate.strategy.OneExecutionStateStrategy
import moxy.viewstate.strategy.StateStrategyType import moxy.viewstate.strategy.StateStrategyType
import moxy.viewstate.strategy.alias.AddToEndSingle
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.BaseMvpView
interface FavouriteCategoriesView : MvpView { interface FavouriteCategoriesView : BaseMvpView {
@StateStrategyType(AddToEndSingleStrategy::class) @StateStrategyType(AddToEndSingleStrategy::class)
fun onCategoriesChanged(categories: List<FavouriteCategory>) fun onCategoriesChanged(categories: List<FavouriteCategory>)
@@ -14,6 +14,6 @@ interface FavouriteCategoriesView : MvpView {
@StateStrategyType(AddToEndSingleStrategy::class) @StateStrategyType(AddToEndSingleStrategy::class)
fun onCheckedCategoriesChanged(checkedIds: Set<Int>) fun onCheckedCategoriesChanged(checkedIds: Set<Int>)
@StateStrategyType(OneExecutionStateStrategy::class) @AddToEndSingle
fun onError(e: Throwable) override fun onLoadingStateChanged(isLoading: Boolean) = Unit
} }

View File

@@ -62,8 +62,8 @@ class LocalListPresenter : BasePresenter<MangaListView<File>>() {
} }
fun importFile(context: Context, uri: Uri) { fun importFile(context: Context, uri: Uri) {
presenterScope.launch(Dispatchers.IO) { launchJob {
try { val list = withContext(Dispatchers.IO) {
val name = MediaStoreCompat.getName(context, uri) val name = MediaStoreCompat.getName(context, uri)
?: throw IOException("Cannot fetch name from uri: $uri") ?: throw IOException("Cannot fetch name from uri: $uri")
if (!LocalMangaRepository.isFileSupported(name)) { if (!LocalMangaRepository.isFileSupported(name)) {
@@ -76,42 +76,25 @@ class LocalListPresenter : BasePresenter<MangaListView<File>>() {
source.copyTo(output) source.copyTo(output)
} }
} ?: throw IOException("Cannot open input stream: $uri") } ?: throw IOException("Cannot open input stream: $uri")
val list = repository.getList(0) repository.getList(0)
withContext(Dispatchers.Main) {
viewState.onListChanged(list)
}
} catch (e: CancellationException) {
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
withContext(Dispatchers.Main) {
viewState.onError(e)
}
} }
viewState.onListChanged(list)
} }
} }
fun delete(manga: Manga) { fun delete(manga: Manga) {
presenterScope.launch { launchJob {
try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { val original = repository.getRemoteManga(manga)
val original = repository.getRemoteManga(manga) repository.delete(manga) || throw IOException("Unable to delete file")
repository.delete(manga) || throw IOException("Unable to delete file") safe {
safe { HistoryRepository().deleteOrSwap(manga, original)
HistoryRepository().deleteOrSwap(manga, original)
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
MangaShortcut(manga).removeAppShortcut(get())
}
viewState.onItemRemoved(manga)
} catch (e: CancellationException) {
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
} }
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
MangaShortcut(manga).removeAppShortcut(get())
}
viewState.onItemRemoved(manga)
} }
} }
} }