From 80149b1ce7b4d69ab9d46efe0068e059daa6c351 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 20 Jul 2023 10:56:15 +0300 Subject: [PATCH] Related manga activity --- app/src/main/AndroidManifest.xml | 3 + .../kotatsu/details/ui/DetailsFragment.kt | 6 ++ .../details/ui/related/RelatedListFragment.kt | 24 +++++ .../ui/related/RelatedListViewModel.kt | 99 +++++++++++++++++++ .../ui/related/RelatedMangaActivity.kt | 50 ++++++++++ app/src/main/res/layout/item_tip.xml | 2 +- 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListFragment.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedMangaActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4a2c84584..ae07018d1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -100,6 +100,9 @@ + { BookmarksSheet.show(parentFragmentManager, manga) } + + R.id.button_related_more -> { + startActivity(RelatedMangaActivity.newIntent(v.context, manga)) + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListFragment.kt new file mode 100644 index 000000000..8ec35ac90 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListFragment.kt @@ -0,0 +1,24 @@ +package org.koitharu.kotatsu.details.ui.related + +import android.view.Menu +import androidx.appcompat.view.ActionMode +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.ui.list.ListSelectionController +import org.koitharu.kotatsu.list.ui.MangaListFragment + +@AndroidEntryPoint +class RelatedListFragment : MangaListFragment() { + + override val viewModel by viewModels() + override val isSwipeRefreshEnabled = false + + override fun onScrolledToEnd() = Unit + + override fun onCreateActionMode(controller: ListSelectionController, mode: ActionMode, menu: Menu): Boolean { + mode.menuInflater.inflate(R.menu.mode_remote, menu) + return super.onCreateActionMode(controller, mode, menu) + } +} + diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt new file mode 100644 index 000000000..8cc83be4b --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt @@ -0,0 +1,99 @@ +package org.koitharu.kotatsu.details.ui.related + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.plus +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.util.ext.call +import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug +import org.koitharu.kotatsu.core.util.ext.require +import org.koitharu.kotatsu.download.ui.worker.DownloadWorker +import org.koitharu.kotatsu.list.domain.ListExtraProvider +import org.koitharu.kotatsu.list.ui.MangaListViewModel +import org.koitharu.kotatsu.list.ui.model.EmptyState +import org.koitharu.kotatsu.list.ui.model.LoadingState +import org.koitharu.kotatsu.list.ui.model.toErrorState +import org.koitharu.kotatsu.list.ui.model.toUi +import org.koitharu.kotatsu.parsers.model.Manga +import javax.inject.Inject + +@HiltViewModel +class RelatedListViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, + mangaRepositoryFactory: MangaRepository.Factory, + settings: AppSettings, + private val extraProvider: ListExtraProvider, + downloadScheduler: DownloadWorker.Scheduler, +) : MangaListViewModel(settings, downloadScheduler) { + + private val seed = savedStateHandle.require(MangaIntent.KEY_MANGA).manga + private val repository = mangaRepositoryFactory.create(seed.source) + private val mangaList = MutableStateFlow?>(null) + private val listError = MutableStateFlow(null) + private var loadingJob: Job? = null + + override val content = combine( + mangaList, + listMode, + listError, + ) { list, mode, error -> + when { + list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true)) + list == null -> listOf(LoadingState) + list.isEmpty() -> listOf(createEmptyState()) + else -> list.toUi(mode, extraProvider) + } + }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) + + init { + loadList() + } + + override fun onRefresh() { + loadList() + } + + override fun onRetry() { + loadList() + } + + private fun loadList(): Job { + loadingJob?.let { + if (it.isActive) return it + } + return launchLoadingJob(Dispatchers.Default) { + try { + listError.value = null + mangaList.value = repository.getRelated(seed) + } catch (e: CancellationException) { + throw e + } catch (e: Throwable) { + e.printStackTraceDebug() + listError.value = e + if (!mangaList.value.isNullOrEmpty()) { + errorEvent.call(e) + } + } + }.also { loadingJob = it } + } + + private fun createEmptyState() = EmptyState( + icon = R.drawable.ic_empty_common, + textPrimary = R.string.nothing_found, + textSecondary = 0, + actionStringRes = 0, + ) +} + diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedMangaActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedMangaActivity.kt new file mode 100644 index 000000000..49aca965c --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedMangaActivity.kt @@ -0,0 +1,50 @@ +package org.koitharu.kotatsu.details.ui.related + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.core.graphics.Insets +import androidx.core.view.updatePadding +import androidx.fragment.app.commit +import com.google.android.material.appbar.AppBarLayout +import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.parser.MangaIntent +import org.koitharu.kotatsu.core.ui.BaseActivity +import org.koitharu.kotatsu.databinding.ActivityContainerBinding +import org.koitharu.kotatsu.main.ui.owners.AppBarOwner +import org.koitharu.kotatsu.parsers.model.Manga + +@AndroidEntryPoint +class RelatedMangaActivity : BaseActivity(), AppBarOwner { + + override val appBar: AppBarLayout + get() = viewBinding.appbar + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(ActivityContainerBinding.inflate(layoutInflater)) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + val fm = supportFragmentManager + if (fm.findFragmentById(R.id.container) == null) { + fm.commit { + setReorderingAllowed(true) + replace(R.id.container, RelatedListFragment::class.java, intent.extras) + } + } + } + + override fun onWindowInsetsChanged(insets: Insets) { + viewBinding.root.updatePadding( + left = insets.left, + right = insets.right, + ) + } + + companion object { + + fun newIntent(context: Context, seed: Manga) = Intent(context, RelatedMangaActivity::class.java) + .putExtra(MangaIntent.KEY_MANGA, ParcelableManga(seed, withChapters = false)) + } +} diff --git a/app/src/main/res/layout/item_tip.xml b/app/src/main/res/layout/item_tip.xml index 0beb8d9f9..0c73ff9fb 100644 --- a/app/src/main/res/layout/item_tip.xml +++ b/app/src/main/res/layout/item_tip.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - style="?materialCardViewOutlinedStyle" + style="?materialCardViewElevatedStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/margin_small">