diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/sheet/BookmarksSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/sheet/BookmarksSheet.kt index 4fd65ebcd..6b4fb8490 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/sheet/BookmarksSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/sheet/BookmarksSheet.kt @@ -37,6 +37,7 @@ import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail import javax.inject.Inject import kotlin.math.roundToInt +@Deprecated("") @AndroidEntryPoint class BookmarksSheet : BaseAdaptiveSheet(), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/sheet/BookmarksSheetViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/sheet/BookmarksSheetViewModel.kt index 5959bd7cf..5a6c5d45a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/sheet/BookmarksSheetViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/sheet/BookmarksSheetViewModel.kt @@ -21,6 +21,7 @@ import org.koitharu.kotatsu.list.ui.model.LoadingFooter import org.koitharu.kotatsu.parsers.util.SuspendLazy import javax.inject.Inject +@Deprecated("") @HiltViewModel class BookmarksSheetViewModel @Inject constructor( savedStateHandle: SavedStateHandle, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/sheet/BaseAdaptiveSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/sheet/BaseAdaptiveSheet.kt index 7e15d2344..3d693da2b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/sheet/BaseAdaptiveSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/sheet/BaseAdaptiveSheet.kt @@ -54,6 +54,10 @@ abstract class BaseAdaptiveSheet : AppCompatDialogFragment() { val onBackPressedDispatcher: OnBackPressedDispatcher get() = requireComponentDialog().onBackPressedDispatcher + var isLocked = false + private set + private var lockCounter = 0 + final override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -138,6 +142,10 @@ abstract class BaseAdaptiveSheet : AppCompatDialogFragment() { } protected fun setExpanded(isExpanded: Boolean, isLocked: Boolean) { + this.isLocked = isLocked + if (!isLocked) { + lockCounter = 0 + } val b = behavior ?: return if (isExpanded) { b.state = BottomSheetBehavior.STATE_EXPANDED @@ -165,6 +173,20 @@ abstract class BaseAdaptiveSheet : AppCompatDialogFragment() { } } + @CallSuper + open fun expandAndLock() { + lockCounter++ + setExpanded(isExpanded = true, isLocked = true) + } + + @CallSuper + open fun unlock() { + lockCounter-- + if (lockCounter <= 0) { + setExpanded(isExpanded, false) + } + } + fun requireViewBinding(): B = checkNotNull(viewBinding) { "Fragment $this did not return a ViewBinding from onCreateView() or this was called before onCreateView()." } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Coil.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Coil.kt index f3c729700..c64f5c97f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Coil.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Coil.kt @@ -25,7 +25,7 @@ fun ImageView.newImageRequest(lifecycleOwner: LifecycleOwner, data: Any?): Image } // disposeImageRequest() return ImageRequest.Builder(context) - .data(data?.takeUnless { it == "" }) + .data(data?.takeUnless { it == "" || it == 0 }) .lifecycle(lifecycleOwner) .crossfade(context) .target(this) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMenuProvider2.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMenuProvider2.kt new file mode 100644 index 000000000..544302fc2 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ChaptersMenuProvider2.kt @@ -0,0 +1,75 @@ +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 org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet +import java.lang.ref.WeakReference + +class ChaptersMenuProvider2( + private val viewModel: DetailsViewModel, + private val sheet: BaseAdaptiveSheet<*>, +) : OnBackPressedCallback(false), MenuProvider, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener { + + private var searchItemRef: WeakReference? = null + + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(R.menu.opt_chapters, menu) + val searchMenuItem = menu.findItem(R.id.action_search) + searchMenuItem.setOnActionExpandListener(this) + val searchView = searchMenuItem.actionView as SearchView + searchView.setOnQueryTextListener(this) + searchView.setIconifiedByDefault(false) + searchView.queryHint = searchMenuItem.title + searchItemRef = WeakReference(searchMenuItem) + } + + override fun onPrepareMenu(menu: Menu) { + menu.findItem(R.id.action_search)?.isVisible = viewModel.isChaptersEmpty.value == false + menu.findItem(R.id.action_reversed)?.isChecked = viewModel.isChaptersReversed.value == true + menu.findItem(R.id.action_grid_view)?.isChecked = viewModel.isChaptersInGridView.value == true + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { + R.id.action_reversed -> { + viewModel.setChaptersReversed(!menuItem.isChecked) + true + } + + R.id.action_grid_view -> { + viewModel.setChaptersInGridView(!menuItem.isChecked) + true + } + + else -> false + } + + override fun handleOnBackPressed() { + searchItemRef?.get()?.collapseActionView() + } + + override fun onMenuItemActionExpand(item: MenuItem): Boolean { + sheet.expandAndLock() + isEnabled = true + return true + } + + override fun onMenuItemActionCollapse(item: MenuItem): Boolean { + isEnabled = false + (item.actionView as? SearchView)?.setQuery("", false) + viewModel.performChapterSearch(null) + sheet.unlock() + return true + } + + override fun onQueryTextSubmit(query: String?): Boolean = false + + override fun onQueryTextChange(newText: String?): Boolean { + viewModel.performChapterSearch(newText) + return true + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity2.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity2.kt index 768af6d91..f9ec7eb2d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity2.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity2.kt @@ -37,8 +37,6 @@ import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.filterNotNull import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.Bookmark -import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter -import org.koitharu.kotatsu.bookmarks.ui.sheet.BookmarksSheet import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.iconResId @@ -96,7 +94,6 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.util.ellipsize -import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo @@ -147,7 +144,6 @@ class DetailsActivity2 : viewBinding.infoLayout.chipAuthor.setOnClickListener(this) viewBinding.imageViewCover.setOnClickListener(this) viewBinding.buttonDescriptionMore.setOnClickListener(this) - viewBinding.buttonBookmarksMore.setOnClickListener(this) viewBinding.buttonScrobblingMore.setOnClickListener(this) viewBinding.buttonRelatedMore.setOnClickListener(this) viewBinding.infoLayout.chipSource.setOnClickListener(this) @@ -175,7 +171,6 @@ class DetailsActivity2 : viewModel.onActionDone.observeEvent(this, ReversibleActionObserver(viewBinding.scrollView, null)) viewModel.historyInfo.observe(this, ::onHistoryChanged) viewModel.isLoading.observe(this, ::onLoadingStateChanged) - viewModel.bookmarks.observe(this, ::onBookmarksChanged) viewModel.scrobblingInfo.observe(this, ::onScrobblingInfoChanged) viewModel.localSize.observe(this, ::onLocalSizeChanged) viewModel.relatedManga.observe(this, ::onRelatedMangaChanged) @@ -273,11 +268,6 @@ class DetailsActivity2 : ScrobblingSelectorSheet.show(supportFragmentManager, manga, null) } - R.id.button_bookmarks_more -> { - val manga = viewModel.manga.value ?: return - BookmarksSheet.show(supportFragmentManager, manga) - } - R.id.button_related_more -> { val manga = viewModel.manga.value ?: return startActivity(RelatedMangaActivity.newIntent(v.context, manga)) @@ -455,20 +445,6 @@ class DetailsActivity2 : } } - private fun onBookmarksChanged(bookmarks: List) { - var adapter = viewBinding.recyclerViewBookmarks.adapter as? BookmarksAdapter - viewBinding.groupBookmarks.isGone = bookmarks.isEmpty() - if (adapter != null) { - adapter.items = bookmarks - } else { - adapter = BookmarksAdapter(coil, this, this) - adapter.items = bookmarks - viewBinding.recyclerViewBookmarks.adapter = adapter - val spacing = resources.getDimensionPixelOffset(R.dimen.bookmark_list_spacing) - viewBinding.recyclerViewBookmarks.addItemDecoration(SpacingItemDecoration(spacing)) - } - } - private fun onScrobblingInfoChanged(scrobblings: List) { var adapter = viewBinding.recyclerViewScrobbling.adapter as? ScrollingInfoAdapter viewBinding.groupScrobbling.isGone = scrobblings.isEmpty() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt index 2c91c9c97..69f1763e7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/ChaptersPagesSheet.kt @@ -5,14 +5,19 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.appcompat.view.ActionMode import androidx.core.view.isVisible +import androidx.fragment.app.activityViewModels import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.ui.util.ActionModeListener +import org.koitharu.kotatsu.core.util.ext.doOnPageChanged +import org.koitharu.kotatsu.core.util.ext.menuView import org.koitharu.kotatsu.core.util.ext.recyclerView import org.koitharu.kotatsu.core.util.ext.setTabsEnabled import org.koitharu.kotatsu.databinding.SheetChaptersPagesBinding +import org.koitharu.kotatsu.details.ui.ChaptersMenuProvider2 +import org.koitharu.kotatsu.details.ui.DetailsViewModel import javax.inject.Inject @AndroidEntryPoint @@ -21,6 +26,8 @@ class ChaptersPagesSheet : BaseAdaptiveSheet(), Actio @Inject lateinit var settings: AppSettings + private val viewModel by activityViewModels() + override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetChaptersPagesBinding { return SheetChaptersPagesBinding.inflate(inflater, container, false) } @@ -29,28 +36,48 @@ class ChaptersPagesSheet : BaseAdaptiveSheet(), Actio super.onViewBindingCreated(binding, savedInstanceState) val adapter = DetailsPagerAdapter2(this, settings) binding.pager.recyclerView?.isNestedScrollingEnabled = false - binding.pager.offscreenPageLimit = 1 + binding.pager.offscreenPageLimit = adapter.itemCount binding.pager.adapter = adapter + binding.pager.doOnPageChanged(::onPageChanged) TabLayoutMediator(binding.tabs, binding.pager, adapter).attach() binding.pager.setCurrentItem(settings.defaultDetailsTab, false) binding.tabs.isVisible = adapter.itemCount > 1 + val menuProvider = ChaptersMenuProvider2(viewModel, this) + onBackPressedDispatcher.addCallback(viewLifecycleOwner, menuProvider) + binding.toolbar.addMenuProvider(menuProvider) + actionModeDelegate.addListener(this, viewLifecycleOwner) } override fun onActionModeStarted(mode: ActionMode) { - setExpanded(true, true) - viewBinding?.run { - pager.isUserInputEnabled = false - tabs.setTabsEnabled(false) - } + expandAndLock() + viewBinding?.toolbar?.menuView?.isEnabled = false } override fun onActionModeFinished(mode: ActionMode) { - setExpanded(isExpanded, false) + unlock() + viewBinding?.toolbar?.menuView?.isEnabled = true + } + + override fun expandAndLock() { + super.expandAndLock() + adjustLockState() + } + + override fun unlock() { + super.unlock() + adjustLockState() + } + + private fun adjustLockState() { viewBinding?.run { - pager.isUserInputEnabled = true - tabs.setTabsEnabled(true) + pager.isUserInputEnabled = !isLocked + tabs.setTabsEnabled(!isLocked) } } + + private fun onPageChanged(position: Int) { + viewBinding?.toolbar?.menuView?.isVisible = position == 0 + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/DetailsPagerAdapter2.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/DetailsPagerAdapter2.kt index e335d5cf8..ae44ee29b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/DetailsPagerAdapter2.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/DetailsPagerAdapter2.kt @@ -6,6 +6,7 @@ import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.details.ui.pager.bookmarks.MangaBookmarksFragment import org.koitharu.kotatsu.details.ui.pager.chapters.ChaptersFragment import org.koitharu.kotatsu.details.ui.pager.pages.PagesFragment @@ -17,11 +18,12 @@ class DetailsPagerAdapter2( val isPagesTabEnabled = settings.isPagesTabEnabled - override fun getItemCount(): Int = if (isPagesTabEnabled) 2 else 1 + override fun getItemCount(): Int = if (isPagesTabEnabled) 3 else 2 override fun createFragment(position: Int): Fragment = when (position) { 0 -> ChaptersFragment() - 1 -> PagesFragment() + 1 -> if (isPagesTabEnabled) PagesFragment() else MangaBookmarksFragment() + 2 -> MangaBookmarksFragment() else -> throw IllegalArgumentException("Invalid position $position") } @@ -29,7 +31,8 @@ class DetailsPagerAdapter2( tab.setText( when (position) { 0 -> R.string.chapters - 1 -> R.string.pages + 1 -> if (isPagesTabEnabled) R.string.pages else R.string.bookmarks + 2 -> R.string.bookmarks else -> 0 }, ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/MangaBookmarksFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/MangaBookmarksFragment.kt new file mode 100644 index 000000000..13617fe16 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/MangaBookmarksFragment.kt @@ -0,0 +1,133 @@ +package org.koitharu.kotatsu.details.ui.pager.bookmarks + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.graphics.Insets +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.GridLayoutManager +import coil.ImageLoader +import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.bookmarks.domain.Bookmark +import org.koitharu.kotatsu.bookmarks.ui.sheet.BookmarksAdapter +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.ui.BaseFragment +import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.core.util.ext.observe +import org.koitharu.kotatsu.databinding.FragmentMangaBookmarksBinding +import org.koitharu.kotatsu.details.ui.DetailsViewModel +import org.koitharu.kotatsu.list.ui.MangaListSpanResolver +import org.koitharu.kotatsu.list.ui.adapter.ListItemType +import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration +import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder +import org.koitharu.kotatsu.reader.ui.pager.ReaderPage +import org.koitharu.kotatsu.reader.ui.thumbnails.OnPageSelectListener +import javax.inject.Inject + +@AndroidEntryPoint +class MangaBookmarksFragment : BaseFragment(), + OnListItemClickListener { + + private val activityViewModel by activityViewModels() + private val viewModel by viewModels() + + @Inject + lateinit var coil: ImageLoader + + @Inject + lateinit var settings: AppSettings + + private var bookmarksAdapter: BookmarksAdapter? = null + private var spanResolver: MangaListSpanResolver? = null + + private val spanSizeLookup = SpanSizeLookup() + private val listCommitCallback = Runnable { + spanSizeLookup.invalidateCache() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + activityViewModel.manga.observe(this, viewModel) + } + + override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentMangaBookmarksBinding { + return FragmentMangaBookmarksBinding.inflate(inflater, container, false) + } + + override fun onViewBindingCreated(binding: FragmentMangaBookmarksBinding, savedInstanceState: Bundle?) { + super.onViewBindingCreated(binding, savedInstanceState) + spanResolver = MangaListSpanResolver(binding.root.resources) + bookmarksAdapter = BookmarksAdapter( + coil = coil, + lifecycleOwner = viewLifecycleOwner, + clickListener = this@MangaBookmarksFragment, + headerClickListener = null, + ) + with(binding.recyclerView) { + addItemDecoration(TypedListSpacingDecoration(context, false)) + adapter = bookmarksAdapter + addOnLayoutChangeListener(spanResolver) + spanResolver?.setGridSize(settings.gridSize / 100f, this) + (layoutManager as GridLayoutManager).spanSizeLookup = spanSizeLookup + } + viewModel.content.observe(viewLifecycleOwner, checkNotNull(bookmarksAdapter)) + } + + override fun onDestroyView() { + spanResolver = null + bookmarksAdapter = null + spanSizeLookup.invalidateCache() + super.onDestroyView() + } + + override fun onPause() { + // required for BottomSheetBehavior + requireViewBinding().recyclerView.isNestedScrollingEnabled = false + super.onPause() + } + + override fun onResume() { + requireViewBinding().recyclerView.isNestedScrollingEnabled = true + super.onResume() + } + + override fun onWindowInsetsChanged(insets: Insets) = Unit + + override fun onItemClick(item: Bookmark, view: View) { + val listener = (parentFragment as? OnPageSelectListener) ?: (activity as? OnPageSelectListener) + if (listener != null) { + listener.onPageSelected(ReaderPage(item.toMangaPage(), item.page, item.chapterId)) + } else { + val intent = IntentBuilder(view.context) + .manga(activityViewModel.manga.value ?: return) + .bookmark(item) + .incognito(true) + .build() + startActivity(intent) + } + } + + private inner class SpanSizeLookup : GridLayoutManager.SpanSizeLookup() { + + init { + isSpanIndexCacheEnabled = true + isSpanGroupIndexCacheEnabled = true + } + + override fun getSpanSize(position: Int): Int { + val total = (viewBinding?.recyclerView?.layoutManager as? GridLayoutManager)?.spanCount ?: return 1 + return when (bookmarksAdapter?.getItemViewType(position)) { + ListItemType.PAGE_THUMB.ordinal -> 1 + else -> total + } + } + + fun invalidateCache() { + invalidateSpanGroupIndexCache() + invalidateSpanIndexCache() + } + } +} + diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/MangaBookmarksViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/MangaBookmarksViewModel.kt new file mode 100644 index 000000000..d1590960e --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/MangaBookmarksViewModel.kt @@ -0,0 +1,68 @@ +package org.koitharu.kotatsu.details.ui.pager.bookmarks + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.plus +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.bookmarks.domain.Bookmark +import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository +import org.koitharu.kotatsu.core.ui.BaseViewModel +import org.koitharu.kotatsu.list.ui.model.EmptyState +import org.koitharu.kotatsu.list.ui.model.ListHeader +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.list.ui.model.LoadingState +import org.koitharu.kotatsu.parsers.model.Manga +import javax.inject.Inject + +@HiltViewModel +class MangaBookmarksViewModel @Inject constructor( + bookmarksRepository: BookmarksRepository, +) : BaseViewModel(), FlowCollector { + + private val manga = MutableStateFlow(null) + + val content: StateFlow> = manga.filterNotNull().flatMapLatest { m -> + bookmarksRepository.observeBookmarks(m) + .map { mapList(m, it) } + }.withErrorHandling() + .filterNotNull() + .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, listOf(LoadingState)) + + override suspend fun emit(value: Manga?) { + manga.value = value + } + + private suspend fun mapList(manga: Manga, bookmarks: List): List? { + val chapters = manga.chapters ?: return null + val bookmarksMap = bookmarks.groupBy { it.chapterId } + val result = ArrayList(bookmarks.size + bookmarksMap.size) + for (chapter in chapters) { + val b = bookmarksMap[chapter.id] + if (b.isNullOrEmpty()) { + continue + } + result += ListHeader(chapter.name) + result.addAll(b) + } + if (result.isEmpty()) { + result.add( + EmptyState( + icon = 0, + textPrimary = R.string.no_bookmarks_yet, + textSecondary = R.string.no_bookmarks_summary, + actionStringRes = 0, + ), + ) + } + return result + } +} diff --git a/app/src/main/res/layout/activity_details_new.xml b/app/src/main/res/layout/activity_details_new.xml index 43dec3f63..a1c92dcee 100644 --- a/app/src/main/res/layout/activity_details_new.xml +++ b/app/src/main/res/layout/activity_details_new.xml @@ -252,48 +252,6 @@ tools:ignore="UnusedAttribute" tools:text="@tools:sample/lorem/random" /> - - -