Bulk-selection favorite
- Bulk selection to add to library & change categories of multiple entries all at once, everywhere.
- It can detect duplication being added and give option to allow/skip one by one or allow/skip all duplication.
- Also allow long-click to add/remove single entry to/from library, everywhere.
- It supports default category.
commit 7bca814a8d8481af148fbbe72a6e43623c701356
Merge: 3ee4f35aa 67534b391
Author: Cuong Tran <cuongtran.tm@gmail.com>
Date: Sun Mar 10 14:26:29 2024 +0700
Merge branch 'feature/bulk-favorite' into develop
commit 67534b391ffad5a6586fb1c18394b25afc3b432d
Author: Cuong Tran <cuongtran.tm@gmail.com>
Date: Sun Mar 10 14:24:00 2024 +0700
reuse method to add/remove single manga
commit f24b528c45b49286430286da6fc93a05c2917aa4
Author: Cuong Tran <cuongtran.tm@gmail.com>
Date: Sun Mar 10 13:58:14 2024 +0700
reuse bulkSelectionButton as AppBar.Action
commit d026241d9b742f6acf64d0090a9e6e7be53923c6
Author: Cuong Tran <cuongtran.tm@gmail.com>
Date: Sun Mar 10 13:26:46 2024 +0700
reuse Dialog composer
commit 7eb23481926ec981bece2e2e9ced87c795ef4eb8
Author: Cuong Tran <cuongtran.tm@gmail.com>
Date: Sun Mar 10 07:20:58 2024 +0700
fix detekt
commit 9643b9d834257836300b69bcb95cd587a38e34e0
Author: Cuong Tran <cuongtran.tm@gmail.com>
Date: Sun Mar 10 00:07:18 2024 +0700
fix: won’t show dialog if skip all then selection list is empty
commit 7faafe9fb440175dcac065bc2963ba3637d10d38
Author: Cuong Tran <cuongtran.tm@gmail.com>
Date: Sat Mar 9 23:58:46 2024 +0700
no need using builder to create AppBarAction
commit 5564b366eb879407510a733128c26a73ad8d6f2e
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Sat Mar 9 18:26:58 2024 +0700
add BulkSelection to the rest of Migrate, MangaDex…
commit 804bbb3943463298ae0b6eec33c9d3457b2891c9
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Sat Mar 9 17:40:42 2024 +0700
migrate the rest to BulkSelectionScreenModel
commit 8e82fc1e87e1e246b7b676ba0fe7ef0c5bf1d016
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Sat Mar 9 11:00:40 2024 +0700
separate BulkSelection into standalone BulkFavoriteScreenModel
commit 94a127b94d67841339ebdc674e398144e515cd14
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Thu Mar 7 13:18:35 2024 +0700
improve: toggle selection mode when last item is unselected
commit ba0009409637f872f37ac407041f937c3afceda8
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Thu Mar 7 13:00:31 2024 +0700
renovate code
commit 2d92ff9181297cfe795149968fdcd7e898852792
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Thu Mar 7 12:25:27 2024 +0700
Feat: allow long click to favorite on GlobalSearch
commit a24bb06a2a66dcd473c50728b04dc4d6118d0085
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Thu Mar 7 11:45:48 2024 +0700
Bulk favorite for GlobalSearchScreen & MigrateSearchScreen
commit 1d844ac74e356a43211c752442cb4279aea30b6f
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Thu Mar 7 11:29:58 2024 +0700
renovate: move SelectionToolbar to its own component
commit 62cb90d74c2bde7f7e824de277a3bf6e0256cadc
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Thu Mar 7 09:26:40 2024 +0700
Fix: Won’t try call favorite if none selected
commit d2ef151c95a4432eddd3848b8e2e102dfd683681
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Wed Mar 6 18:27:54 2024 +0700
Allow/Skip one by one
commit 5e138f701ed66b6c18ae1c7a9d58dae482c211e0
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Wed Mar 6 15:42:21 2024 +0700
Allow skip duplicates
commit 9ac7a0e393d5aa78d77afde307263a85c8fa051c
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Wed Mar 6 14:24:52 2024 +0700
Bulk selection mode for BrowseSourceScreen
commit 9749adca106c3fb911d1b6808f000f9e16008585
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Wed Mar 6 14:06:12 2024 +0700
rename
commit 4b5e5105976ca3e488979b93043530a678a36e9c
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Wed Mar 6 12:34:01 2024 +0700
Bulk selection for SourceFeedScreen
commit 7775c65af0fd957d020acbccd2bb42073ebcba11
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Wed Mar 6 12:21:55 2024 +0700
Use topBar action to add favorite instead of bottomBar
some minor name changes
commit 66417b7adffede1c92e1dbd75b2eac0cc8b9a13c
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Tue Mar 5 16:32:21 2024 +0700
Allow bulk-favorite default category
commit 0870a9db3c1986c5ccab641bc77b8a0a9cf47017
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Tue Mar 5 15:40:02 2024 +0700
Hide BottomNav when in selection mode
commit 3865c6d467ca0c63be5b387fcac9e9be5a096125
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Tue Mar 5 15:35:58 2024 +0700
Selection:
* Clear selection after set Favorite
* Keep selection when editing categories
commit e01a5c5d35f226133d15b7ed800688bf442885bc
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Tue Mar 5 15:25:34 2024 +0700
Allow add to favorite even when no category available
commit a4556058c86801eaabf7bbfae58cdc40af95f467
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Mon Mar 4 18:30:44 2024 +0700
Set Favorite & Libraries OK now
commit 7e045cb23b2d6da58f0f71f9edcf0c2a2fc60038
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Fri Mar 1 16:40:32 2024 +0700
handle back button
commit 8e3915431e0d461d45c34fa0ca9188bbdc074203
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Fri Mar 1 16:09:00 2024 +0700
shade for selected items
commit d89b7bffb5161498bd9e7ebfd26aa3731e808361
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Fri Mar 1 14:33:32 2024 +0700
Toggle and toolbar count
commit 1ea3a4c5496ca0d002fdbafde2ce913fd28c1987
Author: Cuong M. Tran <cuongtran.tm@gmail.com>
Date: Thu Feb 29 18:36:57 2024 +0700
selection list & toggleSelection/clearSelection
This commit is contained in:
parent
56673f25b1
commit
f22fea2702
28 changed files with 1559 additions and 147 deletions
|
|
@ -58,6 +58,9 @@ fun BrowseSourceContent(
|
|||
// SY <--
|
||||
onMangaClick: (Manga) -> Unit,
|
||||
onMangaLongClick: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
|
|
@ -155,6 +158,9 @@ fun BrowseSourceContent(
|
|||
contentPadding = contentPadding,
|
||||
onMangaClick = onMangaClick,
|
||||
onMangaLongClick = onMangaLongClick,
|
||||
// KMK -->
|
||||
selection = selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
LibraryDisplayMode.List -> {
|
||||
|
|
@ -163,6 +169,9 @@ fun BrowseSourceContent(
|
|||
contentPadding = contentPadding,
|
||||
onMangaClick = onMangaClick,
|
||||
onMangaLongClick = onMangaLongClick,
|
||||
// KMK -->
|
||||
selection = selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
|
||||
|
|
@ -172,6 +181,9 @@ fun BrowseSourceContent(
|
|||
contentPadding = contentPadding,
|
||||
onMangaClick = onMangaClick,
|
||||
onMangaLongClick = onMangaLongClick,
|
||||
// KMK -->
|
||||
selection = selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ fun FeedScreen(
|
|||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickDelete: (FeedSavedSearch) -> Unit,
|
||||
onClickManga: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
onLongClickManga: (Manga) -> Unit,
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
onRefresh: () -> Unit,
|
||||
getMangaState: @Composable (Manga, CatalogueSource?) -> State<Manga>,
|
||||
) {
|
||||
|
|
@ -121,6 +125,10 @@ fun FeedScreen(
|
|||
item = item,
|
||||
getMangaState = { getMangaState(it, item.source) },
|
||||
onClickManga = onClickManga,
|
||||
// KMK -->
|
||||
onLongClickManga = onLongClickManga,
|
||||
selection = selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -135,6 +143,10 @@ fun FeedItem(
|
|||
item: FeedItemUI,
|
||||
getMangaState: @Composable ((Manga) -> State<Manga>),
|
||||
onClickManga: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
onLongClickManga: (Manga) -> Unit,
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
when {
|
||||
item.results == null -> {
|
||||
|
|
@ -148,7 +160,10 @@ fun FeedItem(
|
|||
titles = item.results,
|
||||
getManga = getMangaState,
|
||||
onClick = onClickManga,
|
||||
onLongClick = onClickManga,
|
||||
// KMK -->
|
||||
onLongClick = onLongClickManga,
|
||||
selection = selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,17 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import eu.kanade.domain.source.model.installedExtension
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchToolbar
|
||||
import eu.kanade.presentation.components.SelectionToolbar
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
|
||||
|
|
@ -32,22 +36,42 @@ fun GlobalSearchScreen(
|
|||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickItem: (Manga) -> Unit,
|
||||
onLongClickItem: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
bulkFavoriteScreenModel: BulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
) {
|
||||
// KMK -->
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
// KMK <--
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
GlobalSearchToolbar(
|
||||
searchQuery = state.searchQuery,
|
||||
progress = state.progress,
|
||||
total = state.total,
|
||||
navigateUp = navigateUp,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
onSearch = onSearch,
|
||||
sourceFilter = state.sourceFilter,
|
||||
onChangeSearchFilter = onChangeSearchFilter,
|
||||
onlyShowHasResults = state.onlyShowHasResults,
|
||||
onToggleResults = onToggleResults,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
GlobalSearchToolbar(
|
||||
searchQuery = state.searchQuery,
|
||||
progress = state.progress,
|
||||
total = state.total,
|
||||
navigateUp = navigateUp,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
onSearch = onSearch,
|
||||
sourceFilter = state.sourceFilter,
|
||||
onChangeSearchFilter = onChangeSearchFilter,
|
||||
onlyShowHasResults = state.onlyShowHasResults,
|
||||
onToggleResults = onToggleResults,
|
||||
scrollBehavior = scrollBehavior,
|
||||
// KMK -->
|
||||
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
},
|
||||
) { paddingValues ->
|
||||
GlobalSearchContent(
|
||||
|
|
@ -57,6 +81,9 @@ fun GlobalSearchScreen(
|
|||
onClickSource = onClickSource,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = onLongClickItem,
|
||||
// KMK -->
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -70,6 +97,9 @@ internal fun GlobalSearchContent(
|
|||
onClickItem: (Manga) -> Unit,
|
||||
onLongClickItem: (Manga) -> Unit,
|
||||
fromSourceId: Long? = null,
|
||||
// KMK -->
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
LazyColumn(
|
||||
contentPadding = contentPadding,
|
||||
|
|
@ -107,6 +137,9 @@ internal fun GlobalSearchContent(
|
|||
getManga = getManga,
|
||||
onClick = onClickItem,
|
||||
onLongClick = onLongClickItem,
|
||||
// KMK -->
|
||||
selection = selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
is SearchItemResult.Error -> {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@ package eu.kanade.presentation.browse
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchToolbar
|
||||
import eu.kanade.presentation.components.SelectionToolbar
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
|
@ -22,22 +26,42 @@ fun MigrateSearchScreen(
|
|||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickItem: (Manga) -> Unit,
|
||||
onLongClickItem: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
bulkFavoriteScreenModel: BulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
) {
|
||||
// KMK -->
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
// KMK <--
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
GlobalSearchToolbar(
|
||||
searchQuery = state.searchQuery,
|
||||
progress = state.progress,
|
||||
total = state.total,
|
||||
navigateUp = navigateUp,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
onSearch = onSearch,
|
||||
sourceFilter = state.sourceFilter,
|
||||
onChangeSearchFilter = onChangeSearchFilter,
|
||||
onlyShowHasResults = state.onlyShowHasResults,
|
||||
onToggleResults = onToggleResults,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
GlobalSearchToolbar(
|
||||
searchQuery = state.searchQuery,
|
||||
progress = state.progress,
|
||||
total = state.total,
|
||||
navigateUp = navigateUp,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
onSearch = onSearch,
|
||||
sourceFilter = state.sourceFilter,
|
||||
onChangeSearchFilter = onChangeSearchFilter,
|
||||
onlyShowHasResults = state.onlyShowHasResults,
|
||||
onToggleResults = onToggleResults,
|
||||
scrollBehavior = scrollBehavior,
|
||||
// KMK -->
|
||||
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
},
|
||||
) { paddingValues ->
|
||||
GlobalSearchContent(
|
||||
|
|
@ -48,6 +72,9 @@ fun MigrateSearchScreen(
|
|||
onClickSource = onClickSource,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = onLongClickItem,
|
||||
// KMK -->
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import androidx.compose.material3.TopAppBarScrollBehavior
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.presentation.browse.components.BrowseSourceFloatingActionButton
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
||||
|
|
@ -14,8 +16,12 @@ import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
|
|||
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
|
||||
import eu.kanade.presentation.browse.components.SourceSettingsButton
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.AppBarTitle
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.presentation.components.SelectionToolbar
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
|
@ -99,21 +105,38 @@ fun SourceFeedScreen(
|
|||
onSearchQueryChange: (String?) -> Unit,
|
||||
getMangaState: @Composable (Manga) -> State<Manga>,
|
||||
// KMK -->
|
||||
id: Long,
|
||||
sourceId: Long,
|
||||
onLongClickManga: (Manga) -> Unit,
|
||||
bulkFavoriteScreenModel: BulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
) {
|
||||
// KMK -->
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
// KMK <--
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
SourceFeedToolbar(
|
||||
title = name,
|
||||
searchQuery = searchQuery,
|
||||
onSearchQueryChange = onSearchQueryChange,
|
||||
scrollBehavior = scrollBehavior,
|
||||
onClickSearch = onClickSearch,
|
||||
// KMK -->
|
||||
id = id,
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
)
|
||||
SourceFeedToolbar(
|
||||
title = name,
|
||||
searchQuery = searchQuery,
|
||||
onSearchQueryChange = onSearchQueryChange,
|
||||
scrollBehavior = scrollBehavior,
|
||||
onClickSearch = onClickSearch,
|
||||
// KMK -->
|
||||
sourceId = sourceId,
|
||||
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
BrowseSourceFloatingActionButton(
|
||||
|
|
@ -135,6 +158,10 @@ fun SourceFeedScreen(
|
|||
onClickSavedSearch = onClickSavedSearch,
|
||||
onClickDelete = onClickDelete,
|
||||
onClickManga = onClickManga,
|
||||
// KMK -->
|
||||
onLongClickManga = onLongClickManga,
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -152,6 +179,10 @@ fun SourceFeedList(
|
|||
onClickSavedSearch: (SavedSearch) -> Unit,
|
||||
onClickDelete: (FeedSavedSearch) -> Unit,
|
||||
onClickManga: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
onLongClickManga: (Manga) -> Unit,
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = paddingValues + topSmallPaddingValues,
|
||||
|
|
@ -183,6 +214,10 @@ fun SourceFeedList(
|
|||
item = item,
|
||||
getMangaState = { getMangaState(it) },
|
||||
onClickManga = onClickManga,
|
||||
// KMK -->
|
||||
onLongClickManga = onLongClickManga,
|
||||
selection = selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +229,10 @@ fun SourceFeedItem(
|
|||
item: SourceFeedUI,
|
||||
getMangaState: @Composable ((Manga) -> State<Manga>),
|
||||
onClickManga: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
onLongClickManga: (Manga) -> Unit,
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
val results = item.results
|
||||
when {
|
||||
|
|
@ -208,7 +247,10 @@ fun SourceFeedItem(
|
|||
titles = item.results.orEmpty(),
|
||||
getManga = getMangaState,
|
||||
onClick = onClickManga,
|
||||
onLongClick = onClickManga,
|
||||
// KMK -->
|
||||
onLongClick = onLongClickManga,
|
||||
selection = selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -222,7 +264,8 @@ fun SourceFeedToolbar(
|
|||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
onClickSearch: (String) -> Unit,
|
||||
// KMK -->
|
||||
id: Long
|
||||
sourceId: Long,
|
||||
toggleSelectionMode: () -> Unit,
|
||||
// KMK <--
|
||||
) {
|
||||
SearchToolbar(
|
||||
|
|
@ -235,10 +278,15 @@ fun SourceFeedToolbar(
|
|||
placeholderText = stringResource(MR.strings.action_search_hint),
|
||||
// KMK -->
|
||||
actions = {
|
||||
persistentListOf(
|
||||
SourceSettingsButton(id)
|
||||
AppBarActions(
|
||||
actions = persistentListOf(
|
||||
bulkSelectionButton(toggleSelectionMode),
|
||||
)
|
||||
)
|
||||
}
|
||||
persistentListOf(
|
||||
SourceSettingsButton(sourceId),
|
||||
)
|
||||
},
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
|
||||
|
|
@ -33,6 +34,9 @@ fun BrowseSourceComfortableGrid(
|
|||
contentPadding: PaddingValues,
|
||||
onMangaClick: (Manga) -> Unit,
|
||||
onMangaLongClick: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
LazyVerticalGrid(
|
||||
columns = columns,
|
||||
|
|
@ -60,6 +64,9 @@ fun BrowseSourceComfortableGrid(
|
|||
// SY <--
|
||||
onClick = { onMangaClick(manga) },
|
||||
onLongClick = { onMangaLongClick(manga) },
|
||||
// KMK -->
|
||||
isSelected = selection?.fastAny { selected -> selected.id == manga.id } ?: false,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +86,9 @@ private fun BrowseSourceComfortableGridItem(
|
|||
// SY <--
|
||||
onClick: () -> Unit = {},
|
||||
onLongClick: () -> Unit = onClick,
|
||||
// KMK -->
|
||||
isSelected: Boolean = false,
|
||||
// KMK <--
|
||||
) {
|
||||
MangaComfortableGridItem(
|
||||
title = manga.title,
|
||||
|
|
@ -89,7 +99,13 @@ private fun BrowseSourceComfortableGridItem(
|
|||
ogUrl = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
coverAlpha = if (manga.favorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f,
|
||||
coverAlpha = when {
|
||||
// KMK -->
|
||||
isSelected -> CommonMangaItemDefaults.BrowseSelectedCoverAlpha
|
||||
// KMK <--
|
||||
manga.favorite -> CommonMangaItemDefaults.BrowseFavoriteCoverAlpha
|
||||
else -> 1f
|
||||
},
|
||||
coverBadgeStart = {
|
||||
InLibraryBadge(enabled = manga.favorite)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
|
||||
|
|
@ -33,6 +34,9 @@ fun BrowseSourceCompactGrid(
|
|||
contentPadding: PaddingValues,
|
||||
onMangaClick: (Manga) -> Unit,
|
||||
onMangaLongClick: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
LazyVerticalGrid(
|
||||
columns = columns,
|
||||
|
|
@ -60,6 +64,9 @@ fun BrowseSourceCompactGrid(
|
|||
// SY <--
|
||||
onClick = { onMangaClick(manga) },
|
||||
onLongClick = { onMangaLongClick(manga) },
|
||||
// KMK -->
|
||||
isSelected = selection?.fastAny { selected -> selected.id == manga.id } ?: false,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +86,9 @@ private fun BrowseSourceCompactGridItem(
|
|||
// SY <--
|
||||
onClick: () -> Unit = {},
|
||||
onLongClick: () -> Unit = onClick,
|
||||
// KMK -->
|
||||
isSelected: Boolean = false,
|
||||
// KMK <--
|
||||
) {
|
||||
MangaCompactGridItem(
|
||||
title = manga.title,
|
||||
|
|
@ -89,7 +99,13 @@ private fun BrowseSourceCompactGridItem(
|
|||
ogUrl = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
coverAlpha = if (manga.favorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f,
|
||||
coverAlpha = when {
|
||||
// KMK -->
|
||||
isSelected -> CommonMangaItemDefaults.BrowseSelectedCoverAlpha
|
||||
// KMK <--
|
||||
manga.favorite -> CommonMangaItemDefaults.BrowseFavoriteCoverAlpha
|
||||
else -> 1f
|
||||
},
|
||||
coverBadgeStart = {
|
||||
InLibraryBadge(enabled = manga.favorite)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
|
||||
|
|
@ -29,6 +30,9 @@ fun BrowseSourceList(
|
|||
contentPadding: PaddingValues,
|
||||
onMangaClick: (Manga) -> Unit,
|
||||
onMangaLongClick: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
LazyColumn(
|
||||
contentPadding = contentPadding + PaddingValues(vertical = 8.dp),
|
||||
|
|
@ -53,6 +57,9 @@ fun BrowseSourceList(
|
|||
// SY <--
|
||||
onClick = { onMangaClick(manga) },
|
||||
onLongClick = { onMangaLongClick(manga) },
|
||||
// KMK -->
|
||||
isSelected = selection?.fastAny { selected -> selected.id == manga.id } ?: false,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -72,6 +79,9 @@ private fun BrowseSourceListItem(
|
|||
// SY <--
|
||||
onClick: () -> Unit = {},
|
||||
onLongClick: () -> Unit = onClick,
|
||||
// KMK -->
|
||||
isSelected: Boolean = false,
|
||||
// KMK <--
|
||||
) {
|
||||
MangaListItem(
|
||||
title = manga.title,
|
||||
|
|
@ -82,7 +92,13 @@ private fun BrowseSourceListItem(
|
|||
ogUrl = manga.thumbnailUrl,
|
||||
lastModified = manga.coverLastModified,
|
||||
),
|
||||
coverAlpha = if (manga.favorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f,
|
||||
coverAlpha = when {
|
||||
// KMK -->
|
||||
isSelected -> CommonMangaItemDefaults.BrowseSelectedCoverAlpha
|
||||
// KMK <--
|
||||
manga.favorite -> CommonMangaItemDefaults.BrowseFavoriteCoverAlpha
|
||||
else -> 1f
|
||||
},
|
||||
badge = {
|
||||
InLibraryBadge(enabled = manga.favorite)
|
||||
// SY -->
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.runtime.setValue
|
|||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.DropdownMenu
|
||||
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||
import tachiyomi.i18n.MR
|
||||
|
|
@ -27,6 +28,9 @@ fun BrowseSourceSimpleToolbar(
|
|||
displayMode: LibraryDisplayMode?,
|
||||
onDisplayModeChange: (LibraryDisplayMode) -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
// KMK -->
|
||||
toggleSelectionMode: () -> Unit,
|
||||
// KMK <--
|
||||
) {
|
||||
AppBar(
|
||||
navigateUp = navigateUp,
|
||||
|
|
@ -41,6 +45,9 @@ fun BrowseSourceSimpleToolbar(
|
|||
icon = Icons.Outlined.ViewModule,
|
||||
onClick = { selectingDisplayMode = true },
|
||||
),
|
||||
// KMK -->
|
||||
bulkSelectionButton(toggleSelectionMode),
|
||||
// KMK <--
|
||||
),
|
||||
)
|
||||
DropdownMenu(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.automirrored.filled.ViewList
|
||||
import androidx.compose.material.icons.automirrored.outlined.Help
|
||||
import androidx.compose.material.icons.filled.ViewModule
|
||||
import androidx.compose.material.icons.outlined.Help
|
||||
import androidx.compose.material.icons.outlined.Public
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
|
|
@ -21,6 +20,7 @@ import eu.kanade.presentation.components.RadioMenuItem
|
|||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
|
||||
import exh.source.anyIs
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||
|
|
@ -41,6 +41,9 @@ fun BrowseSourceToolbar(
|
|||
onSettingsClick: () -> Unit,
|
||||
onSearch: (String) -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
// KMK -->
|
||||
toggleSelectionMode: () -> Unit,
|
||||
// KMK <--
|
||||
) {
|
||||
// Avoid capturing unstable source in actions lambda
|
||||
val title = source?.name
|
||||
|
|
@ -73,6 +76,9 @@ fun BrowseSourceToolbar(
|
|||
),
|
||||
)
|
||||
}
|
||||
// KMK -->
|
||||
add(bulkSelectionButton(toggleSelectionMode))
|
||||
// KMK <--
|
||||
if (isLocalSource) {
|
||||
if (isConfigurableSource && displayMode != null) {
|
||||
add(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
|
||||
import eu.kanade.presentation.library.components.MangaComfortableGridItem
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
|
@ -29,6 +30,9 @@ fun GlobalSearchCardRow(
|
|||
getManga: @Composable (Manga) -> State<Manga>,
|
||||
onClick: (Manga) -> Unit,
|
||||
onLongClick: (Manga) -> Unit,
|
||||
// KMK -->
|
||||
selection: List<Manga>,
|
||||
// KMK <--
|
||||
) {
|
||||
if (titles.isEmpty()) {
|
||||
EmptyResultItem()
|
||||
|
|
@ -47,6 +51,9 @@ fun GlobalSearchCardRow(
|
|||
isFavorite = title.favorite,
|
||||
onClick = { onClick(title) },
|
||||
onLongClick = { onLongClick(title) },
|
||||
// KMK -->
|
||||
isSelected = selection.fastAny { selected -> selected.id == title.id },
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -59,6 +66,9 @@ private fun MangaItem(
|
|||
isFavorite: Boolean,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
// KMK -->
|
||||
isSelected: Boolean = false,
|
||||
// KMK <--
|
||||
) {
|
||||
Box(modifier = Modifier.width(96.dp)) {
|
||||
MangaComfortableGridItem(
|
||||
|
|
@ -68,7 +78,13 @@ private fun MangaItem(
|
|||
coverBadgeStart = {
|
||||
InLibraryBadge(enabled = isFavorite)
|
||||
},
|
||||
coverAlpha = if (isFavorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f,
|
||||
coverAlpha = when {
|
||||
// KMK -->
|
||||
isSelected -> CommonMangaItemDefaults.BrowseSelectedCoverAlpha
|
||||
// KMK <--
|
||||
isFavorite -> CommonMangaItemDefaults.BrowseFavoriteCoverAlpha
|
||||
else -> 1f
|
||||
},
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,8 +26,11 @@ import androidx.compose.material3.VerticalDivider
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
|
@ -45,6 +48,9 @@ fun GlobalSearchToolbar(
|
|||
onlyShowHasResults: Boolean,
|
||||
onToggleResults: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
// KMK -->
|
||||
toggleSelectionMode: () -> Unit,
|
||||
// KMK <--
|
||||
) {
|
||||
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
|
||||
Box {
|
||||
|
|
@ -55,6 +61,15 @@ fun GlobalSearchToolbar(
|
|||
onClickCloseSearch = navigateUp,
|
||||
navigateUp = navigateUp,
|
||||
scrollBehavior = scrollBehavior,
|
||||
// KMK -->
|
||||
actions = {
|
||||
AppBarActions(
|
||||
actions = persistentListOf(
|
||||
bulkSelectionButton(toggleSelectionMode),
|
||||
)
|
||||
)
|
||||
},
|
||||
// KMK <--
|
||||
)
|
||||
if (progress in 1..<total) {
|
||||
LinearProgressIndicator(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package eu.kanade.presentation.components
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.BookmarkAdd
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
||||
@Composable
|
||||
fun SelectionToolbar(
|
||||
selectedCount: Int,
|
||||
onClickClearSelection: () -> Unit,
|
||||
onChangeCategoryClicked: () -> Unit,
|
||||
) {
|
||||
AppBar(
|
||||
titleContent = { Text(text = "$selectedCount") },
|
||||
actions = {
|
||||
AppBarActions(
|
||||
persistentListOf(
|
||||
AppBar.Action(
|
||||
title = stringResource(MR.strings.action_bookmark),
|
||||
icon = Icons.Filled.BookmarkAdd,
|
||||
onClick = {
|
||||
if (selectedCount > 0) {
|
||||
onChangeCategoryClicked()
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
isActionMode = true,
|
||||
onCancelActionMode = onClickClearSelection,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SelectionToolbarPreview() {
|
||||
SelectionToolbar(
|
||||
selectedCount = 9,
|
||||
{},
|
||||
{},
|
||||
)
|
||||
}
|
||||
|
|
@ -15,6 +15,8 @@ import androidx.compose.material3.SnackbarHostState
|
|||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -22,6 +24,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.zIndex
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -36,11 +39,18 @@ fun TabbedScreen(
|
|||
startIndex: Int? = null,
|
||||
searchQuery: String? = null,
|
||||
onChangeSearchQuery: (String?) -> Unit = {},
|
||||
// KMK -->
|
||||
bulkFavoriteScreenModel: BulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val state = rememberPagerState { tabs.size }
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
// KMK <--
|
||||
|
||||
LaunchedEffect(startIndex) {
|
||||
if (startIndex != null) {
|
||||
state.scrollToPage(startIndex)
|
||||
|
|
@ -51,14 +61,23 @@ fun TabbedScreen(
|
|||
topBar = {
|
||||
val tab = tabs[state.currentPage]
|
||||
val searchEnabled = tab.searchEnabled
|
||||
|
||||
SearchToolbar(
|
||||
titleContent = { AppBarTitle(stringResource(titleRes)) },
|
||||
searchEnabled = searchEnabled,
|
||||
searchQuery = if (searchEnabled) searchQuery else null,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
actions = { AppBarActions(tab.actions) },
|
||||
)
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
SearchToolbar(
|
||||
titleContent = { AppBarTitle(stringResource(titleRes)) },
|
||||
searchEnabled = searchEnabled,
|
||||
searchQuery = if (searchEnabled) searchQuery else null,
|
||||
onChangeSearchQuery = onChangeSearchQuery,
|
||||
actions = { AppBarActions(tab.actions) },
|
||||
)
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
) { contentPadding ->
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ object CommonMangaItemDefaults {
|
|||
val GridVerticalSpacer = 4.dp
|
||||
|
||||
const val BrowseFavoriteCoverAlpha = 0.34f
|
||||
|
||||
// KMK -->
|
||||
const val BrowseSelectedCoverAlpha = 0.17f
|
||||
// KMK <--
|
||||
}
|
||||
|
||||
private val ContinueReadingButtonSize = 28.dp
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
package eu.kanade.presentation.manga
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.FlowColumn
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
|
@ -57,3 +61,111 @@ fun DuplicateMangaDialog(
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
@Composable
|
||||
fun DuplicateMangasDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onAllowAllDuplicate: () -> Unit,
|
||||
onSkipAllDuplicate: () -> Unit,
|
||||
onOpenManga: () -> Unit,
|
||||
onAllowDuplicate: () -> Unit,
|
||||
onSkipDuplicate: () -> Unit,
|
||||
duplicatedName: String,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = {
|
||||
Text(text = duplicatedName)
|
||||
},
|
||||
text = {
|
||||
Text(text = stringResource(MR.strings.confirm_add_duplicate_manga))
|
||||
},
|
||||
confirmButton = {
|
||||
FlowRow(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
FlowColumn {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
onAllowDuplicate()
|
||||
},
|
||||
Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.action_allow_duplicate_manga))
|
||||
}
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
onAllowAllDuplicate()
|
||||
},
|
||||
Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.action_allow_all_duplicate_manga))
|
||||
}
|
||||
}
|
||||
|
||||
FlowColumn {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
onSkipDuplicate()
|
||||
},
|
||||
Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.action_skip_duplicate_manga))
|
||||
}
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
onSkipAllDuplicate()
|
||||
},
|
||||
Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.action_skip_all_duplicate_manga))
|
||||
}
|
||||
}
|
||||
|
||||
FlowColumn {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
onOpenManga()
|
||||
},
|
||||
Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.action_show_manga))
|
||||
}
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
},
|
||||
Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.action_cancel))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun DuplicateMangasDialogPreview() {
|
||||
DuplicateMangasDialog(
|
||||
onDismissRequest = { },
|
||||
onAllowAllDuplicate = { },
|
||||
onSkipAllDuplicate = { },
|
||||
onOpenManga = { },
|
||||
onAllowDuplicate = { },
|
||||
onSkipDuplicate = { },
|
||||
duplicatedName = "Berserk",
|
||||
)
|
||||
}
|
||||
// KMK <--
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ data class BrowseTab(
|
|||
val extensionsScreenModel = rememberScreenModel { ExtensionsScreenModel() }
|
||||
val extensionsState by extensionsScreenModel.state.collectAsState()
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
// KMK <--
|
||||
|
||||
TabbedScreen(
|
||||
titleRes = MR.strings.browse,
|
||||
// SY -->
|
||||
|
|
@ -76,7 +80,7 @@ data class BrowseTab(
|
|||
)
|
||||
} else if (feedTabInFront) {
|
||||
persistentListOf(
|
||||
feedTab(),
|
||||
feedTab(bulkFavoriteScreenModel),
|
||||
sourcesTab(),
|
||||
extensionsTab(extensionsScreenModel),
|
||||
migrateSourceTab(),
|
||||
|
|
@ -84,7 +88,7 @@ data class BrowseTab(
|
|||
} else {
|
||||
persistentListOf(
|
||||
sourcesTab(),
|
||||
feedTab(),
|
||||
feedTab(bulkFavoriteScreenModel),
|
||||
extensionsTab(extensionsScreenModel),
|
||||
migrateSourceTab(),
|
||||
)
|
||||
|
|
@ -93,6 +97,9 @@ data class BrowseTab(
|
|||
// SY <--
|
||||
searchQuery = extensionsState.searchQuery,
|
||||
onChangeSearchQuery = extensionsScreenModel::search,
|
||||
// KMK -->
|
||||
bulkFavoriteScreenModel = bulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,484 @@
|
|||
package eu.kanade.tachiyomi.ui.browse
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Checklist
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedback
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.track.interactor.AddTracks
|
||||
import eu.kanade.presentation.browse.components.RemoveMangaDialog
|
||||
import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.manga.DuplicateMangaDialog
|
||||
import eu.kanade.presentation.manga.DuplicateMangasDialog
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
import eu.kanade.tachiyomi.util.removeCovers
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.mutate
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import tachiyomi.core.common.preference.CheckboxState
|
||||
import tachiyomi.core.common.preference.mapAsCheckboxState
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
import tachiyomi.domain.category.interactor.SetMangaCategories
|
||||
import tachiyomi.domain.category.model.Category
|
||||
import tachiyomi.domain.chapter.interactor.SetMangaDefaultChapterFlags
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.toMangaUpdate
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.time.Instant
|
||||
|
||||
class BulkFavoriteScreenModel(
|
||||
initialState: State = State(),
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
||||
private val getCategories: GetCategories = Injekt.get(),
|
||||
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
||||
private val updateManga: UpdateManga = Injekt.get(),
|
||||
private val coverCache: CoverCache = Injekt.get(),
|
||||
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
|
||||
private val addTracks: AddTracks = Injekt.get(),
|
||||
) : StateScreenModel<BulkFavoriteScreenModel.State>(initialState) {
|
||||
|
||||
fun backHandler() {
|
||||
toggleSelectionMode()
|
||||
}
|
||||
|
||||
fun toggleSelectionMode() {
|
||||
if (state.value.selectionMode) {
|
||||
clearSelection()
|
||||
}
|
||||
mutableState.update { it.copy(selectionMode = !it.selectionMode) }
|
||||
}
|
||||
|
||||
private fun clearSelection() {
|
||||
mutableState.update { it.copy(selection = persistentListOf()) }
|
||||
}
|
||||
|
||||
fun toggleSelection(manga: Manga) {
|
||||
mutableState.update { state ->
|
||||
val newSelection = state.selection.mutate { list ->
|
||||
if (list.fastAny { it.id == manga.id }) {
|
||||
list.removeAll { it.id == manga.id }
|
||||
} else {
|
||||
list.add(manga)
|
||||
}
|
||||
}
|
||||
state.copy(selection = newSelection)
|
||||
}.also {
|
||||
if (state.value.selection.isEmpty()) {
|
||||
toggleSelectionMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addFavorite(startIdx: Int = 0) {
|
||||
screenModelScope.launch {
|
||||
val mangaWithDup = getDuplicateLibraryManga(startIdx)
|
||||
if (mangaWithDup != null) {
|
||||
setDialog(Dialog.AllowDuplicate(mangaWithDup))
|
||||
} else {
|
||||
addFavoriteDuplicate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) {
|
||||
screenModelScope.launch {
|
||||
val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection
|
||||
if (mangaList.isEmpty()) {
|
||||
toggleSelectionMode()
|
||||
return@launch
|
||||
}
|
||||
val categories = getCategories()
|
||||
val defaultCategoryId = libraryPreferences.defaultCategory().get()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
|
||||
|
||||
when {
|
||||
// Default category set
|
||||
defaultCategory != null -> {
|
||||
setMangasCategories(mangaList, listOf(defaultCategory.id), emptyList())
|
||||
}
|
||||
|
||||
// Automatic 'Default' or no categories
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> {
|
||||
// Automatic 'Default' or no categories
|
||||
setMangasCategories(mangaList, emptyList(), emptyList())
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Get indexes of the common categories to preselect.
|
||||
val common = getCommonCategories(mangaList)
|
||||
// Get indexes of the mix categories to preselect.
|
||||
val mix = getMixCategories(mangaList)
|
||||
val preselected = categories
|
||||
.map {
|
||||
when (it) {
|
||||
in common -> CheckboxState.State.Checked(it)
|
||||
in mix -> CheckboxState.TriState.Exclude(it)
|
||||
else -> CheckboxState.State.None(it)
|
||||
}
|
||||
}
|
||||
.toImmutableList()
|
||||
setDialog(Dialog.ChangeMangasCategory(mangaList, preselected))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getNotDuplicateLibraryMangas(): List<Manga> {
|
||||
return state.value.selection.filterNot { manga ->
|
||||
getDuplicateLibraryManga.await(manga).isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getDuplicateLibraryManga(startIdx: Int = 0): Pair<Int, Manga>? {
|
||||
val mangas = state.value.selection
|
||||
mangas.fastForEachIndexed { index, manga ->
|
||||
if (index < startIdx) return@fastForEachIndexed
|
||||
val dup = getDuplicateLibraryManga.await(manga)
|
||||
if (dup.isEmpty()) return@fastForEachIndexed
|
||||
return Pair(index, dup.first())
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun removeDuplicateSelectedManga(index: Int) {
|
||||
mutableState.update { state ->
|
||||
val newSelection = state.selection.mutate { list ->
|
||||
list.removeAt(index)
|
||||
}
|
||||
state.copy(selection = newSelection)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk update categories of manga using old and new common categories.
|
||||
*
|
||||
* @param mangaList the list of manga to move.
|
||||
* @param addCategories the categories to add for all mangas.
|
||||
* @param removeCategories the categories to remove in all mangas.
|
||||
*/
|
||||
fun setMangasCategories(mangaList: List<Manga>, addCategories: List<Long>, removeCategories: List<Long>) {
|
||||
screenModelScope.launchNonCancellable {
|
||||
mangaList.fastForEach { manga ->
|
||||
val categoryIds = getCategories.await(manga.id)
|
||||
.map { it.id }
|
||||
.subtract(removeCategories.toSet())
|
||||
.plus(addCategories)
|
||||
.toList()
|
||||
|
||||
moveMangaToCategoriesAndAddToLibrary(manga, categoryIds)
|
||||
}
|
||||
}
|
||||
toggleSelectionMode()
|
||||
}
|
||||
|
||||
private fun moveMangaToCategoriesAndAddToLibrary(manga: Manga, categories: List<Long>) {
|
||||
moveMangaToCategory(manga.id, categories)
|
||||
if (manga.favorite) return
|
||||
|
||||
screenModelScope.launchIO {
|
||||
updateManga.awaitUpdateFavorite(manga.id, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun moveMangaToCategory(mangaId: Long, categoryIds: List<Long>) {
|
||||
screenModelScope.launchIO {
|
||||
setMangaCategories.await(mangaId, categoryIds)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the common categories for the given list of manga.
|
||||
*
|
||||
* @param mangas the list of manga.
|
||||
*/
|
||||
private suspend fun getCommonCategories(mangas: List<Manga>): Collection<Category> {
|
||||
if (mangas.isEmpty()) return emptyList()
|
||||
return mangas
|
||||
.map { getCategories.await(it.id).toSet() }
|
||||
.reduce { set1, set2 -> set1.intersect(set2) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mix (non-common) categories for the given list of manga.
|
||||
*
|
||||
* @param mangas the list of manga.
|
||||
*/
|
||||
private suspend fun getMixCategories(mangas: List<Manga>): Collection<Category> {
|
||||
if (mangas.isEmpty()) return emptyList()
|
||||
val mangaCategories = mangas.map { getCategories.await(it.id).toSet() }
|
||||
val common = mangaCategories.reduce { set1, set2 -> set1.intersect(set2) }
|
||||
return mangaCategories.flatten().distinct().subtract(common)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user categories.
|
||||
*
|
||||
* @return List of categories, not including the default category
|
||||
*/
|
||||
suspend fun getCategories(): List<Category> {
|
||||
return getCategories.subscribe()
|
||||
.firstOrNull()
|
||||
?.filterNot { it.isSystemCategory }
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
suspend fun getDuplicateLibraryManga(manga: Manga): Manga? {
|
||||
return getDuplicateLibraryManga.await(manga).getOrNull(0)
|
||||
}
|
||||
|
||||
private fun moveMangaToCategories(manga: Manga, vararg categories: Category) {
|
||||
moveMangaToCategories(manga, categories.filter { it.id != 0L }.map { it.id })
|
||||
}
|
||||
|
||||
fun moveMangaToCategories(manga: Manga, categoryIds: List<Long>) {
|
||||
screenModelScope.launchIO {
|
||||
setMangaCategories.await(
|
||||
mangaId = manga.id,
|
||||
categoryIds = categoryIds.toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes a manga from the library.
|
||||
*
|
||||
* @param manga the manga to update.
|
||||
*/
|
||||
fun changeMangaFavorite(manga: Manga) {
|
||||
val source = sourceManager.getOrStub(manga.source)
|
||||
|
||||
screenModelScope.launch {
|
||||
var new = manga.copy(
|
||||
favorite = !manga.favorite,
|
||||
dateAdded = when (manga.favorite) {
|
||||
true -> 0
|
||||
false -> Instant.now().toEpochMilli()
|
||||
},
|
||||
)
|
||||
// TODO: also allow deleting chapters when remove favorite (just like in [MangaScreenModel])
|
||||
if (!new.favorite) {
|
||||
new = new.removeCovers(coverCache)
|
||||
} else {
|
||||
setMangaDefaultChapterFlags.await(manga)
|
||||
addTracks.bindEnhancedTrackers(manga, source)
|
||||
}
|
||||
|
||||
updateManga.await(new.toMangaUpdate())
|
||||
}
|
||||
}
|
||||
|
||||
fun addFavorite(manga: Manga) {
|
||||
screenModelScope.launch {
|
||||
val categories = getCategories()
|
||||
val defaultCategoryId = libraryPreferences.defaultCategory().get()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
|
||||
|
||||
when {
|
||||
// Default category set
|
||||
defaultCategory != null -> {
|
||||
moveMangaToCategories(manga, defaultCategory)
|
||||
changeMangaFavorite(manga)
|
||||
}
|
||||
|
||||
// Automatic 'Default' or no categories
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> {
|
||||
moveMangaToCategories(manga)
|
||||
changeMangaFavorite(manga)
|
||||
}
|
||||
|
||||
// Choose a category
|
||||
else -> {
|
||||
val preselectedIds = getCategories.await(manga.id).map { it.id }
|
||||
setDialog(
|
||||
Dialog.ChangeMangaCategory(
|
||||
manga,
|
||||
categories.mapAsCheckboxState { it.id in preselectedIds }.toImmutableList(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addRemoveManga(manga: Manga, haptic: HapticFeedback? = null) {
|
||||
screenModelScope.launchIO {
|
||||
val duplicateManga = getDuplicateLibraryManga(manga)
|
||||
when {
|
||||
manga.favorite -> setDialog(
|
||||
Dialog.RemoveManga(manga),
|
||||
)
|
||||
duplicateManga != null -> setDialog(
|
||||
Dialog.AddDuplicateManga(
|
||||
manga,
|
||||
duplicateManga,
|
||||
),
|
||||
)
|
||||
else -> addFavorite(manga)
|
||||
}
|
||||
haptic?.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
}
|
||||
|
||||
fun setDialog(dialog: Dialog?) {
|
||||
mutableState.update {
|
||||
it.copy(dialog = dialog)
|
||||
}
|
||||
}
|
||||
|
||||
fun dismissDialog() {
|
||||
mutableState.update {
|
||||
it.copy(dialog = null)
|
||||
}
|
||||
}
|
||||
|
||||
interface Dialog {
|
||||
data class RemoveManga(val manga: Manga) : Dialog
|
||||
data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
|
||||
data class ChangeMangaCategory(
|
||||
val manga: Manga,
|
||||
val initialSelection: ImmutableList<CheckboxState.State<Category>>,
|
||||
) : Dialog
|
||||
data class ChangeMangasCategory(
|
||||
val mangas: List<Manga>,
|
||||
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||
) : Dialog
|
||||
data class AllowDuplicate(val duplicatedManga: Pair<Int, Manga>) : Dialog
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class State(
|
||||
val dialog: Dialog? = null,
|
||||
val selection: PersistentList<Manga> = persistentListOf(),
|
||||
val selectionMode: Boolean = false,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddDuplicateMangaDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
val dialog = bulkFavoriteState.dialog as BulkFavoriteScreenModel.Dialog.AddDuplicateManga
|
||||
|
||||
DuplicateMangaDialog(
|
||||
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
|
||||
onConfirm = { bulkFavoriteScreenModel.addFavorite(dialog.manga) },
|
||||
onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RemoveMangaDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
val dialog = bulkFavoriteState.dialog as BulkFavoriteScreenModel.Dialog.RemoveManga
|
||||
|
||||
RemoveMangaDialog(
|
||||
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
|
||||
onConfirm = {
|
||||
bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga)
|
||||
},
|
||||
mangaToRemove = dialog.manga,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChangeMangaCategoryDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
val dialog = bulkFavoriteState.dialog as BulkFavoriteScreenModel.Dialog.ChangeMangaCategory
|
||||
|
||||
ChangeCategoryDialog(
|
||||
initialSelection = dialog.initialSelection,
|
||||
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
|
||||
onEditCategories = { navigator.push(CategoryScreen()) },
|
||||
onConfirm = { include, _ ->
|
||||
bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga)
|
||||
bulkFavoriteScreenModel.moveMangaToCategories(dialog.manga, include)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChangeMangasCategoryDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
val dialog = bulkFavoriteState.dialog as BulkFavoriteScreenModel.Dialog.ChangeMangasCategory
|
||||
|
||||
ChangeCategoryDialog(
|
||||
initialSelection = dialog.initialSelection,
|
||||
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
|
||||
onEditCategories = { navigator.push(CategoryScreen()) },
|
||||
onConfirm = { include, exclude ->
|
||||
bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AllowDuplicateDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
val dialog = bulkFavoriteState.dialog as BulkFavoriteScreenModel.Dialog.AllowDuplicate
|
||||
|
||||
DuplicateMangasDialog(
|
||||
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
|
||||
onAllowAllDuplicate = bulkFavoriteScreenModel::addFavoriteDuplicate,
|
||||
onSkipAllDuplicate = {
|
||||
bulkFavoriteScreenModel.addFavoriteDuplicate(skipAllDuplicates = true)
|
||||
},
|
||||
onOpenManga = {
|
||||
navigator.push(MangaScreen(dialog.duplicatedManga.second.id))
|
||||
},
|
||||
onAllowDuplicate = {
|
||||
bulkFavoriteScreenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1)
|
||||
},
|
||||
onSkipDuplicate = {
|
||||
bulkFavoriteScreenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first)
|
||||
bulkFavoriteScreenModel.addFavorite(startIdx = dialog.duplicatedManga.first)
|
||||
},
|
||||
duplicatedName = dialog.duplicatedManga.second.title,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun bulkSelectionButton(toggleSelectionMode: () -> Unit) =
|
||||
AppBar.Action(
|
||||
title = stringResource(MR.strings.action_bulk_select),
|
||||
icon = Icons.Outlined.Checklist,
|
||||
onClick = toggleSelectionMode,
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun BulkSelectionButtonPreview() {
|
||||
bulkSelectionButton { }
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.browse.feed
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -7,6 +8,7 @@ import androidx.compose.runtime.DisposableEffect
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.core.stack.StackEvent
|
||||
|
|
@ -18,7 +20,15 @@ import eu.kanade.presentation.browse.FeedDeleteConfirmDialog
|
|||
import eu.kanade.presentation.browse.FeedScreen
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.TabContent
|
||||
import eu.kanade.tachiyomi.ui.browse.AddDuplicateMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangaCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
|
||||
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
|
@ -29,11 +39,29 @@ import tachiyomi.i18n.sy.SYMR
|
|||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
||||
@Composable
|
||||
fun Screen.feedTab(): TabContent {
|
||||
fun Screen.feedTab(
|
||||
// KMK -->
|
||||
bulkFavoriteScreenModel: BulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
): TabContent {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val screenModel = rememberScreenModel { FeedScreenModel() }
|
||||
val state by screenModel.state.collectAsState()
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
BackHandler(enabled = bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.backHandler()
|
||||
}
|
||||
|
||||
LaunchedEffect(bulkFavoriteState.selectionMode) {
|
||||
HomeScreen.showBottomNav(!bulkFavoriteState.selectionMode)
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
DisposableEffect(navigator.lastEvent) {
|
||||
if (navigator.lastEvent == StackEvent.Push) {
|
||||
screenModel.pushed = true
|
||||
|
|
@ -58,6 +86,9 @@ fun Screen.feedTab(): TabContent {
|
|||
screenModel.openAddDialog()
|
||||
},
|
||||
),
|
||||
// KMK -->
|
||||
bulkSelectionButton(bulkFavoriteScreenModel::toggleSelectionMode),
|
||||
// KMK <--
|
||||
),
|
||||
content = { contentPadding, snackbarHostState ->
|
||||
FeedScreen(
|
||||
|
|
@ -89,8 +120,24 @@ fun Screen.feedTab(): TabContent {
|
|||
},
|
||||
onClickDelete = screenModel::openDeleteDialog,
|
||||
onClickManga = { manga ->
|
||||
navigator.push(MangaScreen(manga.id, true))
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(manga)
|
||||
} else {
|
||||
// KMK <--
|
||||
navigator.push(MangaScreen(manga.id, true))
|
||||
}
|
||||
},
|
||||
// KMK -->
|
||||
onLongClickManga = { manga ->
|
||||
if (!bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.addRemoveManga(manga, haptic)
|
||||
} else {
|
||||
navigator.push(MangaScreen(manga.id, true))
|
||||
}
|
||||
},
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
onRefresh = screenModel::init,
|
||||
getMangaState = { manga, source -> screenModel.getManga(initialManga = manga, source = source) },
|
||||
)
|
||||
|
|
@ -133,6 +180,22 @@ fun Screen.feedTab(): TabContent {
|
|||
}
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.AddDuplicateManga ->
|
||||
AddDuplicateMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.RemoveManga ->
|
||||
RemoveMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory ->
|
||||
ChangeMangaCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
val internalErrString = stringResource(MR.strings.internal_error)
|
||||
val tooManyFeedsString = stringResource(SYMR.strings.too_many_in_feed)
|
||||
LaunchedEffect(Unit) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.browse.migration.search
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -8,6 +9,9 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
|||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.browse.MigrateSearchScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
|
||||
|
|
@ -23,6 +27,15 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
|
|||
val dialogScreenModel = rememberScreenModel { MigrateSearchScreenDialogScreenModel(mangaId = mangaId) }
|
||||
val dialogState by dialogScreenModel.state.collectAsState()
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
BackHandler(enabled = bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelectionMode()
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
MigrateSearchScreen(
|
||||
state = state,
|
||||
fromSourceId = state.fromSourceId,
|
||||
|
|
@ -38,15 +51,35 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
|
|||
// SY <--
|
||||
},
|
||||
onClickItem = {
|
||||
// SY -->
|
||||
navigator.items
|
||||
.filterIsInstance<MigrationListScreen>()
|
||||
.last()
|
||||
.newSelectedItem = mangaId to it.id
|
||||
navigator.popUntil { it is MigrationListScreen }
|
||||
// SY <--
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(it)
|
||||
} else
|
||||
// KMK <--
|
||||
{
|
||||
// SY -->
|
||||
navigator.items
|
||||
.filterIsInstance<MigrationListScreen>()
|
||||
.last()
|
||||
.newSelectedItem = mangaId to it.id
|
||||
navigator.popUntil { it is MigrationListScreen }
|
||||
// SY <--
|
||||
}
|
||||
},
|
||||
onLongClickItem = { navigator.push(MangaScreen(it.id, true)) },
|
||||
// KMK -->
|
||||
bulkFavoriteScreenModel = bulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
)
|
||||
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.browse.migration.search
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -14,9 +15,15 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
|||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.browse.BrowseSourceContent
|
||||
import eu.kanade.presentation.browse.components.BrowseSourceFloatingActionButton
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.presentation.components.SelectionToolbar
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterDialog
|
||||
|
|
@ -44,15 +51,43 @@ data class SourceSearchScreen(
|
|||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
BackHandler(enabled = bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelectionMode()
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
SearchToolbar(
|
||||
searchQuery = state.toolbarQuery ?: "",
|
||||
onChangeSearchQuery = screenModel::setToolbarQuery,
|
||||
onClickCloseSearch = navigator::pop,
|
||||
onSearch = screenModel::search,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
SearchToolbar(
|
||||
searchQuery = state.toolbarQuery ?: "",
|
||||
onChangeSearchQuery = screenModel::setToolbarQuery,
|
||||
onClickCloseSearch = navigator::pop,
|
||||
onSearch = screenModel::search,
|
||||
scrollBehavior = scrollBehavior,
|
||||
// KMK -->
|
||||
actions = {
|
||||
AppBarActions(
|
||||
actions = persistentListOf(
|
||||
bulkSelectionButton(bulkFavoriteScreenModel::toggleSelectionMode),
|
||||
)
|
||||
)
|
||||
},
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
// SY -->
|
||||
|
|
@ -96,8 +131,19 @@ data class SourceSearchScreen(
|
|||
},
|
||||
onHelpClick = { uriHandler.openUri(Constants.URL_HELP) },
|
||||
onLocalSourceHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) },
|
||||
onMangaClick = openMigrateDialog,
|
||||
onMangaClick = { manga ->
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(manga)
|
||||
} else {
|
||||
// KMK <--
|
||||
openMigrateDialog(manga)
|
||||
}
|
||||
},
|
||||
onMangaLongClick = { navigator.push(MangaScreen(it.id, true)) },
|
||||
// KMK -->
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -123,5 +169,15 @@ data class SourceSearchScreen(
|
|||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.browse.source.browse
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -43,11 +44,15 @@ import eu.kanade.presentation.browse.components.RemoveMangaDialog
|
|||
import eu.kanade.presentation.browse.components.SavedSearchCreateDialog
|
||||
import eu.kanade.presentation.browse.components.SavedSearchDeleteDialog
|
||||
import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
||||
import eu.kanade.presentation.components.SelectionToolbar
|
||||
import eu.kanade.presentation.manga.DuplicateMangaDialog
|
||||
import eu.kanade.presentation.util.AssistContentScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
|
||||
|
|
@ -133,6 +138,15 @@ data class BrowseSourceScreen(
|
|||
)
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
BackHandler(enabled = bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.backHandler()
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
LaunchedEffect(screenModel.source) {
|
||||
assistUrl = (screenModel.source as? HttpSource)?.baseUrl
|
||||
}
|
||||
|
|
@ -140,18 +154,31 @@ data class BrowseSourceScreen(
|
|||
Scaffold(
|
||||
topBar = {
|
||||
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
|
||||
BrowseSourceToolbar(
|
||||
searchQuery = state.toolbarQuery,
|
||||
onSearchQueryChange = screenModel::setToolbarQuery,
|
||||
source = screenModel.source,
|
||||
displayMode = screenModel.displayMode,
|
||||
onDisplayModeChange = { screenModel.displayMode = it },
|
||||
navigateUp = navigateUp,
|
||||
onWebViewClick = onWebViewClick,
|
||||
onHelpClick = onHelpClick,
|
||||
onSettingsClick = { navigator.push(SourcePreferencesScreen(sourceId)) },
|
||||
onSearch = screenModel::search,
|
||||
)
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
BrowseSourceToolbar(
|
||||
searchQuery = state.toolbarQuery,
|
||||
onSearchQueryChange = screenModel::setToolbarQuery,
|
||||
source = screenModel.source,
|
||||
displayMode = screenModel.displayMode,
|
||||
onDisplayModeChange = { screenModel.displayMode = it },
|
||||
navigateUp = navigateUp,
|
||||
onWebViewClick = onWebViewClick,
|
||||
onHelpClick = onHelpClick,
|
||||
onSettingsClick = { navigator.push(SourcePreferencesScreen(sourceId)) },
|
||||
onSearch = screenModel::search,
|
||||
// KMK -->
|
||||
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
@ -244,23 +271,42 @@ data class BrowseSourceScreen(
|
|||
onWebViewClick = onWebViewClick,
|
||||
onHelpClick = { uriHandler.openUri(Constants.URL_HELP) },
|
||||
onLocalSourceHelpClick = onHelpClick,
|
||||
onMangaClick = { navigator.push(MangaScreen(it.id, true, smartSearchConfig)) },
|
||||
onMangaLongClick = { manga ->
|
||||
scope.launchIO {
|
||||
val duplicateManga = screenModel.getDuplicateLibraryManga(manga)
|
||||
when {
|
||||
manga.favorite -> screenModel.setDialog(BrowseSourceScreenModel.Dialog.RemoveManga(manga))
|
||||
duplicateManga != null -> screenModel.setDialog(
|
||||
BrowseSourceScreenModel.Dialog.AddDuplicateManga(
|
||||
manga,
|
||||
duplicateManga,
|
||||
),
|
||||
)
|
||||
else -> screenModel.addFavorite(manga)
|
||||
}
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
onMangaClick = {
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(it)
|
||||
} else {
|
||||
// KMK <--
|
||||
navigator.push(MangaScreen(it.id, true, smartSearchConfig))
|
||||
}
|
||||
},
|
||||
onMangaLongClick = { manga ->
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
navigator.push(MangaScreen(manga.id, true))
|
||||
} else {
|
||||
// KMK <--
|
||||
scope.launchIO {
|
||||
val duplicateManga = screenModel.getDuplicateLibraryManga(manga)
|
||||
when {
|
||||
manga.favorite -> screenModel.setDialog(
|
||||
BrowseSourceScreenModel.Dialog.RemoveManga(manga)
|
||||
)
|
||||
duplicateManga != null -> screenModel.setDialog(
|
||||
BrowseSourceScreenModel.Dialog.AddDuplicateManga(
|
||||
manga,
|
||||
duplicateManga,
|
||||
),
|
||||
)
|
||||
else -> screenModel.addFavorite(manga)
|
||||
}
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
}
|
||||
},
|
||||
// KMK -->
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -349,6 +395,16 @@ data class BrowseSourceScreen(
|
|||
else -> {}
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
queryEvent.receiveAsFlow()
|
||||
.collectLatest {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
|
|
@ -14,6 +15,12 @@ import eu.kanade.presentation.browse.components.SourceFeedAddDialog
|
|||
import eu.kanade.presentation.browse.components.SourceFeedDeleteDialog
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.ui.browse.AddDuplicateMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangaCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterDialog
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
|
|
@ -33,6 +40,13 @@ class SourceFeedScreen(val sourceId: Long) : Screen() {
|
|||
val navigator = LocalNavigator.currentOrThrow
|
||||
val context = LocalContext.current
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
val haptic = LocalHapticFeedback.current
|
||||
// KMK <--
|
||||
|
||||
SourceFeedScreen(
|
||||
name = screenModel.source.name,
|
||||
isLoading = state.isLoading,
|
||||
|
|
@ -43,13 +57,29 @@ class SourceFeedScreen(val sourceId: Long) : Screen() {
|
|||
onClickLatest = { onLatestClick(navigator, screenModel.source) },
|
||||
onClickSavedSearch = { onSavedSearchClick(navigator, screenModel.source, it) },
|
||||
onClickDelete = screenModel::openDeleteFeed,
|
||||
onClickManga = { onMangaClick(navigator, it) },
|
||||
onClickManga = {
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(it)
|
||||
} else {
|
||||
// KMK <--
|
||||
onMangaClick(navigator, it)
|
||||
}
|
||||
},
|
||||
onClickSearch = { onSearchClick(navigator, screenModel.source, it) },
|
||||
searchQuery = state.searchQuery,
|
||||
onSearchQueryChange = screenModel::search,
|
||||
getMangaState = { screenModel.getManga(initialManga = it) },
|
||||
// KMK -->
|
||||
id = screenModel.source.id,
|
||||
sourceId = screenModel.source.id,
|
||||
onLongClickManga = { manga ->
|
||||
if (!bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.addRemoveManga(manga, haptic)
|
||||
} else {
|
||||
navigator.push(MangaScreen(manga.id, true))
|
||||
}
|
||||
},
|
||||
bulkFavoriteScreenModel = bulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
)
|
||||
|
||||
|
|
@ -140,8 +170,30 @@ class SourceFeedScreen(val sourceId: Long) : Screen() {
|
|||
null -> Unit
|
||||
}
|
||||
|
||||
BackHandler(state.searchQuery != null) {
|
||||
screenModel.search(null)
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.AddDuplicateManga ->
|
||||
AddDuplicateMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.RemoveManga ->
|
||||
RemoveMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory ->
|
||||
ChangeMangaCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
BackHandler(state.searchQuery != null || bulkFavoriteState.selectionMode) {
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.backHandler()
|
||||
} else {
|
||||
// KMK <--
|
||||
screenModel.search(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.browse.source.globalsearch
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
|
|
@ -7,11 +8,18 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.browse.GlobalSearchScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.ui.browse.AddDuplicateMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangaCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
|
|
@ -36,6 +44,17 @@ class GlobalSearchScreen(
|
|||
mutableStateOf(searchQuery.isNotEmpty() && !extensionFilter.isNullOrEmpty() && state.total == 1)
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
BackHandler(enabled = bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.backHandler()
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
if (showSingleLoadingScreen) {
|
||||
LoadingScreen()
|
||||
|
||||
|
|
@ -66,9 +85,44 @@ class GlobalSearchScreen(
|
|||
onClickSource = {
|
||||
navigator.push(BrowseSourceScreen(it.id, state.searchQuery))
|
||||
},
|
||||
onClickItem = { navigator.push(MangaScreen(it.id, true)) },
|
||||
onLongClickItem = { navigator.push(MangaScreen(it.id, true)) },
|
||||
onClickItem = {
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(it)
|
||||
} else {
|
||||
// KMK <--
|
||||
navigator.push(MangaScreen(it.id, true))
|
||||
}
|
||||
},
|
||||
onLongClickItem = { manga ->
|
||||
// KMK -->
|
||||
if (!bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.addRemoveManga(manga, haptic)
|
||||
} else {
|
||||
// KMK <--
|
||||
navigator.push(MangaScreen(manga.id, true))
|
||||
}
|
||||
},
|
||||
// KMK -->
|
||||
bulkFavoriteScreenModel = bulkFavoriteScreenModel,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.AddDuplicateManga ->
|
||||
AddDuplicateMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.RemoveManga ->
|
||||
RemoveMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory ->
|
||||
ChangeMangaCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package exh.md.follows
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -18,8 +19,12 @@ import eu.kanade.presentation.browse.BrowseSourceContent
|
|||
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
|
||||
import eu.kanade.presentation.browse.components.RemoveMangaDialog
|
||||
import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
||||
import eu.kanade.presentation.components.SelectionToolbar
|
||||
import eu.kanade.presentation.manga.DuplicateMangaDialog
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
|
|
@ -38,17 +43,39 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
|
|||
val screenModel = rememberScreenModel { MangaDexFollowsScreenModel(sourceId) }
|
||||
val state by screenModel.state.collectAsState()
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
BackHandler(enabled = bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelectionMode()
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
BrowseSourceSimpleToolbar(
|
||||
title = stringResource(SYMR.strings.mangadex_follows),
|
||||
displayMode = screenModel.displayMode,
|
||||
onDisplayModeChange = { screenModel.displayMode = it },
|
||||
navigateUp = navigator::pop,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
BrowseSourceSimpleToolbar(
|
||||
title = stringResource(SYMR.strings.mangadex_follows),
|
||||
displayMode = screenModel.displayMode,
|
||||
onDisplayModeChange = { screenModel.displayMode = it },
|
||||
navigateUp = navigator::pop,
|
||||
scrollBehavior = scrollBehavior,
|
||||
// KMK -->
|
||||
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
},
|
||||
snackbarHost = {
|
||||
SnackbarHost(hostState = snackbarHostState)
|
||||
|
|
@ -69,23 +96,42 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
|
|||
onWebViewClick = null,
|
||||
onHelpClick = null,
|
||||
onLocalSourceHelpClick = null,
|
||||
onMangaClick = { navigator.push(MangaScreen(it.id, true)) },
|
||||
onMangaLongClick = { manga ->
|
||||
scope.launchIO {
|
||||
val duplicateManga = screenModel.getDuplicateLibraryManga(manga)
|
||||
when {
|
||||
manga.favorite -> screenModel.setDialog(BrowseSourceScreenModel.Dialog.RemoveManga(manga))
|
||||
duplicateManga != null -> screenModel.setDialog(
|
||||
BrowseSourceScreenModel.Dialog.AddDuplicateManga(
|
||||
manga,
|
||||
duplicateManga,
|
||||
),
|
||||
)
|
||||
else -> screenModel.addFavorite(manga)
|
||||
}
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
onMangaClick = {
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(it)
|
||||
} else {
|
||||
// KMK <--
|
||||
navigator.push(MangaScreen(it.id, true))
|
||||
}
|
||||
},
|
||||
onMangaLongClick = { manga ->
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
navigator.push(MangaScreen(manga.id, true))
|
||||
} else {
|
||||
// KMK <--
|
||||
scope.launchIO {
|
||||
val duplicateManga = screenModel.getDuplicateLibraryManga(manga)
|
||||
when {
|
||||
manga.favorite -> screenModel.setDialog(
|
||||
BrowseSourceScreenModel.Dialog.RemoveManga(manga)
|
||||
)
|
||||
duplicateManga != null -> screenModel.setDialog(
|
||||
BrowseSourceScreenModel.Dialog.AddDuplicateManga(
|
||||
manga,
|
||||
duplicateManga,
|
||||
),
|
||||
)
|
||||
else -> screenModel.addFavorite(manga)
|
||||
}
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
}
|
||||
},
|
||||
// KMK -->
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -123,5 +169,15 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
|
|||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package exh.md.similar
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -7,13 +8,21 @@ import androidx.compose.runtime.collectAsState
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.browse.BrowseSourceContent
|
||||
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
|
||||
import eu.kanade.presentation.components.SelectionToolbar
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.ui.browse.AddDuplicateMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangaCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.i18n.sy.SYMR
|
||||
|
|
@ -28,6 +37,17 @@ class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
|
|||
val state by screenModel.state.collectAsState()
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
BackHandler(enabled = bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelectionMode()
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
val onMangaClick: (Manga) -> Unit = {
|
||||
navigator.push(MangaScreen(it.id, true))
|
||||
}
|
||||
|
|
@ -36,13 +56,26 @@ class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
|
|||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
BrowseSourceSimpleToolbar(
|
||||
navigateUp = navigator::pop,
|
||||
title = stringResource(SYMR.strings.similar, screenModel.manga.title),
|
||||
displayMode = screenModel.displayMode,
|
||||
onDisplayModeChange = { screenModel.displayMode = it },
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
BrowseSourceSimpleToolbar(
|
||||
navigateUp = navigator::pop,
|
||||
title = stringResource(SYMR.strings.similar, screenModel.manga.title),
|
||||
displayMode = screenModel.displayMode,
|
||||
onDisplayModeChange = { screenModel.displayMode = it },
|
||||
scrollBehavior = scrollBehavior,
|
||||
// KMK -->
|
||||
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
) { paddingValues ->
|
||||
|
|
@ -61,9 +94,44 @@ class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
|
|||
onWebViewClick = null,
|
||||
onHelpClick = null,
|
||||
onLocalSourceHelpClick = null,
|
||||
onMangaClick = onMangaClick,
|
||||
onMangaLongClick = onMangaClick,
|
||||
onMangaClick = { manga ->
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(manga)
|
||||
} else {
|
||||
// KMK <--
|
||||
onMangaClick(manga)
|
||||
}
|
||||
},
|
||||
onMangaLongClick = { manga ->
|
||||
// KMK -->
|
||||
if (!bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.addRemoveManga(manga, haptic)
|
||||
} else {
|
||||
// KMK <--
|
||||
onMangaClick(manga)
|
||||
}
|
||||
},
|
||||
// KMK -->
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.AddDuplicateManga ->
|
||||
AddDuplicateMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.RemoveManga ->
|
||||
RemoveMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory ->
|
||||
ChangeMangaCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package exh.recs
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -7,6 +8,7 @@ import androidx.compose.runtime.collectAsState
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
|
|
@ -14,7 +16,14 @@ import cafe.adriel.voyager.navigator.Navigator
|
|||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.browse.BrowseSourceContent
|
||||
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
|
||||
import eu.kanade.presentation.components.SelectionToolbar
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.ui.browse.AddDuplicateMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangaCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
|
|
@ -27,6 +36,17 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
|
|||
val state by screenModel.state.collectAsState()
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
// KMK -->
|
||||
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
|
||||
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
|
||||
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
BackHandler(enabled = bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelectionMode()
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
val onMangaClick: (Manga) -> Unit = { manga ->
|
||||
openSmartSearch(navigator, manga.ogTitle)
|
||||
}
|
||||
|
|
@ -35,13 +55,26 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
|
|||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
BrowseSourceSimpleToolbar(
|
||||
navigateUp = navigator::pop,
|
||||
title = screenModel.manga.title,
|
||||
displayMode = screenModel.displayMode,
|
||||
onDisplayModeChange = { screenModel.displayMode = it },
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
SelectionToolbar(
|
||||
selectedCount = bulkFavoriteState.selection.size,
|
||||
onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite,
|
||||
)
|
||||
} else {
|
||||
// KMK <--
|
||||
BrowseSourceSimpleToolbar(
|
||||
navigateUp = navigator::pop,
|
||||
title = screenModel.manga.title,
|
||||
displayMode = screenModel.displayMode,
|
||||
onDisplayModeChange = { screenModel.displayMode = it },
|
||||
scrollBehavior = scrollBehavior,
|
||||
// KMK -->
|
||||
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
) { paddingValues ->
|
||||
|
|
@ -60,10 +93,45 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
|
|||
onWebViewClick = null,
|
||||
onHelpClick = null,
|
||||
onLocalSourceHelpClick = null,
|
||||
onMangaClick = onMangaClick,
|
||||
onMangaLongClick = onMangaClick,
|
||||
onMangaClick = { manga ->
|
||||
// KMK -->
|
||||
if (bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.toggleSelection(manga)
|
||||
} else {
|
||||
// KMK <--
|
||||
onMangaClick(manga)
|
||||
}
|
||||
},
|
||||
onMangaLongClick = { manga ->
|
||||
// KMK -->
|
||||
if (!bulkFavoriteState.selectionMode) {
|
||||
bulkFavoriteScreenModel.addRemoveManga(manga, haptic)
|
||||
} else {
|
||||
// KMK <--
|
||||
onMangaClick(manga)
|
||||
}
|
||||
},
|
||||
// KMK -->
|
||||
selection = bulkFavoriteState.selection,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
when (bulkFavoriteState.dialog) {
|
||||
is BulkFavoriteScreenModel.Dialog.AddDuplicateManga ->
|
||||
AddDuplicateMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.RemoveManga ->
|
||||
RemoveMangaDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory ->
|
||||
ChangeMangaCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory ->
|
||||
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
|
||||
is BulkFavoriteScreenModel.Dialog.AllowDuplicate ->
|
||||
AllowDuplicateDialog(bulkFavoriteScreenModel)
|
||||
else -> {}
|
||||
}
|
||||
// KMK <--
|
||||
}
|
||||
|
||||
private fun openSmartSearch(navigator: Navigator, title: String) {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
<string name="action_toggle_nsfw_only">Toggle NSFW only</string>
|
||||
<string name="action_select_all">Select all</string>
|
||||
<string name="action_select_inverse">Select inverse</string>
|
||||
<string name="action_bulk_select">Bulk selection mode</string>
|
||||
<string name="action_mark_as_read">Mark as read</string>
|
||||
<string name="action_mark_as_unread">Mark as unread</string>
|
||||
<string name="action_mark_previous_as_read">Mark previous as read</string>
|
||||
|
|
@ -677,6 +678,10 @@
|
|||
<string name="remove_from_library">Remove from library</string>
|
||||
<string name="unknown_title">Unknown title</string>
|
||||
<string name="confirm_add_duplicate_manga">You have an entry in your library with the same name.\n\nDo you still wish to continue?</string>
|
||||
<string name="action_allow_duplicate_manga">Allow it</string>
|
||||
<string name="action_skip_duplicate_manga">Skip it</string>
|
||||
<string name="action_allow_all_duplicate_manga">Allow all</string>
|
||||
<string name="action_skip_all_duplicate_manga">Skip all</string>
|
||||
<string name="manga_added_library">Added to library</string>
|
||||
<string name="manga_removed_library">Removed from library</string>
|
||||
<string name="manga_info_expand">More</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue