diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueAdapter.kt deleted file mode 100644 index ff9332fd47..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueAdapter.kt +++ /dev/null @@ -1,107 +0,0 @@ -package eu.kanade.tachiyomi.ui.catalogue - -import android.view.Gravity -import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import android.widget.FrameLayout -import eu.davidea.flexibleadapter4.FlexibleAdapter -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.util.inflate -import eu.kanade.tachiyomi.widget.AutofitRecyclerView -import kotlinx.android.synthetic.main.item_catalogue_grid.view.* -import java.util.* - -/** - * Adapter storing a list of manga from the catalogue. - * - * @param fragment the fragment containing this adapter. - */ -class CatalogueAdapter(val fragment: CatalogueFragment) : FlexibleAdapter() { - - /** - * Property to get the list of manga in the adapter. - */ - val items: List - get() = mItems - - init { - mItems = ArrayList() - setHasStableIds(true) - } - - /** - * Adds a list of manga to the adapter. - * - * @param list the list to add. - */ - fun addItems(list: List) { - if (list.isNotEmpty()) { - val sizeBeforeAdding = mItems.size - mItems.addAll(list) - notifyItemRangeInserted(sizeBeforeAdding, list.size) - } - } - - /** - * Clears the list of manga from the adapter. - */ - fun clear() { - val sizeBeforeRemoving = mItems.size - mItems.clear() - notifyItemRangeRemoved(0, sizeBeforeRemoving) - } - - /** - * Returns the identifier for a manga. - * - * @param position the position in the adapter. - * @return an identifier for the item. - */ - override fun getItemId(position: Int): Long { - return mItems[position].id!! - } - - /** - * Used to filter the list. Required but not used. - */ - override fun updateDataSet(param: String) {} - - /** - * Creates a new view holder. - * - * @param parent the parent view. - * @param viewType the type of the holder. - * @return a new view holder for a manga. - */ - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CatalogueHolder { - if (parent.id == R.id.catalogue_grid) { - val view = parent.inflate(R.layout.item_catalogue_grid).apply { - card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) - gradient.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM) - } - return CatalogueGridHolder(view, this, fragment) - } else { - val view = parent.inflate(R.layout.item_catalogue_list) - return CatalogueListHolder(view, this, fragment) - } - } - - /** - * Binds a holder with a new position. - * - * @param holder the holder to bind. - * @param position the position to bind. - */ - override fun onBindViewHolder(holder: CatalogueHolder, position: Int) { - val manga = getItem(position) - holder.onSetValues(manga) - } - - /** - * Property to return the height for the covers based on the width to keep an aspect ratio. - */ - val coverHeight: Int - get() = (fragment.recycler as AutofitRecyclerView).itemWidth / 3 * 4 - -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt index 03425fae92..1f48e966d0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt @@ -11,11 +11,12 @@ import android.widget.ProgressBar import android.widget.Spinner import com.afollestad.materialdialogs.MaterialDialog import com.f2prateek.rx.preferences.Preference +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.source.model.FilterList import eu.kanade.tachiyomi.data.source.online.LoginSource -import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaActivity @@ -24,7 +25,6 @@ import eu.kanade.tachiyomi.util.inflate import eu.kanade.tachiyomi.util.snack import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.widget.AutofitRecyclerView -import eu.kanade.tachiyomi.widget.EndlessScrollListener import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_catalogue.* @@ -40,7 +40,10 @@ import java.util.concurrent.TimeUnit.MILLISECONDS * Uses R.layout.fragment_catalogue. */ @RequiresPresenter(CataloguePresenter::class) -open class CatalogueFragment : BaseRxFragment(), FlexibleViewHolder.OnListItemClickListener { +open class CatalogueFragment : BaseRxFragment(), + FlexibleAdapter.OnItemClickListener, + FlexibleAdapter.OnItemLongClickListener, + FlexibleAdapter.EndlessScrollListener { /** * Spinner shown in the toolbar to change the selected source. @@ -50,12 +53,7 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie /** * Adapter containing the list of manga from the catalogue. */ - private lateinit var adapter: CatalogueAdapter - - /** - * Scroll listener. It loads next pages when the end of the list is reached. - */ - private var scrollListener: EndlessScrollListener? = null + private lateinit var adapter: FlexibleAdapter> /** * Query of the search box. @@ -130,6 +128,8 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie lateinit var recycler: RecyclerView + private var progressItem: ProgressItem? = null + companion object { /** * Creates a new instance of this fragment. @@ -160,7 +160,7 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie } // Initialize adapter, scroll listener and recycler views - adapter = CatalogueAdapter(this) + adapter = FlexibleAdapter(null, this) setupRecycler() // Create toolbar spinner @@ -251,11 +251,18 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie .skip(1) // Set again the adapter to recalculate the covers height .subscribe { adapter = this@CatalogueFragment.adapter } + + (layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return when (adapter.getItemViewType(position)) { + R.layout.item_catalogue_grid -> 1 + else -> spanCount + } + } + } } } - scrollListener = EndlessScrollListener(recycler.layoutManager as LinearLayoutManager, { requestNextPage() }) recycler.setHasFixedSize(true) - recycler.addOnScrollListener(scrollListener) recycler.adapter = adapter catalogue_view.addView(recycler, 1) @@ -376,29 +383,19 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie presenter.restartPager(newQuery) } - /** - * Requests the next page (if available). Called from scroll listeners when they reach the end. - */ - private fun requestNextPage() { - if (presenter.hasNextPage()) { - showGridProgressBar() - presenter.requestNext() - } - } - /** * Called from the presenter when the network request is received. * * @param page the current page. * @param mangas the list of manga of the page. */ - fun onAddPage(page: Int, mangas: List) { + fun onAddPage(page: Int, mangas: List) { hideProgressBar() if (page == 1) { adapter.clear() - scrollListener?.resetScroll() + resetProgressItem() } - adapter.addItems(mangas) + adapter.onLoadMoreComplete(mangas) } /** @@ -407,6 +404,7 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie * @param error the error received. */ fun onAddPageError(error: Throwable) { + adapter.onLoadMoreComplete(null) hideProgressBar() val message = if (error is NoResultsException) "No results found" else (error.message ?: "") @@ -414,12 +412,42 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie snack?.dismiss() snack = catalogue_view.snack(message, Snackbar.LENGTH_INDEFINITE) { setAction(R.string.action_retry) { - showProgressBar() + // If not the first page, show bottom progress bar. + if (adapter.mainItemCount > 0) { + val item = progressItem ?: return@setAction + adapter.addScrollableFooterWithDelay(item, 0, true) + } else { + showProgressBar() + } presenter.requestNext() } } } + /** + * Sets a new progress item and reenables the scroll listener. + */ + private fun resetProgressItem() { + progressItem = ProgressItem() + adapter.endlessTargetCount = 0 + adapter.setEndlessScrollListener(this, progressItem!!) + } + + /** + * Called by the adapter when scrolled near the bottom. + */ + override fun onLoadMore(lastPosition: Int, currentPage: Int) { + if (presenter.hasNextPage()) { + presenter.requestNext() + } else { + adapter.onLoadMoreComplete(null) + adapter.endlessTargetCount = 1 + } + } + + override fun noMoreLoad(newItemsSize: Int) { + } + /** * Called from the presenter when a manga is initialized. * @@ -433,13 +461,18 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie * Swaps the current display mode. */ fun swapDisplayMode() { + if (!isAdded) return + presenter.swapDisplayMode() val isListMode = presenter.isListMode activity.invalidateOptionsMenu() setupRecycler() if (!isListMode || !context.connectivityManager.isActiveNetworkMetered) { // Initialize mangas if going to grid view or if over wifi when going to list view - presenter.initializeMangas(adapter.items) + val mangas = (0..adapter.itemCount-1).mapNotNull { + (adapter.getItem(it) as? CatalogueItem)?.manga + } + presenter.initializeMangas(mangas) } } @@ -474,21 +507,11 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie snack = null } - /** - * Shows the progress bar at the end of the screen. - */ - private fun showGridProgressBar() { - progress_grid.visibility = ProgressBar.VISIBLE - snack?.dismiss() - snack = null - } - /** * Hides active progress bars. */ private fun hideProgressBar() { progress.visibility = ProgressBar.GONE - progress_grid.visibility = ProgressBar.GONE } /** @@ -497,10 +520,10 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie * @param position the position of the element clicked. * @return true if the item should be selected, false otherwise. */ - override fun onListItemClick(position: Int): Boolean { - val item = adapter.getItem(position) ?: return false + override fun onItemClick(position: Int): Boolean { + val item = adapter.getItem(position) as? CatalogueItem ?: return false - val intent = MangaActivity.newIntent(activity, item, true) + val intent = MangaActivity.newIntent(activity, item.manga, true) startActivity(intent) return false } @@ -510,8 +533,8 @@ open class CatalogueFragment : BaseRxFragment(), FlexibleVie * * @param position the position of the element clicked. */ - override fun onListItemLongClick(position: Int) { - val manga = adapter.getItem(position) ?: return + override fun onItemLongClick(position: Int) { + val manga = (adapter.getItem(position) as? CatalogueItem?)?.manga ?: return val textRes = if (manga.favorite) R.string.remove_from_library else R.string.add_to_library diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt index f057164e83..60cec7d01e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.catalogue import android.view.View import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy +import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.data.database.models.Manga import kotlinx.android.synthetic.main.item_catalogue_grid.view.* @@ -12,11 +13,10 @@ import kotlinx.android.synthetic.main.item_catalogue_grid.view.* * * @param view the inflated view for this holder. * @param adapter the adapter handling this holder. - * @param listener a listener to react to single tap and long tap events. * @constructor creates a new catalogue holder. */ -class CatalogueGridHolder(private val view: View, private val adapter: CatalogueAdapter, listener: OnListItemClickListener) : - CatalogueHolder(view, adapter, listener) { +class CatalogueGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) : + CatalogueHolder(view, adapter) { /** * Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueHolder.kt index e6a1c63e75..014b7904ac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueHolder.kt @@ -1,18 +1,18 @@ package eu.kanade.tachiyomi.ui.catalogue import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder /** * Generic class used to hold the displayed data of a manga in the catalogue. * * @param view the inflated view for this holder. * @param adapter the adapter handling this holder. - * @param listener a listener to react to single tap and long tap events. */ -abstract class CatalogueHolder(view: View, adapter: CatalogueAdapter, listener: OnListItemClickListener) : - FlexibleViewHolder(view, adapter, listener) { +abstract class CatalogueHolder(view: View, adapter: FlexibleAdapter<*>) : + FlexibleViewHolder(view, adapter) { /** * Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueItem.kt new file mode 100644 index 0000000000..5aa1eecd1c --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueItem.kt @@ -0,0 +1,53 @@ +package eu.kanade.tachiyomi.ui.catalogue + +import android.view.Gravity +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.FrameLayout +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 +import eu.kanade.tachiyomi.util.inflate +import eu.kanade.tachiyomi.widget.AutofitRecyclerView +import kotlinx.android.synthetic.main.item_catalogue_grid.view.* + +class CatalogueItem(val manga: Manga) : AbstractFlexibleItem() { + + override fun getLayoutRes(): Int { + return R.layout.item_catalogue_grid + } + + override fun createViewHolder(adapter: FlexibleAdapter>, inflater: LayoutInflater, parent: ViewGroup): CatalogueHolder { + if (parent is AutofitRecyclerView) { + val view = parent.inflate(R.layout.item_catalogue_grid).apply { + card.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.itemWidth / 3 * 4) + gradient.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.itemWidth / 3 * 4 / 2, Gravity.BOTTOM) + } + return CatalogueGridHolder(view, adapter) + } else { + val view = parent.inflate(R.layout.item_catalogue_list) + return CatalogueListHolder(view, adapter) + } + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: CatalogueHolder, position: Int, payloads: List?) { + holder.onSetValues(manga) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other is CatalogueItem) { + return manga.id!! == other.manga.id!! + } + return false + } + + override fun hashCode(): Int { + return manga.id!!.hashCode() + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt index 65f4f67126..9f98786b09 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.catalogue import android.view.View import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy +import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.util.getResourceColor import kotlinx.android.synthetic.main.item_catalogue_list.view.* @@ -13,11 +14,10 @@ import kotlinx.android.synthetic.main.item_catalogue_list.view.* * * @param view the inflated view for this holder. * @param adapter the adapter handling this holder. - * @param listener a listener to react to single tap and long tap events. * @constructor creates a new catalogue holder. */ -class CatalogueListHolder(private val view: View, adapter: CatalogueAdapter, listener: OnListItemClickListener) : - CatalogueHolder(view, adapter, listener) { +class CatalogueListHolder(private val view: View, adapter: FlexibleAdapter<*>) : + CatalogueHolder(view, adapter) { private val favoriteColor = view.context.getResourceColor(android.R.attr.textColorHint) private val unfavoriteColor = view.context.getResourceColor(android.R.attr.textColorPrimary) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt index e796a048e3..a77d745aea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt @@ -163,6 +163,7 @@ open class CataloguePresenter : BasePresenter() { .observeOn(Schedulers.io()) .map { it.first to it.second.map { networkToLocalManga(it, sourceId) } } .doOnNext { initializeMangas(it.second) } + .map { it.first to it.second.map(::CatalogueItem) } .observeOn(AndroidSchedulers.mainThread()) .subscribeReplay({ view, pair -> view.onAddPage(pair.first, pair.second) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/ProgressItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/ProgressItem.kt new file mode 100644 index 0000000000..7d1d8cfbea --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/ProgressItem.kt @@ -0,0 +1,52 @@ +package eu.kanade.tachiyomi.ui.catalogue + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ProgressBar +import android.widget.TextView +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import eu.kanade.tachiyomi.R + + +class ProgressItem : AbstractFlexibleItem() { + + var loadMore = true + + override fun getLayoutRes(): Int { + return R.layout.progress_item + } + + override fun createViewHolder(adapter: FlexibleAdapter>, inflater: LayoutInflater, parent: ViewGroup): Holder { + return Holder(inflater.inflate(layoutRes, parent, false), adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List) { + holder.progressBar.visibility = View.GONE + holder.progressMessage.visibility = View.GONE + + if (!adapter.isEndlessScrollEnabled) { + loadMore = false + } + + if (loadMore) { + holder.progressBar.visibility = View.VISIBLE + } else { + holder.progressMessage.visibility = View.VISIBLE + } + } + + override fun equals(other: Any?): Boolean { + return this === other + } + + class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { + + val progressBar = view.findViewById(R.id.progress_bar) as ProgressBar + val progressMessage = view.findViewById(R.id.progress_message) as TextView + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/EndlessScrollListener.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/EndlessScrollListener.kt deleted file mode 100644 index 55e81c6d30..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/EndlessScrollListener.kt +++ /dev/null @@ -1,46 +0,0 @@ -package eu.kanade.tachiyomi.widget - -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView - -class EndlessScrollListener( - private val layoutManager: LinearLayoutManager, - private val requestNext: () -> Unit) -: RecyclerView.OnScrollListener() { - - companion object { - // The minimum amount of items to have below your current scroll position before loading - // more. - private val VISIBLE_THRESHOLD = 5 - } - - private var previousTotal = 0 // The total number of items in the dataset after the last load - private var loading = true // True if we are still waiting for the last set of data to load. - private var firstVisibleItem = 0 - private var visibleItemCount = 0 - private var totalItemCount = 0 - - fun resetScroll() { - previousTotal = 0 - loading = true - } - - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - - visibleItemCount = recyclerView.childCount - totalItemCount = layoutManager.itemCount - firstVisibleItem = layoutManager.findFirstVisibleItemPosition() - - if (loading && totalItemCount > previousTotal) { - loading = false - previousTotal = totalItemCount - } - if (!loading && totalItemCount - visibleItemCount <= firstVisibleItem + VISIBLE_THRESHOLD) { - // End has been reached - requestNext() - loading = true - } - } - -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_catalogue.xml b/app/src/main/res/layout/fragment_catalogue.xml index 0eb4fafaf4..92d1845d18 100644 --- a/app/src/main/res/layout/fragment_catalogue.xml +++ b/app/src/main/res/layout/fragment_catalogue.xml @@ -21,14 +21,6 @@ android:layout_gravity="center_vertical|center_horizontal" android:visibility="gone"/> - - \ No newline at end of file diff --git a/app/src/main/res/layout/progress_item.xml b/app/src/main/res/layout/progress_item.xml new file mode 100644 index 0000000000..d4976b8755 --- /dev/null +++ b/app/src/main/res/layout/progress_item.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4d14b80792..4a71c6add8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -223,6 +223,7 @@ This source requires you to log in Select a source Please enable at least one valid source + No more results This manga was removed from the database!