From ac935eb20328017bc9eb4b5bf36c6ab68bc733cd Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sun, 23 Feb 2020 14:43:40 +0200 Subject: [PATCH] Update history fragment --- .../koitharu/kotatsu/core/db/HistoryDao.kt | 3 ++ .../domain/history/HistoryRepository.kt | 5 ++ .../kotatsu/ui/main/list/MangaListFragment.kt | 49 +++++++++++++++---- .../kotatsu/ui/main/list/MangaListView.kt | 3 ++ .../list/favourites/FavouritesListFragment.kt | 1 + .../main/list/history/HistoryListFragment.kt | 49 ++++++++++++++++--- .../main/list/history/HistoryListPresenter.kt | 16 ++++++ .../koitharu/kotatsu/utils/ext/StringExt.kt | 6 ++- app/src/main/res/menu/popup_history.xml | 9 ++++ app/src/main/res/values/strings.xml | 4 ++ 10 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 app/src/main/res/menu/popup_history.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt index 1706d2ef5..7c21e8828 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt @@ -27,6 +27,9 @@ abstract class HistoryDao { @Query("UPDATE history SET page = :page, chapter_id = :chapterId, updated_at = :updatedAt WHERE manga_id = :mangaId") abstract suspend fun update(mangaId: Long, page: Int, chapterId: Long, updatedAt: Long): Int + @Query("DELETE FROM history WHERE manga_id = :mangaId") + abstract suspend fun delete(mangaId: Long) + suspend fun update(entity: HistoryEntity) = update(entity.mangaId, entity.page, entity.chapterId, entity.updatedAt) @Transaction diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/history/HistoryRepository.kt b/app/src/main/java/org/koitharu/kotatsu/domain/history/HistoryRepository.kt index d891534b3..009445bdb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/history/HistoryRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/history/HistoryRepository.kt @@ -53,6 +53,11 @@ class HistoryRepository : KoinComponent { notifyHistoryChanged() } + suspend fun delete(manga: Manga) { + db.historyDao().delete(manga.id) + notifyHistoryChanged() + } + companion object { private val listeners = HashSet() diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt index 60a055176..e950b2067 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt @@ -2,11 +2,9 @@ package org.koitharu.kotatsu.ui.main.list import android.content.SharedPreferences import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View +import android.view.* import androidx.annotation.CallSuper +import androidx.appcompat.widget.PopupMenu import androidx.core.view.GravityCompat import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout @@ -42,7 +40,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang private val settings by inject() - private lateinit var adapter: MangaListAdapter + private var adapter: MangaListAdapter? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -65,6 +63,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang } override fun onDestroyView() { + adapter = null settings.unsubscribe(this) super.onDestroyView() } @@ -103,8 +102,23 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang startActivity(MangaDetailsActivity.newIntent(context ?: return, item)) } + override fun onItemLongClick(item: Manga, position: Int, view: View): Boolean { + val menu = PopupMenu(context ?: return false, view) + onCreatePopupMenu(menu.menuInflater, menu.menu, item) + return if (menu.menu.hasVisibleItems()) { + menu.setOnMenuItemClickListener { + onPopupMenuItemSelected(it, item) + } + menu.gravity = GravityCompat.END or Gravity.TOP + menu.show() + true + } else { + false + } + } + override fun onListChanged(list: List) { - adapter.replaceData(list) + adapter?.replaceData(list) if (list.isEmpty()) { setUpEmptyListHolder() layout_holder.isVisible = true @@ -114,7 +128,20 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang } override fun onListAppended(list: List) { - adapter.appendData(list) + adapter?.appendData(list) + } + + @CallSuper + override fun onItemRemoved(item: Manga) { + adapter?.let { + it.removeItem(item) + if (it.itemCount == 0) { + setUpEmptyListHolder() + layout_holder.isVisible = true + } else { + layout_holder.isVisible = false + } + } } override fun onError(e: Exception) { @@ -181,7 +208,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang recyclerView.adapter = null recyclerView.layoutManager = null recyclerView.clearItemDecorations() - adapter.listMode = mode + adapter?.listMode = mode recyclerView.layoutManager = when (mode) { ListMode.GRID -> GridLayoutManager(ctx, 3) else -> LinearLayoutManager(ctx) @@ -196,7 +223,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang ) } ) - adapter.notifyDataSetChanged() + adapter?.notifyDataSetChanged() recyclerView.firstItem = position } @@ -213,4 +240,8 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang else -> null } } + + protected open fun onCreatePopupMenu(inflater: MenuInflater, menu: Menu, data: Manga) = Unit + + protected open fun onPopupMenuItemSelected(item: MenuItem, data: Manga) = false } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListView.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListView.kt index 6cf737756..634e3b950 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListView.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListView.kt @@ -23,4 +23,7 @@ interface MangaListView : MvpView { @StateStrategyType(AddToEndSingleStrategy::class) fun onInitFilter(sortOrders: List, tags: List, currentFilter: MangaFilter?) + + @StateStrategyType(AddToEndStrategy::class) + fun onItemRemoved(item: Manga) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListFragment.kt index 69b093706..3b82bc2ba 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListFragment.kt @@ -6,6 +6,7 @@ import android.view.MenuItem import kotlinx.android.synthetic.main.fragment_list.* import moxy.ktx.moxyPresenter import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.ui.main.list.MangaListFragment import org.koitharu.kotatsu.ui.main.list.MangaListView diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/history/HistoryListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/history/HistoryListFragment.kt index 6b1b03b97..16b619772 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/history/HistoryListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/history/HistoryListFragment.kt @@ -3,14 +3,18 @@ package org.koitharu.kotatsu.ui.main.list.history import android.view.Menu import android.view.MenuInflater import android.view.MenuItem +import androidx.appcompat.app.AlertDialog +import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.fragment_list.* import moxy.ktx.moxyPresenter import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.MangaHistory import org.koitharu.kotatsu.ui.main.list.MangaListFragment import org.koitharu.kotatsu.ui.main.list.MangaListView +import org.koitharu.kotatsu.utils.ext.ellipsize -class HistoryListFragment : MangaListFragment(), MangaListView{ +class HistoryListFragment : MangaListFragment(), MangaListView { private val presenter by moxyPresenter(factory = ::HistoryListPresenter) @@ -23,12 +27,20 @@ class HistoryListFragment : MangaListFragment(), MangaListView { - presenter.clearHistory() - true + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.action_clear_history -> { + AlertDialog.Builder(context ?: return false) + .setTitle(R.string.clear_history) + .setMessage(R.string.text_clear_history_prompt) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.clear) { _, _ -> + presenter.clearHistory() + }.show() + true + } + else -> super.onOptionsItemSelected(item) } - else -> super.onOptionsItemSelected(item) } override fun getTitle(): CharSequence? { @@ -40,6 +52,31 @@ class HistoryListFragment : MangaListFragment(), MangaListView { + presenter.removeFromHistory(data) + true + } + else -> super.onPopupMenuItemSelected(item, data) + } + } + + override fun onItemRemoved(item: Manga) { + super.onItemRemoved(item) + Snackbar.make( + recyclerView, getString( + R.string._s_removed_from_history, + item.title.ellipsize(14) + ), Snackbar.LENGTH_SHORT + ).show() + } + companion object { fun newInstance() = HistoryListFragment() diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/history/HistoryListPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/history/HistoryListPresenter.kt index 17ac40062..ef2d166a1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/history/HistoryListPresenter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/history/HistoryListPresenter.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import moxy.InjectViewState import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.MangaHistory import org.koitharu.kotatsu.domain.history.HistoryRepository import org.koitharu.kotatsu.ui.common.BasePresenter @@ -61,4 +62,19 @@ class HistoryListPresenter : BasePresenter>() { } } } + + fun removeFromHistory(manga: Manga) { + launch { + try { + withContext(Dispatchers.IO) { + repository.delete(manga) + } + viewState.onItemRemoved(manga) + } catch (e: Exception) { + if (BuildConfig.DEBUG) { + e.printStackTrace() + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt index 84a2d3442..52aa43a7b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt @@ -57,4 +57,8 @@ fun String.transliterate(skipMissing: Boolean): String { fun String.toFileName() = this.transliterate(false) .replace(Regex("[^a-z0-9_\\-]", setOf(RegexOption.IGNORE_CASE)), " ") - .replace(Regex("\\s+"), "_") \ No newline at end of file + .replace(Regex("\\s+"), "_") + +fun String.ellipsize(maxLength: Int) = if (this.length > maxLength) { + this.take(maxLength - 1) + Typography.ellipsis +} else this \ No newline at end of file diff --git a/app/src/main/res/menu/popup_history.xml b/app/src/main/res/menu/popup_history.xml new file mode 100644 index 000000000..42eebe5fb --- /dev/null +++ b/app/src/main/res/menu/popup_history.xml @@ -0,0 +1,9 @@ + + + + + + \ 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 6397cf6e9..d7ec45a51 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,4 +61,8 @@ Dark Automatic Pages + Clear + Are you rally want to clear all your reading history? This action cannot be undone. + Remove + \"%s\" removed from history \ No newline at end of file