Cleanup unused files and strings

This commit is contained in:
Jobobby04
2022-03-27 20:24:13 -04:00
parent 5d1d5778ad
commit c67b7092fb
17 changed files with 11 additions and 1057 deletions
@@ -11,7 +11,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.LatestControllerBinding
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerBinding
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
@@ -28,7 +28,7 @@ import exh.savedsearches.models.SavedSearch
* [FeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search
*/
open class FeedController :
NucleusController<LatestControllerBinding, FeedPresenter>(),
NucleusController<GlobalSearchControllerBinding, FeedPresenter>(),
FeedCardAdapter.OnMangaClickListener,
FeedAdapter.OnFeedClickListener {
@@ -123,7 +123,7 @@ open class FeedController :
onMangaClick(manga)
}
override fun createBinding(inflater: LayoutInflater): LatestControllerBinding = LatestControllerBinding.inflate(inflater)
override fun createBinding(inflater: LayoutInflater): GlobalSearchControllerBinding = GlobalSearchControllerBinding.inflate(inflater)
/**
* Called when the view is created
@@ -6,7 +6,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.LatestControllerCardBinding
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardBinding
import eu.kanade.tachiyomi.util.system.LocaleHelper
/**
@@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
class FeedHolder(view: View, val adapter: FeedAdapter) :
FlexibleViewHolder(view, adapter) {
private val binding = LatestControllerCardBinding.bind(view)
private val binding = GlobalSearchControllerCardBinding.bind(view)
/**
* Adapter containing manga from search results.
@@ -345,7 +345,7 @@ open class BrowseSourcePresenter(
.forEach { service ->
launchIO {
try {
service.match(source, manga)?.let { track ->
service.match(manga)?.let { track ->
track.manga_id = manga.id!!
(service as TrackService).bind(track)
db.insertTrack(track).executeAsBlocking()
@@ -13,7 +13,7 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.LatestControllerBinding
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerBinding
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.FilterList
@@ -40,7 +40,7 @@ import xyz.nulldev.ts.api.http.serializer.FilterSerializer
* [SourceFeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search
*/
open class SourceFeedController :
SearchableNucleusController<LatestControllerBinding, SourceFeedPresenter>,
SearchableNucleusController<GlobalSearchControllerBinding, SourceFeedPresenter>,
FabController,
SourceFeedCardAdapter.OnMangaClickListener,
SourceFeedAdapter.OnFeedClickListener {
@@ -131,7 +131,7 @@ open class SourceFeedController :
}
}
override fun createBinding(inflater: LayoutInflater): LatestControllerBinding = LatestControllerBinding.inflate(inflater)
override fun createBinding(inflater: LayoutInflater): GlobalSearchControllerBinding = GlobalSearchControllerBinding.inflate(inflater)
/**
* Called when the view is created
@@ -7,7 +7,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.LatestControllerCardBinding
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardBinding
/**
* Holder that binds the [SourceFeedItem] containing catalogue cards.
@@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.databinding.LatestControllerCardBinding
class SourceFeedHolder(view: View, val adapter: SourceFeedAdapter) :
FlexibleViewHolder(view, adapter) {
private val binding = LatestControllerCardBinding.bind(view)
private val binding = GlobalSearchControllerCardBinding.bind(view)
/**
* Adapter containing manga from search results.
@@ -1,27 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.source.index
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.models.Manga
/**
* Adapter that holds the manga items from search results.
*
* @param controller instance of [IndexController].
*/
class IndexCardAdapter(controller: IndexController) :
FlexibleAdapter<IndexCardItem>(null, controller, true) {
/**
* Listen for browse item clicks.
*/
val mangaClickListener: OnMangaClickListener = controller
/**
* Listener which should be called when user clicks browse.
* Note: Should only be handled by [IndexController]
*/
interface OnMangaClickListener {
fun onMangaClick(manga: Manga)
fun onMangaLongClick(manga: Manga)
}
}
@@ -1,58 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.source.index
import android.view.View
import androidx.core.view.isVisible
import coil.dispose
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardItemBinding
import eu.kanade.tachiyomi.util.view.loadAutoPause
class IndexCardHolder(view: View, adapter: IndexCardAdapter) :
FlexibleViewHolder(view, adapter) {
private val binding = GlobalSearchControllerCardItemBinding.bind(view)
init {
// Call onMangaClickListener when item is pressed.
itemView.setOnClickListener {
val item = adapter.getItem(bindingAdapterPosition)
if (item != null) {
adapter.mangaClickListener.onMangaClick(item.manga)
}
}
itemView.setOnLongClickListener {
val item = adapter.getItem(bindingAdapterPosition)
if (item != null) {
adapter.mangaClickListener.onMangaLongClick(item.manga)
}
true
}
}
fun bind(manga: Manga) {
binding.card.clipToOutline = true
// Set manga title
binding.title.text = manga.title
// Set alpha of thumbnail.
binding.cover.alpha = if (manga.favorite) 0.3f else 1.0f
// For rounded corners
binding.badges.clipToOutline = true
// Set favorite badge
binding.favoriteText.isVisible = manga.favorite
setImage(manga)
}
fun setImage(manga: Manga) {
binding.cover.dispose()
binding.cover.loadAutoPause(manga) {
setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
}
}
}
@@ -1,40 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.source.index
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
class IndexCardItem(val manga: Manga) : AbstractFlexibleItem<IndexCardHolder>() {
override fun getLayoutRes(): Int {
return R.layout.global_search_controller_card_item
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): IndexCardHolder {
return IndexCardHolder(view, adapter as IndexCardAdapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: IndexCardHolder,
position: Int,
payloads: List<Any?>?
) {
holder.bind(manga)
}
override fun equals(other: Any?): Boolean {
if (other is IndexCardItem) {
return manga.id == other.manga.id
}
return false
}
override fun hashCode(): Int {
return manga.id?.toInt() ?: 0
}
}
@@ -1,346 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.source.index
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.IndexControllerBinding
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.ui.base.controller.FabController
import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterSheet
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.toast
import exh.util.nullIfBlank
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import reactivecircus.flowbinding.android.view.clicks
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import xyz.nulldev.ts.api.http.serializer.FilterSerializer
/**
* This controller shows and manages the different search result in global search.
* This controller should only handle UI actions, IO actions should be done by [IndexPresenter]
* [IndexCardAdapter.OnMangaClickListener] called when manga is clicked in global search
*/
open class IndexController :
SearchableNucleusController<IndexControllerBinding, IndexPresenter>,
FabController,
IndexCardAdapter.OnMangaClickListener {
constructor(source: CatalogueSource?) : super(
bundleOf(
SOURCE_EXTRA to (source?.id ?: 0)
)
) {
this.source = source
}
constructor(sourceId: Long) : this(
Injekt.get<SourceManager>().get(sourceId) as? CatalogueSource
)
@Suppress("unused")
constructor(bundle: Bundle) : this(bundle.getLong(SOURCE_EXTRA))
var source: CatalogueSource? = null
private var latestAdapter: IndexCardAdapter? = null
private var browseAdapter: IndexCardAdapter? = null
private var actionFab: ExtendedFloatingActionButton? = null
/**
* Sheet containing filter items.
*/
private var filterSheet: SourceFilterSheet? = null
init {
setHasOptionsMenu(true)
}
override fun getTitle(): String? {
return source!!.name
}
/**
* Create the [IndexPresenter] used in controller.
*
* @return instance of [IndexPresenter]
*/
override fun createPresenter(): IndexPresenter {
return IndexPresenter(source!!)
}
/**
* Called when manga in global search is clicked, opens manga.
*
* @param manga clicked item containing manga information.
*/
override fun onMangaClick(manga: Manga) {
// Open MangaController.
router.pushController(MangaController(manga, true).withFadeTransaction())
}
/**
* Called when manga in global search is long clicked.
*
* @param manga clicked item containing manga information.
*/
override fun onMangaLongClick(manga: Manga) {
// Delegate to single click by default.
onMangaClick(manga)
}
/**
* Adds items to the options menu.
*
* @param menu menu containing options.
* @param inflater used to load the menu xml.
*/
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
createOptionsMenu(menu, inflater, R.menu.global_search, R.id.action_search)
}
override fun onSearchViewQueryTextSubmit(query: String?) {
onBrowseClick(query.nullIfBlank())
}
override fun onSearchViewQueryTextChange(newText: String?) {
if (router.backstack.lastOrNull()?.controller == this) {
presenter.query = newText ?: ""
}
}
override fun createBinding(inflater: LayoutInflater) = IndexControllerBinding.inflate(inflater)
/**
* Called when the view is created
*
* @param view view of controller
*/
override fun onViewCreated(view: View) {
super.onViewCreated(view)
// Prepare filter sheet
initFilterSheet()
latestAdapter = IndexCardAdapter(this)
binding.latestRecycler.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.HORIZONTAL, false)
binding.latestRecycler.adapter = latestAdapter
browseAdapter = IndexCardAdapter(this)
binding.browseRecycler.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.HORIZONTAL, false)
binding.browseRecycler.adapter = browseAdapter
binding.latestBarWrapper.clicks()
.onEach {
onLatestClick()
}
.launchIn(viewScope)
binding.browseBarWrapper.clicks()
.onEach {
onBrowseClick()
}
.launchIn(viewScope)
presenter.latestItems
.onEach {
bind(it, true)
}
.launchIn(viewScope)
presenter.browseItems
.onEach {
bind(it, false)
}
.launchIn(viewScope)
presenter.getLatest()
}
private val filterSerializer = FilterSerializer()
open fun initFilterSheet() {
if (presenter.sourceFilters.isEmpty()) {
actionFab?.text = activity!!.getString(R.string.saved_searches)
}
filterSheet = SourceFilterSheet(
activity!!,
// SY -->
this,
presenter.source,
presenter.loadSearches(),
// SY <--
onFilterClicked = {
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
filterSheet?.dismiss()
if (allDefault) {
onBrowseClick(
presenter.query.nullIfBlank()
)
} else {
onBrowseClick(
presenter.query.nullIfBlank(),
filters = Json.encodeToString(filterSerializer.serialize(presenter.sourceFilters))
)
}
},
onResetClicked = {},
onSaveClicked = {},
onSavedSearchClicked = cb@{ idOfSearch ->
val search = presenter.loadSearch(idOfSearch)
if (search == null) {
filterSheet?.context?.let {
MaterialAlertDialogBuilder(it)
.setTitle(R.string.save_search_failed_to_load)
.setMessage(R.string.save_search_failed_to_load_message)
.show()
}
return@cb
}
if (search.filterList == null) {
activity?.toast(R.string.save_search_invalid)
return@cb
}
presenter.sourceFilters = FilterList(search.filterList)
filterSheet?.setFilters(presenter.filterItems)
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
filterSheet?.dismiss()
if (!allDefault) {
onBrowseClick(
search = presenter.query.nullIfBlank(),
savedSearch = search.id
)
}
},
onSavedSearchDeleteClicked = { _, _ -> }
)
filterSheet?.setFilters(presenter.filterItems)
// TODO: [ExtendedFloatingActionButton] hide/show methods don't work properly
filterSheet?.setOnShowListener { actionFab?.isVisible = false }
filterSheet?.setOnDismissListener { actionFab?.isVisible = true }
actionFab?.setOnClickListener { filterSheet?.show() }
actionFab?.isVisible = true
}
override fun configureFab(fab: ExtendedFloatingActionButton) {
actionFab = fab
// Controlled by initFilterSheet()
fab.isVisible = false
fab.setText(R.string.action_filter)
fab.setIconResource(R.drawable.ic_filter_list_24dp)
}
override fun cleanupFab(fab: ExtendedFloatingActionButton) {
fab.setOnClickListener(null)
actionFab = null
}
private fun bind(results: List<IndexCardItem>?, isLatest: Boolean) {
val progress = if (isLatest) binding.latestProgress else binding.browseProgress
when {
results == null -> {
progress.isVisible = true
showResultsHolder(isLatest)
}
results.isEmpty() -> {
progress.isVisible = false
showNoResults(isLatest)
}
else -> {
progress.isVisible = false
showResultsHolder(isLatest)
}
}
val adapter = if (isLatest) {
latestAdapter
} else {
browseAdapter
}
adapter?.updateDataSet(results)
}
fun onError(e: Exception, isLatest: Boolean) {
e.message?.let {
val textView = if (isLatest) {
binding.latestNoResultsFound
} else {
binding.browseNoResultsFound
}
textView.text = it
}
}
private fun showResultsHolder(isLatest: Boolean) {
(if (isLatest) binding.latestNoResultsFound else binding.browseNoResultsFound).isVisible = false
}
private fun showNoResults(isLatest: Boolean) {
(if (isLatest) binding.latestNoResultsFound else binding.browseNoResultsFound).isVisible = true
}
override fun onDestroyView(view: View) {
latestAdapter = null
browseAdapter = null
super.onDestroyView(view)
}
fun onBrowseClick(search: String? = null, savedSearch: Long? = null, filters: String? = null) {
router.replaceTopController(BrowseSourceController(presenter.source, search, savedSearch = savedSearch, filterList = filters).withFadeTransaction())
}
private fun onLatestClick() {
router.replaceTopController(LatestUpdatesController(presenter.source).withFadeTransaction())
}
/**
* Called from the presenter when a manga is initialized.
*
* @param manga the initialized manga.
*/
fun onMangaInitialized(manga: Manga, isLatest: Boolean) {
val adapter = if (isLatest) latestAdapter else browseAdapter
adapter ?: return
adapter.allBoundViewHolders.forEach {
if (it !is IndexCardHolder) return@forEach
if (adapter.getItem(it.bindingAdapterPosition)?.manga?.id != manga.id) return@forEach
it.setImage(manga)
}
}
companion object {
const val SOURCE_EXTRA = "source"
}
}
@@ -1,274 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.source.index
import android.os.Bundle
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter.Companion.toItems
import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.logcat
import exh.log.xLogE
import exh.savedsearches.EXHSavedSearch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import logcat.LogPriority
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import xyz.nulldev.ts.api.http.serializer.FilterSerializer
import java.lang.RuntimeException
/**
* Presenter of [IndexController]
* Function calls should be done from here. UI calls should be done from the controller.
*
* @param source the source.
* @param db manages the database calls.
* @param preferences manages the preference calls.
*/
open class IndexPresenter(
val source: CatalogueSource,
val db: DatabaseHelper = Injekt.get(),
val preferences: PreferencesHelper = Injekt.get()
) : BasePresenter<IndexController>() {
/**
* Subject which fetches image of given manga.
*/
private val fetchImageFlow = MutableSharedFlow<Pair<List<Manga>, Boolean>>()
/**
* Modifiable list of filters.
*/
var sourceFilters = FilterList()
set(value) {
field = value
filterItems = value.toItems()
}
var filterItems: List<IFlexible<*>> = emptyList()
/**
* Subscription for fetching images of manga.
*/
private var fetchImageJob: Job? = null
val latestItems = MutableStateFlow<List<IndexCardItem>?>(null)
val browseItems = MutableStateFlow<List<IndexCardItem>?>(null)
init {
query = ""
}
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
sourceFilters = source.getFilterList()
}
/**
* Initiates get latest per watching source.
*/
fun getLatest() {
// Create image fetch subscription
initializeFetchImageSubscription()
presenterScope.launch(Dispatchers.IO) {
if (latestItems.value != null) return@launch
val results = if (source.supportsLatest) {
try {
source.fetchLatestUpdates(1)
.awaitSingle()
.mangas
.map { networkToLocalManga(it, source.id) }
} catch (e: Exception) {
withUIContext {
view?.onError(e, true)
}
emptyList()
}
} else emptyList()
fetchImage(results, true)
latestItems.value = results.map { IndexCardItem(it) }
}
presenterScope.launch(Dispatchers.IO) {
if (browseItems.value != null) return@launch
val results = try {
source.fetchPopularManga(1)
.awaitSingle()
.mangas
.map { networkToLocalManga(it, source.id) }
} catch (e: Exception) {
withUIContext {
view?.onError(e, true)
}
emptyList()
}
fetchImage(results, false)
browseItems.value = results.map { IndexCardItem(it) }
}
}
/**
* Initialize a list of manga.
*
* @param manga the list of manga to initialize.
*/
private fun fetchImage(manga: List<Manga>, isLatest: Boolean) {
presenterScope.launchIO {
fetchImageFlow.emit(manga to isLatest)
}
}
/**
* Subscribes to the initializer of manga details and updates the view if needed.
*/
private fun initializeFetchImageSubscription() {
fetchImageJob?.cancel()
fetchImageFlow
.flatMapConcat { (manga, isLatest) ->
manga.asFlow()
.filter { it.thumbnail_url == null && !it.initialized }
.map {
getMangaDetailsFlow(it, source, isLatest)
}
}
.buffer(Channel.RENDEZVOUS)
.flowOn(Dispatchers.IO)
.onEach { (manga, isLatest) ->
withUIContext {
view?.onMangaInitialized(manga, isLatest)
}
}
.catch {
logcat(LogPriority.ERROR, it)
}
.launchIn(presenterScope)
}
/**
* Returns an observable of manga that initializes the given manga.
*
* @param manga the manga to initialize.
* @return an observable of the manga to initialize
*/
private suspend fun getMangaDetailsFlow(manga: Manga, source: Source, isLatest: Boolean): Pair<Manga, Boolean> {
val networkManga = source.getMangaDetails(manga.toMangaInfo())
manga.copyFrom(networkManga.toSManga())
manga.initialized = true
db.insertManga(manga).executeAsBlocking()
return manga to isLatest
}
/**
* Returns a manga from the database for the given manga from network. It creates a new entry
* if the manga is not yet in the database.
*
* @param sManga the manga from the source.
* @return a manga from the database.
*/
protected open fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
if (localManga == null) {
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
newManga.copyFrom(sManga)
val result = db.insertManga(newManga).executeAsBlocking()
newManga.id = result.insertedId()
localManga = newManga
}
return localManga
}
private val filterSerializer = FilterSerializer()
fun loadSearch(searchId: Long): EXHSavedSearch? {
val search = db.getSavedSearch(searchId).executeAsBlocking() ?: return null
return EXHSavedSearch(
id = search.id!!,
name = search.name,
query = search.query.orEmpty(),
filterList = runCatching {
val originalFilters = source.getFilterList()
filterSerializer.deserialize(
filters = originalFilters,
json = search.filtersJson
?.let { Json.decodeFromString<JsonArray>(it) }
?: return@runCatching null
)
originalFilters
}.getOrNull()
)
}
fun loadSearches(): List<EXHSavedSearch> {
return db.getSavedSearches(source.id).executeAsBlocking().map {
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
id = it.id!!,
name = it.name,
query = it.query.orEmpty(),
filterList = null
)
val filters = try {
Json.decodeFromString<JsonArray>(filtersJson)
} catch (e: Exception) {
null
} ?: return@map EXHSavedSearch(
id = it.id!!,
name = it.name,
query = it.query.orEmpty(),
filterList = null
)
try {
val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, filters)
EXHSavedSearch(
id = it.id!!,
name = it.name,
query = it.query.orEmpty(),
filterList = originalFilters
)
} catch (t: RuntimeException) {
// Load failed
xLogE("Failed to load saved search!", t)
EXHSavedSearch(
id = it.id!!,
name = it.name,
query = it.query.orEmpty(),
filterList = null
)
}
}
}
}