Update chapters list ui, add bookmark indicator
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
package org.koitharu.kotatsu.bookmarks.data
|
||||
|
||||
import androidx.room.*
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaWithTags
|
||||
|
||||
@@ -18,7 +22,7 @@ abstract class BookmarksDao {
|
||||
|
||||
@Transaction
|
||||
@Query(
|
||||
"SELECT * FROM manga JOIN bookmarks ON bookmarks.manga_id = manga.manga_id ORDER BY bookmarks.created_at"
|
||||
"SELECT * FROM manga JOIN bookmarks ON bookmarks.manga_id = manga.manga_id ORDER BY bookmarks.created_at",
|
||||
)
|
||||
abstract fun observe(): Flow<Map<MangaWithTags, List<BookmarkEntity>>>
|
||||
|
||||
@@ -29,5 +33,8 @@ abstract class BookmarksDao {
|
||||
abstract suspend fun delete(entity: BookmarkEntity)
|
||||
|
||||
@Query("DELETE FROM bookmarks WHERE manga_id = :mangaId AND page_id = :pageId")
|
||||
abstract suspend fun delete(mangaId: Long, pageId: Long)
|
||||
}
|
||||
abstract suspend fun delete(mangaId: Long, pageId: Long): Int
|
||||
|
||||
@Query("DELETE FROM bookmarks WHERE manga_id = :mangaId AND chapter_id = :chapterId AND page = :page")
|
||||
abstract suspend fun delete(mangaId: Long, chapterId: Long, page: Int): Int
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.koitharu.kotatsu.bookmarks.domain
|
||||
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
class Bookmark(
|
||||
val manga: Manga,
|
||||
@@ -27,9 +27,7 @@ class Bookmark(
|
||||
if (scroll != other.scroll) return false
|
||||
if (imageUrl != other.imageUrl) return false
|
||||
if (createdAt != other.createdAt) return false
|
||||
if (percent != other.percent) return false
|
||||
|
||||
return true
|
||||
return percent == other.percent
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@@ -43,4 +41,4 @@ class Bookmark(
|
||||
result = 31 * result + percent.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,14 @@ class BookmarksRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun removeBookmark(mangaId: Long, pageId: Long) {
|
||||
db.bookmarksDao.delete(mangaId, pageId)
|
||||
suspend fun removeBookmark(mangaId: Long, chapterId: Long, page: Int) {
|
||||
check(db.bookmarksDao.delete(mangaId, chapterId, page) != 0) {
|
||||
"Bookmark not found"
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun removeBookmark(bookmark: Bookmark) {
|
||||
removeBookmark(bookmark.manga.id, bookmark.chapterId, bookmark.page)
|
||||
}
|
||||
|
||||
suspend fun removeBookmarks(ids: Map<Manga, Set<Long>>): ReversibleHandle {
|
||||
|
||||
@@ -19,7 +19,9 @@ class BookmarksAdapter(
|
||||
private class DiffCallback : DiffUtil.ItemCallback<Bookmark>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: Bookmark, newItem: Bookmark): Boolean {
|
||||
return oldItem.manga.id == newItem.manga.id && oldItem.pageId == newItem.pageId
|
||||
return oldItem.manga.id == newItem.manga.id &&
|
||||
oldItem.chapterId == newItem.chapterId &&
|
||||
oldItem.page == newItem.page
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Bookmark, newItem: Bookmark): Boolean {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.koitharu.kotatsu.core.util.ext
|
||||
|
||||
import android.view.View
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
||||
|
||||
fun BottomSheetBehavior<*>.doOnExpansionsChanged(callback: (isExpanded: Boolean) -> Unit) {
|
||||
var isExpended = state == BottomSheetBehavior.STATE_EXPANDED
|
||||
callback(isExpended)
|
||||
addBottomSheetCallback(
|
||||
object : BottomSheetCallback() {
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
val expanded = newState == BottomSheetBehavior.STATE_EXPANDED
|
||||
if (expanded != isExpended) {
|
||||
isExpended = expanded
|
||||
callback(expanded)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -6,18 +6,25 @@ import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import org.koitharu.kotatsu.core.ui.util.ActionModeListener
|
||||
import org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar
|
||||
import org.koitharu.kotatsu.core.util.ext.doOnExpansionsChanged
|
||||
|
||||
class ChaptersBottomSheetMediator(
|
||||
bottomSheet: View,
|
||||
private val behavior: BottomSheetBehavior<*>,
|
||||
) : OnBackPressedCallback(false),
|
||||
ActionModeListener,
|
||||
BottomSheetHeaderBar.OnExpansionChangeListener,
|
||||
OnLayoutChangeListener {
|
||||
|
||||
private val behavior = BottomSheetBehavior.from(bottomSheet)
|
||||
private var lockCounter = 0
|
||||
|
||||
init {
|
||||
behavior.doOnExpansionsChanged { isExpanded ->
|
||||
isEnabled = isExpanded
|
||||
if (!isExpanded) {
|
||||
unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
@@ -30,13 +37,6 @@ class ChaptersBottomSheetMediator(
|
||||
unlock()
|
||||
}
|
||||
|
||||
override fun onExpansionStateChanged(headerBar: BottomSheetHeaderBar, isExpanded: Boolean) {
|
||||
isEnabled = isExpanded
|
||||
if (!isExpanded) {
|
||||
unlock()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayoutChange(
|
||||
v: View?,
|
||||
left: Int,
|
||||
@@ -61,6 +61,9 @@ class ChaptersBottomSheetMediator(
|
||||
|
||||
fun unlock() {
|
||||
lockCounter--
|
||||
if (lockCounter < 0) {
|
||||
lockCounter = 0
|
||||
}
|
||||
behavior.isDraggable = lockCounter <= 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.details.ui
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
@@ -56,6 +57,9 @@ class ChaptersFragment :
|
||||
checkNotNull(selectionController).attachToRecyclerView(this)
|
||||
setHasFixedSize(true)
|
||||
adapter = chaptersAdapter
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
scrollIndicators = if (resources.getBoolean(R.bool.is_tablet)) 0 else View.SCROLL_INDICATOR_TOP
|
||||
}
|
||||
}
|
||||
viewModel.isLoading.observe(viewLifecycleOwner, this::onLoadingStateChanged)
|
||||
viewModel.chapters.observe(viewLifecycleOwner, this::onChaptersChanged)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.koitharu.kotatsu.details.ui
|
||||
|
||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
||||
import org.koitharu.kotatsu.details.ui.model.toListItem
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
|
||||
fun mapChapters(
|
||||
remoteManga: Manga?,
|
||||
@@ -11,12 +13,14 @@ fun mapChapters(
|
||||
history: MangaHistory?,
|
||||
newCount: Int,
|
||||
branch: String?,
|
||||
bookmarks: List<Bookmark>,
|
||||
): List<ChapterListItem> {
|
||||
val remoteChapters = remoteManga?.getChapters(branch).orEmpty()
|
||||
val localChapters = localManga?.getChapters(branch).orEmpty()
|
||||
if (remoteChapters.isEmpty() && localChapters.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
val bookmarked = bookmarks.mapToSet { it.chapterId }
|
||||
val currentId = history?.chapterId ?: 0L
|
||||
val newFrom = if (newCount == 0 || remoteChapters.isEmpty()) Int.MAX_VALUE else remoteChapters.size - newCount
|
||||
val chaptersSize = maxOf(remoteChapters.size, localChapters.size)
|
||||
@@ -41,6 +45,7 @@ fun mapChapters(
|
||||
isUnread = isUnread,
|
||||
isNew = isUnread && result.size >= newFrom,
|
||||
isDownloaded = local != null,
|
||||
isBookmarked = chapter.id in bookmarked,
|
||||
)
|
||||
}
|
||||
if (!localMap.isNullOrEmpty()) {
|
||||
@@ -53,6 +58,7 @@ fun mapChapters(
|
||||
isUnread = isUnread,
|
||||
isNew = false,
|
||||
isDownloaded = remoteManga != null,
|
||||
isBookmarked = chapter.id in bookmarked,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,18 @@ package org.koitharu.kotatsu.details.ui
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.MenuProvider
|
||||
import org.koitharu.kotatsu.R
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class ChaptersMenuProvider(
|
||||
private val viewModel: DetailsViewModel,
|
||||
private val bottomSheetMediator: ChaptersBottomSheetMediator?,
|
||||
) : MenuProvider, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
|
||||
) : OnBackPressedCallback(false), MenuProvider, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
|
||||
|
||||
private var searchItemRef: WeakReference<MenuItem>? = null
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.opt_chapters, menu)
|
||||
@@ -20,6 +24,7 @@ class ChaptersMenuProvider(
|
||||
searchView.setOnQueryTextListener(this)
|
||||
searchView.setIconifiedByDefault(false)
|
||||
searchView.queryHint = searchMenuItem.title
|
||||
searchItemRef = WeakReference(searchMenuItem)
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
@@ -32,15 +37,22 @@ class ChaptersMenuProvider(
|
||||
viewModel.setChaptersReversed(!menuItem.isChecked)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
searchItemRef?.get()?.collapseActionView()
|
||||
}
|
||||
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
bottomSheetMediator?.lock()
|
||||
isEnabled = true
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
isEnabled = false
|
||||
(item.actionView as? SearchView)?.setQuery("", false)
|
||||
viewModel.performChapterSearch(null)
|
||||
bottomSheetMediator?.unlock()
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.transition.AutoTransition
|
||||
import android.transition.Slide
|
||||
import android.transition.TransitionManager
|
||||
import android.view.Gravity
|
||||
@@ -18,6 +19,7 @@ import androidx.core.graphics.Insets
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -31,8 +33,11 @@ import org.koitharu.kotatsu.core.parser.MangaIntent
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.dialog.RecyclerViewAlertDialog
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar
|
||||
import org.koitharu.kotatsu.core.util.ViewBadge
|
||||
import org.koitharu.kotatsu.core.util.ext.doOnExpansionsChanged
|
||||
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
|
||||
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
|
||||
import org.koitharu.kotatsu.core.util.ext.measureHeight
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.setNavigationBarTransparentCompat
|
||||
@@ -49,19 +54,16 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet
|
||||
import javax.inject.Inject
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DetailsActivity :
|
||||
BaseActivity<ActivityDetailsBinding>(),
|
||||
View.OnClickListener,
|
||||
BottomSheetHeaderBar.OnExpansionChangeListener,
|
||||
NoModalBottomSheetOwner,
|
||||
View.OnLongClickListener,
|
||||
PopupMenu.OnMenuItemClickListener {
|
||||
|
||||
override val bsHeader: BottomSheetHeaderBar?
|
||||
get() = viewBinding.headerChapters
|
||||
|
||||
@Inject
|
||||
lateinit var shortcutsUpdater: ShortcutsUpdater
|
||||
|
||||
@@ -82,16 +84,22 @@ class DetailsActivity :
|
||||
viewBinding.buttonDropdown.setOnClickListener(this)
|
||||
viewBadge = ViewBadge(viewBinding.buttonRead, this)
|
||||
|
||||
chaptersMenuProvider = if (viewBinding.layoutBottom != null) {
|
||||
val bsMediator = ChaptersBottomSheetMediator(checkNotNull(viewBinding.layoutBottom))
|
||||
if (viewBinding.layoutBottom != null) {
|
||||
val behavior = BottomSheetBehavior.from(checkNotNull(viewBinding.layoutBottom))
|
||||
val bsMediator = ChaptersBottomSheetMediator(behavior)
|
||||
actionModeDelegate.addListener(bsMediator)
|
||||
checkNotNull(viewBinding.headerChapters).addOnExpansionChangeListener(bsMediator)
|
||||
checkNotNull(viewBinding.headerChapters).addOnLayoutChangeListener(bsMediator)
|
||||
checkNotNull(viewBinding.layoutBsHeader).addOnLayoutChangeListener(bsMediator)
|
||||
onBackPressedDispatcher.addCallback(bsMediator)
|
||||
ChaptersMenuProvider(viewModel, bsMediator)
|
||||
chaptersMenuProvider = ChaptersMenuProvider(viewModel, bsMediator)
|
||||
behavior.doOnExpansionsChanged(::onChaptersSheetStateChanged)
|
||||
viewBinding.toolbarChapters?.setNavigationOnClickListener {
|
||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
} else {
|
||||
ChaptersMenuProvider(viewModel, null)
|
||||
chaptersMenuProvider = ChaptersMenuProvider(viewModel, null)
|
||||
addMenuProvider(chaptersMenuProvider)
|
||||
}
|
||||
onBackPressedDispatcher.addCallback(chaptersMenuProvider)
|
||||
|
||||
viewModel.manga.filterNotNull().observe(this, ::onMangaUpdated)
|
||||
viewModel.newChaptersCount.observe(this, ::onNewChaptersChanged)
|
||||
@@ -114,11 +122,11 @@ class DetailsActivity :
|
||||
}
|
||||
viewModel.historyInfo.observe(this, ::onHistoryChanged)
|
||||
viewModel.selectedBranch.observe(this) {
|
||||
viewBinding.headerChapters?.subtitle = it
|
||||
viewBinding.toolbarChapters?.subtitle = it
|
||||
viewBinding.textViewSubtitle?.textAndVisible = it
|
||||
}
|
||||
viewModel.isChaptersReversed.observe(this) {
|
||||
viewBinding.headerChapters?.invalidateMenu() ?: invalidateOptionsMenu()
|
||||
viewBinding.toolbarChapters?.invalidateMenu() ?: invalidateOptionsMenu()
|
||||
}
|
||||
viewModel.favouriteCategories.observe(this) {
|
||||
invalidateOptionsMenu()
|
||||
@@ -137,7 +145,10 @@ class DetailsActivity :
|
||||
shortcutsUpdater = shortcutsUpdater,
|
||||
),
|
||||
)
|
||||
viewBinding.headerChapters?.addOnExpansionChangeListener(this) ?: addMenuProvider(chaptersMenuProvider)
|
||||
}
|
||||
|
||||
override fun getBottomSheetCollapsedHeight(): Int {
|
||||
return viewBinding.layoutBsHeader?.measureHeight() ?: 0
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
@@ -184,11 +195,19 @@ class DetailsActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun onExpansionStateChanged(headerBar: BottomSheetHeaderBar, isExpanded: Boolean) {
|
||||
private fun onChaptersSheetStateChanged(isExpanded: Boolean) {
|
||||
val toolbar = viewBinding.toolbarChapters ?: return
|
||||
if (isAnimationsEnabled) {
|
||||
val transition = AutoTransition()
|
||||
transition.duration = getAnimationDuration(R.integer.config_tinyAnimTime)
|
||||
TransitionManager.beginDelayedTransition(toolbar, transition)
|
||||
}
|
||||
if (isExpanded) {
|
||||
headerBar.addMenuProvider(chaptersMenuProvider)
|
||||
toolbar.addMenuProvider(chaptersMenuProvider)
|
||||
toolbar.setNavigationIcon(materialR.drawable.abc_ic_clear_material)
|
||||
} else {
|
||||
headerBar.removeMenuProvider(chaptersMenuProvider)
|
||||
toolbar.removeMenuProvider(chaptersMenuProvider)
|
||||
toolbar.navigationIcon = null
|
||||
}
|
||||
viewBinding.buttonRead.isGone = isExpanded
|
||||
}
|
||||
@@ -237,7 +256,7 @@ class DetailsActivity :
|
||||
info.totalChapters == 0 -> getString(R.string.no_chapters)
|
||||
else -> resources.getQuantityString(R.plurals.chapters, info.totalChapters, info.totalChapters)
|
||||
}
|
||||
viewBinding.headerChapters?.title = text
|
||||
viewBinding.toolbarChapters?.title = text
|
||||
viewBinding.textViewTitle?.text = text
|
||||
}
|
||||
|
||||
@@ -282,8 +301,6 @@ class DetailsActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun isTabletLayout() = viewBinding.layoutBottom == null
|
||||
|
||||
private fun showBottomSheet(isVisible: Boolean) {
|
||||
val view = viewBinding.layoutBottom ?: return
|
||||
if (view.isVisible == isVisible) return
|
||||
@@ -297,7 +314,7 @@ class DetailsActivity :
|
||||
private fun makeSnackbar(text: CharSequence, @BaseTransientBottomBar.Duration duration: Int): Snackbar {
|
||||
val sb = Snackbar.make(viewBinding.containerDetails, text, duration)
|
||||
if (viewBinding.layoutBottom?.isVisible == true) {
|
||||
sb.anchorView = viewBinding.headerChapters
|
||||
sb.anchorView = viewBinding.toolbarChapters
|
||||
}
|
||||
return sb
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.koitharu.kotatsu.core.util.ext.crossfade
|
||||
import org.koitharu.kotatsu.core.util.ext.drawableTop
|
||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty
|
||||
import org.koitharu.kotatsu.core.util.ext.measureHeight
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
||||
import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf
|
||||
@@ -271,7 +270,7 @@ class DetailsFragment :
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
requireViewBinding().root.updatePadding(
|
||||
bottom = (
|
||||
(activity as? NoModalBottomSheetOwner)?.bsHeader?.measureHeight()
|
||||
(activity as? NoModalBottomSheetOwner)?.getBottomSheetCollapsedHeight()
|
||||
?.plus(insets.bottom)?.plus(resources.resolveDp(16))
|
||||
)
|
||||
?: insets.bottom,
|
||||
|
||||
@@ -171,8 +171,9 @@ class DetailsViewModel @Inject constructor(
|
||||
history,
|
||||
selectedBranch,
|
||||
newChaptersCount,
|
||||
) { manga, history, branch, news ->
|
||||
mapChapters(manga?.remote, manga?.local, history, news, branch)
|
||||
bookmarks,
|
||||
) { manga, history, branch, news, bookmarks ->
|
||||
mapChapters(manga?.remote, manga?.local, history, news, branch, bookmarks)
|
||||
},
|
||||
isChaptersReversed,
|
||||
chaptersQuery,
|
||||
@@ -209,8 +210,8 @@ class DetailsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun removeBookmark(bookmark: Bookmark) {
|
||||
launchJob {
|
||||
bookmarksRepository.removeBookmark(bookmark.manga.id, bookmark.pageId)
|
||||
launchJob(Dispatchers.Default) {
|
||||
bookmarksRepository.removeBookmark(bookmark)
|
||||
onShowToast.call(R.string.bookmark_removed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package org.koitharu.kotatsu.details.ui.adapter
|
||||
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.util.ext.drawableStart
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
||||
import org.koitharu.kotatsu.databinding.ItemChapterBinding
|
||||
@@ -43,7 +45,13 @@ fun chapterListItemAD(
|
||||
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorTertiary))
|
||||
}
|
||||
}
|
||||
binding.imageViewBookmarked.isVisible = item.isBookmarked
|
||||
binding.imageViewDownloaded.isVisible = item.isDownloaded
|
||||
binding.imageViewNew.isVisible = item.isNew
|
||||
// binding.imageViewNew.isVisible = item.isNew
|
||||
binding.textViewTitle.drawableStart = if (item.isNew) {
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_new)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ class ChapterListItem(
|
||||
val isDownloaded: Boolean
|
||||
get() = hasFlag(FLAG_DOWNLOADED)
|
||||
|
||||
val isBookmarked: Boolean
|
||||
get() = hasFlag(FLAG_BOOKMARKED)
|
||||
|
||||
val isNew: Boolean
|
||||
get() = hasFlag(FLAG_NEW)
|
||||
|
||||
@@ -70,6 +73,7 @@ class ChapterListItem(
|
||||
const val FLAG_UNREAD = 2
|
||||
const val FLAG_CURRENT = 4
|
||||
const val FLAG_NEW = 8
|
||||
const val FLAG_BOOKMARKED = 16
|
||||
const val FLAG_DOWNLOADED = 32
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.details.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_BOOKMARKED
|
||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_CURRENT
|
||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_DOWNLOADED
|
||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_NEW
|
||||
@@ -11,11 +12,13 @@ fun MangaChapter.toListItem(
|
||||
isUnread: Boolean,
|
||||
isNew: Boolean,
|
||||
isDownloaded: Boolean,
|
||||
isBookmarked: Boolean,
|
||||
): ChapterListItem {
|
||||
var flags = 0
|
||||
if (isCurrent) flags = flags or FLAG_CURRENT
|
||||
if (isUnread) flags = flags or FLAG_UNREAD
|
||||
if (isNew) flags = flags or FLAG_NEW
|
||||
if (isBookmarked) flags = flags or FLAG_BOOKMARKED
|
||||
if (isDownloaded) flags = flags or FLAG_DOWNLOADED
|
||||
return ChapterListItem(
|
||||
chapter = this,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.koitharu.kotatsu.main.ui.owners
|
||||
|
||||
import org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar
|
||||
|
||||
interface NoModalBottomSheetOwner {
|
||||
|
||||
val bsHeader: BottomSheetHeaderBar?
|
||||
fun getBottomSheetCollapsedHeight(): Int
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ class ChaptersSheet : BaseAdaptiveSheet<SheetChaptersBinding>(), OnListItemClick
|
||||
isUnread = index > currentPosition,
|
||||
isNew = false,
|
||||
isDownloaded = false,
|
||||
isBookmarked = false,
|
||||
)
|
||||
}
|
||||
binding.recyclerView.adapter = ChaptersAdapter(this).also { adapter ->
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty
|
||||
import org.koitharu.kotatsu.core.util.ext.requireValue
|
||||
import org.koitharu.kotatsu.details.domain.DoubleMangaLoadUseCase
|
||||
import org.koitharu.kotatsu.details.domain.model.DoubleManga
|
||||
@@ -140,7 +141,9 @@ class ReaderViewModel @Inject constructor(
|
||||
flowOf(false)
|
||||
} else {
|
||||
bookmarksRepository.observeBookmark(manga, state.chapterId, state.page)
|
||||
.map { it != null }
|
||||
.map {
|
||||
it != null && it.chapterId == state.chapterId && it.page == state.page
|
||||
}
|
||||
}
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, false)
|
||||
|
||||
@@ -285,7 +288,7 @@ class ReaderViewModel @Inject constructor(
|
||||
chapterId = state.chapterId,
|
||||
page = state.page,
|
||||
scroll = state.scroll,
|
||||
imageUrl = page.preview ?: pageLoader.getPageUrl(page),
|
||||
imageUrl = page.preview.ifNullOrEmpty { pageLoader.getPageUrl(page) },
|
||||
createdAt = Date(),
|
||||
percent = computePercent(state.chapterId, state.page),
|
||||
)
|
||||
@@ -301,8 +304,8 @@ class ReaderViewModel @Inject constructor(
|
||||
bookmarkJob = launchJob {
|
||||
loadingJob?.join()
|
||||
val manga = checkNotNull(mangaData.value?.any)
|
||||
val page = checkNotNull(getCurrentPage()) { "Page not found" }
|
||||
bookmarksRepository.removeBookmark(manga.id, page.id)
|
||||
val state = checkNotNull(getCurrentState())
|
||||
bookmarksRepository.removeBookmark(manga.id, state.chapterId, state.page)
|
||||
onShowToast.call(R.string.bookmark_removed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?colorControlNormal"
|
||||
android:tint="?colorError"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
|
||||
@@ -48,38 +48,51 @@
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.BottomSheet"
|
||||
tools:visibility="visible">
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar
|
||||
android:id="@+id/header_chapters"
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_bs_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
|
||||
app:fitStatusBar="true"
|
||||
tools:menu="@menu/opt_chapters">
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/button_dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
|
||||
android:id="@+id/dragHandle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_chapters"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:src="@drawable/ic_expand_more" />
|
||||
android:theme="@style/ThemeOverlay.Kotatsu.MainToolbar"
|
||||
tools:menu="@menu/opt_chapters">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_read"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
|
||||
android:enabled="false"
|
||||
android:text="@string/read"
|
||||
android:textAllCaps="false"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="8dp"
|
||||
tools:enabled="true"
|
||||
tools:icon="@drawable/ic_read" />
|
||||
<ImageView
|
||||
android:id="@+id/button_dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/translations"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:src="@drawable/ic_expand_more" />
|
||||
|
||||
</org.koitharu.kotatsu.core.ui.widgets.BottomSheetHeaderBar>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_read"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginHorizontal="@dimen/toolbar_button_margin"
|
||||
android:enabled="false"
|
||||
android:text="@string/read"
|
||||
android:textAllCaps="false"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="8dp"
|
||||
tools:enabled="true"
|
||||
tools:icon="@drawable/ic_read" />
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/container_chapters"
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
android:paddingRight="@dimen/list_spacing"
|
||||
android:paddingBottom="@dimen/grid_spacing_outer"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:trackColor="?attr/colorOutline"
|
||||
tools:listitem="@layout/item_feed" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="4dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
||||
@@ -55,18 +56,19 @@
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_new"
|
||||
android:id="@+id/imageView_bookmarked"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:src="@drawable/ic_new"
|
||||
app:tint="?colorError" />
|
||||
android:contentDescription="@string/bookmarks"
|
||||
app:srcCompat="@drawable/ic_bookmark" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_downloaded"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:src="@drawable/ic_save_ok" />
|
||||
android:contentDescription="@string/downloaded"
|
||||
app:srcCompat="@drawable/ic_save_ok" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
android:scrollIndicators="top"
|
||||
app:bubbleSize="normal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:scrollerOffset="6dp"
|
||||
app:trackColor="?attr/colorOutline"
|
||||
tools:ignore="UnusedAttribute"
|
||||
tools:listitem="@layout/item_checkable_new" />
|
||||
</FrameLayout>
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
app:bubbleSize="small"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="3"
|
||||
app:trackColor="?attr/colorOutline"
|
||||
tools:listitem="@layout/item_page_thumb"
|
||||
tools:targetApi="m" />
|
||||
|
||||
|
||||
@@ -425,4 +425,5 @@
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="invalid_value_message">Invalid value</string>
|
||||
<string name="manga_branch_title_template">%1$s (%2$s)</string>
|
||||
<string name="downloaded">Downloaded</string>
|
||||
</resources>
|
||||
|
||||
@@ -133,6 +133,7 @@
|
||||
<item name="bubbleTextColor">?colorOnTertiary</item>
|
||||
<item name="trackColor">?colorOutline</item>
|
||||
<item name="bubbleSize">normal</item>
|
||||
<item name="scrollerOffset">6dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Kotatsu.ListItemTextView" parent="">
|
||||
|
||||
Reference in New Issue
Block a user