Merge branch 'devel' into feature/shikimori

This commit is contained in:
Koitharu
2022-05-09 15:31:15 +03:00
14 changed files with 66 additions and 24 deletions

View File

@@ -24,7 +24,7 @@ import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.widgets.ChipsView import org.koitharu.kotatsu.base.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.model.MangaHistory import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesDialog import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet
import org.koitharu.kotatsu.image.ui.ImageActivity import org.koitharu.kotatsu.image.ui.ImageActivity
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -181,7 +181,7 @@ class DetailsFragment :
val manga = viewModel.manga.value ?: return val manga = viewModel.manga.value ?: return
when (v.id) { when (v.id) {
R.id.button_favorite -> { R.id.button_favorite -> {
FavouriteCategoriesDialog.show(childFragmentManager, manga) FavouriteCategoriesBottomSheet.show(childFragmentManager, manga)
} }
R.id.button_read -> { R.id.button_read -> {
val chapterId = viewModel.readingHistory.value?.chapterId val chapterId = viewModel.readingHistory.value?.chapterId

View File

@@ -16,7 +16,7 @@ abstract class FavouriteCategoriesDao {
abstract fun observeAll(): Flow<List<FavouriteCategoryEntity>> abstract fun observeAll(): Flow<List<FavouriteCategoryEntity>>
@Query("SELECT * FROM favourite_categories WHERE category_id = :id") @Query("SELECT * FROM favourite_categories WHERE category_id = :id")
abstract fun observe(id: Long): Flow<FavouriteCategoryEntity> abstract fun observe(id: Long): Flow<FavouriteCategoryEntity?>
@Insert(onConflict = OnConflictStrategy.ABORT) @Insert(onConflict = OnConflictStrategy.ABORT)
abstract suspend fun insert(category: FavouriteCategoryEntity): Long abstract suspend fun insert(category: FavouriteCategoryEntity): Long

View File

@@ -1,10 +1,7 @@
package org.koitharu.kotatsu.favourites.domain package org.koitharu.kotatsu.favourites.domain
import androidx.room.withTransaction import androidx.room.withTransaction
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.entity.* import org.koitharu.kotatsu.core.db.entity.*
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
@@ -52,9 +49,9 @@ class FavouritesRepository(
}.distinctUntilChanged() }.distinctUntilChanged()
} }
fun observeCategory(id: Long): Flow<FavouriteCategory> { fun observeCategory(id: Long): Flow<FavouriteCategory?> {
return db.favouriteCategoriesDao.observe(id) return db.favouriteCategoriesDao.observe(id)
.map { it.toFavouriteCategory() } .map { it?.toFavouriteCategory() }
} }
fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> { fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> {
@@ -162,6 +159,7 @@ class FavouritesRepository(
private fun observeOrder(categoryId: Long): Flow<SortOrder> { private fun observeOrder(categoryId: Long): Flow<SortOrder> {
return db.favouriteCategoriesDao.observe(categoryId) return db.favouriteCategoriesDao.observe(categoryId)
.filterNotNull()
.map { x -> SortOrder(x.order, SortOrder.NEWEST) } .map { x -> SortOrder(x.order, SortOrder.NEWEST) }
.distinctUntilChanged() .distinctUntilChanged()
} }

View File

@@ -6,6 +6,7 @@ import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@@ -17,6 +18,7 @@ import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.util.ActionModeListener import org.koitharu.kotatsu.base.ui.util.ActionModeListener
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding
import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate
import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel
@@ -31,13 +33,15 @@ class FavouritesContainerFragment :
BaseFragment<FragmentFavouritesBinding>(), BaseFragment<FragmentFavouritesBinding>(),
FavouritesTabLongClickListener, FavouritesTabLongClickListener,
CategoriesEditDelegate.CategoriesEditCallback, CategoriesEditDelegate.CategoriesEditCallback,
ActionModeListener { ActionModeListener,
View.OnClickListener {
private val viewModel by viewModel<FavouritesCategoriesViewModel>() private val viewModel by viewModel<FavouritesCategoriesViewModel>()
private val editDelegate by lazy(LazyThreadSafetyMode.NONE) { private val editDelegate by lazy(LazyThreadSafetyMode.NONE) {
CategoriesEditDelegate(requireContext(), this) CategoriesEditDelegate(requireContext(), this)
} }
private var pagerAdapter: FavouritesPagerAdapter? = null private var pagerAdapter: FavouritesPagerAdapter? = null
private var stubBinding: ItemEmptyStateBinding? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -52,9 +56,7 @@ class FavouritesContainerFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val adapter = FavouritesPagerAdapter(this, this) val adapter = FavouritesPagerAdapter(this, this)
viewModel.visibleCategories.value?.let { viewModel.visibleCategories.value?.let(::onCategoriesChanged)
adapter.replaceData(it)
}
binding.pager.adapter = adapter binding.pager.adapter = adapter
pagerAdapter = adapter pagerAdapter = adapter
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach() TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
@@ -66,6 +68,7 @@ class FavouritesContainerFragment :
override fun onDestroyView() { override fun onDestroyView() {
pagerAdapter = null pagerAdapter = null
stubBinding = null
super.onDestroyView() super.onDestroyView()
} }
@@ -101,6 +104,15 @@ class FavouritesContainerFragment :
private fun onCategoriesChanged(categories: List<CategoryListModel>) { private fun onCategoriesChanged(categories: List<CategoryListModel>) {
pagerAdapter?.replaceData(categories) pagerAdapter?.replaceData(categories)
if (categories.isEmpty()) {
binding.pager.isVisible = false
binding.tabs.isVisible = false
showStub()
} else {
binding.pager.isVisible = true
binding.tabs.isVisible = true
(stubBinding?.root ?: binding.stubEmptyState).isVisible = false
}
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -130,6 +142,12 @@ class FavouritesContainerFragment :
return true return true
} }
override fun onClick(v: View) {
when (v.id) {
R.id.button_retry -> startActivity(FavouritesCategoryEditActivity.newIntent(v.context))
}
}
override fun onDeleteCategory(category: FavouriteCategory) { override fun onDeleteCategory(category: FavouriteCategory) {
viewModel.deleteCategory(category.id) viewModel.deleteCategory(category.id)
} }
@@ -147,7 +165,7 @@ class FavouritesContainerFragment :
menu.setOnMenuItemClickListener { menu.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_remove -> editDelegate.deleteCategory(category) R.id.action_remove -> editDelegate.deleteCategory(category)
R.id.action_edit -> FavouritesCategoryEditActivity.newIntent(tabView.context, category.id) R.id.action_edit -> startActivity(FavouritesCategoryEditActivity.newIntent(tabView.context, category.id))
else -> return@setOnMenuItemClickListener false else -> return@setOnMenuItemClickListener false
} }
true true
@@ -160,7 +178,7 @@ class FavouritesContainerFragment :
menu.inflate(R.menu.popup_category_all) menu.inflate(R.menu.popup_category_all)
menu.setOnMenuItemClickListener { menu.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_create -> FavouritesCategoryEditActivity.newIntent(requireContext()) R.id.action_create -> startActivity(FavouritesCategoryEditActivity.newIntent(requireContext()))
R.id.action_hide -> viewModel.setAllCategoriesVisible(false) R.id.action_hide -> viewModel.setAllCategoriesVisible(false)
} }
true true
@@ -168,6 +186,18 @@ class FavouritesContainerFragment :
menu.show() menu.show()
} }
private fun showStub() {
val stub = stubBinding ?: ItemEmptyStateBinding.bind(binding.stubEmptyState.inflate())
stub.root.isVisible = true
stub.icon.setImageResource(R.drawable.ic_heart_outline)
stub.textPrimary.setText(R.string.text_empty_holder_primary)
stub.textSecondary.setText(R.string.empty_favourite_categories)
stub.buttonRetry.setText(R.string.add)
stub.buttonRetry.isVisible = true
stub.buttonRetry.setOnClickListener(this)
stubBinding = stub
}
companion object { companion object {
fun newInstance() = FavouritesContainerFragment() fun newInstance() = FavouritesContainerFragment()

View File

@@ -30,7 +30,7 @@ class FavouritesCategoriesViewModel(
repository.observeCategories(), repository.observeCategories(),
observeAllCategoriesVisible(), observeAllCategoriesVisible(),
) { list, showAll -> ) { list, showAll ->
mapCategories(list, showAll, showAll) mapCategories(list, showAll, showAll && list.isNotEmpty())
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
fun deleteCategory(id: Long) { fun deleteCategory(id: Long) {

View File

@@ -24,7 +24,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.withArgs import org.koitharu.kotatsu.utils.ext.withArgs
class FavouriteCategoriesDialog : class FavouriteCategoriesBottomSheet :
BaseBottomSheet<DialogFavoriteCategoriesBinding>(), BaseBottomSheet<DialogFavoriteCategoriesBinding>(),
OnListItemClickListener<MangaCategoryItem>, OnListItemClickListener<MangaCategoryItem>,
CategoriesEditDelegate.CategoriesEditCallback, CategoriesEditDelegate.CategoriesEditCallback,
@@ -59,7 +59,7 @@ class FavouriteCategoriesDialog :
override fun onMenuItemClick(item: MenuItem): Boolean { override fun onMenuItemClick(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.action_create -> { R.id.action_create -> {
FavouritesCategoryEditActivity.newIntent(requireContext()) startActivity(FavouritesCategoryEditActivity.newIntent(requireContext()))
true true
} }
else -> false else -> false
@@ -87,7 +87,7 @@ class FavouriteCategoriesDialog :
fun show(fm: FragmentManager, manga: Manga) = Companion.show(fm, listOf(manga)) fun show(fm: FragmentManager, manga: Manga) = Companion.show(fm, listOf(manga))
fun show(fm: FragmentManager, manga: Collection<Manga>) = FavouriteCategoriesDialog().withArgs(1) { fun show(fm: FragmentManager, manga: Collection<Manga>) = FavouriteCategoriesBottomSheet().withArgs(1) {
putParcelableArrayList( putParcelableArrayList(
KEY_MANGA_LIST, KEY_MANGA_LIST,
manga.mapTo(ArrayList(manga.size)) { ParcelableManga(it, withChapters = false) } manga.mapTo(ArrayList(manga.size)) { ParcelableManga(it, withChapters = false) }

View File

@@ -28,11 +28,11 @@ class FavouritesListViewModel(
settings: AppSettings, settings: AppSettings,
) : MangaListViewModel(settings), CountersProvider { ) : MangaListViewModel(settings), CountersProvider {
var sortOrder: LiveData<SortOrder> = if (categoryId == NO_ID) { var sortOrder: LiveData<SortOrder?> = if (categoryId == NO_ID) {
MutableLiveData(null) MutableLiveData(null)
} else { } else {
repository.observeCategory(categoryId) repository.observeCategory(categoryId)
.map { it.order } .map { it?.order }
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
} }

View File

@@ -28,7 +28,7 @@ import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.databinding.FragmentListBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.download.ui.service.DownloadService import org.koitharu.kotatsu.download.ui.service.DownloadService
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesDialog import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter.Companion.ITEM_TYPE_MANGA_GRID import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter.Companion.ITEM_TYPE_MANGA_GRID
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
@@ -297,7 +297,7 @@ abstract class MangaListFragment :
true true
} }
R.id.action_favourite -> { R.id.action_favourite -> {
FavouriteCategoriesDialog.show(childFragmentManager, selectedItems) FavouriteCategoriesBottomSheet.show(childFragmentManager, selectedItems)
mode.finish() mode.finish()
true true
} }

View File

@@ -65,6 +65,9 @@ class FeedViewModel(
if (loadingJob?.isActive == true) { if (loadingJob?.isActive == true) {
return return
} }
if (append && !hasNextPage.value) {
return
}
loadingJob = launchLoadingJob(Dispatchers.Default) { loadingJob = launchLoadingJob(Dispatchers.Default) {
val offset = if (append) logList.value?.size ?: 0 else 0 val offset = if (append) logList.value?.size ?: 0 else 0
val list = repository.getTrackingLog(offset, 20) val list = repository.getTrackingLog(offset, 20)

View File

@@ -34,7 +34,9 @@
android:id="@+id/edit_name" android:id="@+id/edit_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/name" /> android:hint="@string/name"
android:imeOptions="actionDone"
android:inputType="textCapSentences" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>

View File

@@ -20,6 +20,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:overScrollMode="never" android:overScrollMode="never"
android:paddingBottom="@dimen/list_spacing"
android:scrollbars="vertical" android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_checkable_new" /> tools:listitem="@layout/item_checkable_new" />

View File

@@ -17,4 +17,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<ViewStub
android:id="@+id/stub_empty_state"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/item_empty_state" />
</LinearLayout> </LinearLayout>

View File

@@ -285,4 +285,5 @@
<string name="edit">Изменить</string> <string name="edit">Изменить</string>
<string name="edit_category">Изменить категорию</string> <string name="edit_category">Изменить категорию</string>
<string name="tracking">Отслеживание</string> <string name="tracking">Отслеживание</string>
<string name="empty_favourite_categories">Нет категорий избранного</string>
</resources> </resources>

View File

@@ -290,4 +290,5 @@
<string name="edit">Edit</string> <string name="edit">Edit</string>
<string name="edit_category">Edit category</string> <string name="edit_category">Edit category</string>
<string name="tracking">Tracking</string> <string name="tracking">Tracking</string>
<string name="empty_favourite_categories">No favourite categories</string>
</resources> </resources>