Add bulk-selection & improve display mode in RecommendsScreen

This commit is contained in:
Cuong-Tran 2025-01-24 02:38:23 +07:00
parent 7b30a2a10f
commit c643d8a55b
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
10 changed files with 92 additions and 56 deletions

View file

@ -57,13 +57,10 @@ fun GlobalSearchScreen(
onChangeCategoryClick = bulkFavoriteScreenModel::addFavorite, onChangeCategoryClick = bulkFavoriteScreenModel::addFavorite,
onSelectAll = { onSelectAll = {
state.filteredItems.forEach { (_, result) -> state.filteredItems.forEach { (_, result) ->
when (result) { if (result is SearchItemResult.Success) {
is SearchItemResult.Success -> { result.result.forEach { manga ->
result.result.forEach { manga -> bulkFavoriteScreenModel.select(manga)
bulkFavoriteScreenModel.select(manga)
}
} }
else -> {}
} }
} }
}, },

View file

@ -25,7 +25,7 @@ import tachiyomi.presentation.core.i18n.stringResource
fun BrowseSourceSimpleToolbar( fun BrowseSourceSimpleToolbar(
navigateUp: () -> Unit, navigateUp: () -> Unit,
title: String, title: String,
displayMode: LibraryDisplayMode, displayMode: LibraryDisplayMode?,
onDisplayModeChange: (LibraryDisplayMode) -> Unit, onDisplayModeChange: (LibraryDisplayMode) -> Unit,
scrollBehavior: TopAppBarScrollBehavior, scrollBehavior: TopAppBarScrollBehavior,
// KMK --> // KMK -->
@ -38,25 +38,30 @@ fun BrowseSourceSimpleToolbar(
title = title, title = title,
actions = { actions = {
var selectingDisplayMode by remember { mutableStateOf(false) } var selectingDisplayMode by remember { mutableStateOf(false) }
// KMK -->
AppBarActions( AppBarActions(
// SY --> actions = persistentListOf<AppBar.AppBarAction>().builder()
actions = persistentListOf( .apply {
AppBar.Action( displayMode?.let {
title = stringResource(MR.strings.action_display_mode), add(
// KMK --> AppBar.Action(
icon = if (displayMode == LibraryDisplayMode.List) { title = stringResource(MR.strings.action_display_mode),
Icons.AutoMirrored.Filled.ViewList icon = if (displayMode == LibraryDisplayMode.List) {
} else { Icons.AutoMirrored.Filled.ViewList
Icons.Filled.ViewModule } else {
}, Icons.Filled.ViewModule
// KMK <-- },
onClick = { selectingDisplayMode = true }, onClick = { selectingDisplayMode = true },
), ),
// KMK --> )
bulkSelectionButton(isRunning, toggleSelectionMode), add(
// KMK <-- bulkSelectionButton(isRunning, toggleSelectionMode),
), )
}
}
.build(),
) )
// KMK <--
DropdownMenu( DropdownMenu(
expanded = selectingDisplayMode, expanded = selectingDisplayMode,
onDismissRequest = { selectingDisplayMode = false }, onDismissRequest = { selectingDisplayMode = false },

View file

@ -35,7 +35,7 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
BackHandler(enabled = bulkFavoriteState.selectionMode) { BackHandler(enabled = bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode() bulkFavoriteScreenModel.backHandler()
} }
// KMK <-- // KMK <--

View file

@ -75,7 +75,7 @@ data class SourceSearchScreen(
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
BackHandler(enabled = bulkFavoriteState.selectionMode) { BackHandler(enabled = bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode() bulkFavoriteScreenModel.backHandler()
} }
val mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems() val mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems()

View file

@ -50,7 +50,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
import tachiyomi.core.common.preference.CheckboxState import tachiyomi.core.common.preference.CheckboxState

View file

@ -87,8 +87,6 @@ import exh.pagepreview.PagePreviewScreen
import exh.recs.RecommendsScreen import exh.recs.RecommendsScreen
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource import exh.source.getMainSource
import exh.source.isMdBasedSource
import exh.ui.ifSourcesLoaded
import exh.ui.metadata.MetadataViewScreen import exh.ui.metadata.MetadataViewScreen
import exh.ui.smartsearch.SmartSearchScreen import exh.ui.smartsearch.SmartSearchScreen
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@ -160,7 +158,7 @@ class MangaScreen(
BackHandler(enabled = bulkFavoriteState.selectionMode || showingRelatedMangasScreen.value) { BackHandler(enabled = bulkFavoriteState.selectionMode || showingRelatedMangasScreen.value) {
when { when {
bulkFavoriteState.selectionMode -> bulkFavoriteScreenModel.toggleSelectionMode() bulkFavoriteState.selectionMode -> bulkFavoriteScreenModel.backHandler()
showingRelatedMangasScreen.value -> showingRelatedMangasScreen.value = false showingRelatedMangasScreen.value -> showingRelatedMangasScreen.value = false
} }
} }

View file

@ -59,7 +59,7 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
BackHandler(enabled = bulkFavoriteState.selectionMode) { BackHandler(enabled = bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode() bulkFavoriteScreenModel.backHandler()
} }
// KMK <-- // KMK <--

View file

@ -28,7 +28,6 @@ import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import exh.ui.ifSourcesLoaded
import mihon.presentation.core.util.collectAsLazyPagingItems import mihon.presentation.core.util.collectAsLazyPagingItems
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@ -65,7 +64,7 @@ class BrowseRecommendsScreen(
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
BackHandler(enabled = bulkFavoriteState.selectionMode) { BackHandler(enabled = bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode() bulkFavoriteScreenModel.backHandler()
} }
// KMK <-- // KMK <--

View file

@ -1,25 +1,16 @@
package exh.recs package exh.recs
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
import eu.kanade.presentation.components.BulkSelectionToolbar
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.AddDuplicateMangaDialog import eu.kanade.tachiyomi.ui.browse.AddDuplicateMangaDialog
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
@ -28,14 +19,11 @@ import eu.kanade.tachiyomi.ui.browse.ChangeMangaCategoryDialog
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
import mihon.presentation.core.util.collectAsLazyPagingItems
import tachiyomi.core.common.util.lang.launchIO
import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import exh.recs.components.RecommendsScreen import exh.recs.components.RecommendsScreen
import exh.ui.ifSourcesLoaded import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() { class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
@ -63,7 +51,7 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
BackHandler(enabled = bulkFavoriteState.selectionMode) { BackHandler(enabled = bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode() bulkFavoriteScreenModel.backHandler()
} }
// KMK <-- // KMK <--
@ -125,8 +113,6 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
}, },
// KMK --> // KMK -->
bulkFavoriteScreenModel = bulkFavoriteScreenModel, bulkFavoriteScreenModel = bulkFavoriteScreenModel,
displayMode = screenModel.displayMode,
onDisplayModeChange = { screenModel.displayMode = it },
// KMK <-- // KMK <--
) )

View file

@ -4,13 +4,18 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
import eu.kanade.presentation.browse.components.GlobalSearchCardRow import eu.kanade.presentation.browse.components.GlobalSearchCardRow
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
import eu.kanade.presentation.browse.components.GlobalSearchResultItem import eu.kanade.presentation.browse.components.GlobalSearchResultItem
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.BulkSelectionToolbar
import eu.kanade.presentation.util.formattedMessage import eu.kanade.presentation.util.formattedMessage
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
import exh.recs.RecommendationItemResult import exh.recs.RecommendationItemResult
import exh.recs.RecommendsScreenModel import exh.recs.RecommendsScreenModel
import exh.recs.sources.RecommendationPagingSource import exh.recs.sources.RecommendationPagingSource
@ -30,14 +35,52 @@ fun RecommendsScreen(
onClickSource: (RecommendationPagingSource) -> Unit, onClickSource: (RecommendationPagingSource) -> Unit,
onClickItem: (Manga) -> Unit, onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit, onLongClickItem: (Manga) -> Unit,
// KMK -->
bulkFavoriteScreenModel: BulkFavoriteScreenModel,
// KMK <--
) { ) {
// KMK -->
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
// KMK <--
Scaffold( Scaffold(
topBar = { scrollBehavior -> topBar = { scrollBehavior ->
AppBar( // KMK -->
title = stringResource(SYMR.strings.similar, manga.title), if (bulkFavoriteState.selectionMode) {
scrollBehavior = scrollBehavior, BulkSelectionToolbar(
navigateUp = navigateUp, selectedCount = bulkFavoriteState.selection.size,
) isRunning = bulkFavoriteState.isRunning,
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
onChangeCategoryClick = bulkFavoriteScreenModel::addFavorite,
onSelectAll = {
state.filteredItems.forEach { (_, result) ->
if (result is RecommendationItemResult.Success) {
result.result.forEach { manga ->
bulkFavoriteScreenModel.select(manga)
}
}
}
},
onReverseSelection = {
bulkFavoriteScreenModel.reverseSelection(
state.filteredItems.values
.filterIsInstance<SearchItemResult.Success>()
.flatMap { it.result },
)
},
)
} else {
BrowseSourceSimpleToolbar(
displayMode = null,
onDisplayModeChange = {},
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
isRunning = bulkFavoriteState.isRunning,
// KMK <--
title = stringResource(SYMR.strings.similar, manga.title),
scrollBehavior = scrollBehavior,
navigateUp = navigateUp,
)
}
}, },
) { paddingValues -> ) { paddingValues ->
RecommendsContent( RecommendsContent(
@ -47,6 +90,9 @@ fun RecommendsScreen(
onClickSource = onClickSource, onClickSource = onClickSource,
onClickItem = onClickItem, onClickItem = onClickItem,
onLongClickItem = onLongClickItem, onLongClickItem = onLongClickItem,
// KMK -->
selection = bulkFavoriteState.selection,
// KMK <--
) )
} }
} }
@ -59,6 +105,9 @@ internal fun RecommendsContent(
onClickSource: (RecommendationPagingSource) -> Unit, onClickSource: (RecommendationPagingSource) -> Unit,
onClickItem: (Manga) -> Unit, onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit, onLongClickItem: (Manga) -> Unit,
// KMK -->
selection: List<Manga>,
// KMK <--
) { ) {
LazyColumn( LazyColumn(
contentPadding = contentPadding, contentPadding = contentPadding,
@ -80,6 +129,9 @@ internal fun RecommendsContent(
getManga = getManga, getManga = getManga,
onClick = onClickItem, onClick = onClickItem,
onLongClick = onLongClickItem, onLongClick = onLongClickItem,
// KMK -->
selection = selection,
// KMK <--
) )
} }
is RecommendationItemResult.Error -> { is RecommendationItemResult.Error -> {