add BulkSelection to the rest of Migrate, MangaDex…

This commit is contained in:
Cuong M. Tran 2024-03-09 18:26:58 +07:00 committed by Cuong Tran
parent df229af0eb
commit a7c6232a50
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
7 changed files with 531 additions and 62 deletions

View file

@ -59,7 +59,7 @@ fun BrowseSourceContent(
onMangaClick: (Manga) -> Unit, onMangaClick: (Manga) -> Unit,
onMangaLongClick: (Manga) -> Unit, onMangaLongClick: (Manga) -> Unit,
// KMK --> // KMK -->
selection: List<Manga>? = null, selection: List<Manga>,
// KMK <-- // KMK <--
) { ) {
val context = LocalContext.current val context = LocalContext.current

View file

@ -2,6 +2,7 @@ package eu.kanade.presentation.browse.components
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Check import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.Checklist
import androidx.compose.material.icons.outlined.ViewModule import androidx.compose.material.icons.outlined.ViewModule
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -27,6 +28,9 @@ fun BrowseSourceSimpleToolbar(
displayMode: LibraryDisplayMode?, displayMode: LibraryDisplayMode?,
onDisplayModeChange: (LibraryDisplayMode) -> Unit, onDisplayModeChange: (LibraryDisplayMode) -> Unit,
scrollBehavior: TopAppBarScrollBehavior, scrollBehavior: TopAppBarScrollBehavior,
// KMK -->
toggleSelectionMode: () -> Unit,
// KMK <--
) { ) {
AppBar( AppBar(
navigateUp = navigateUp, navigateUp = navigateUp,
@ -41,6 +45,13 @@ fun BrowseSourceSimpleToolbar(
icon = Icons.Outlined.ViewModule, icon = Icons.Outlined.ViewModule,
onClick = { selectingDisplayMode = true }, onClick = { selectingDisplayMode = true },
), ),
// KMK -->
AppBar.Action(
title = stringResource(MR.strings.action_bulk_select),
icon = Icons.Outlined.Checklist,
onClick = toggleSelectionMode,
),
// KMK <--
), ),
) )
DropdownMenu( DropdownMenu(

View file

@ -53,18 +53,19 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
}, },
onClickItem = { onClickItem = {
// KMK --> // KMK -->
if (bulkFavoriteState.selectionMode) if (bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelection(it) bulkFavoriteScreenModel.toggleSelection(it)
else { } else
// KMK <-- // KMK <--
// SY --> {
navigator.items // SY -->
.filterIsInstance<MigrationListScreen>() navigator.items
.last() .filterIsInstance<MigrationListScreen>()
.newSelectedItem = mangaId to it.id .last()
navigator.popUntil { it is MigrationListScreen } .newSelectedItem = mangaId to it.id
// SY <-- navigator.popUntil { it is MigrationListScreen }
} // SY <--
}
}, },
onLongClickItem = { navigator.push(MangaScreen(it.id, true)) }, onLongClickItem = { navigator.push(MangaScreen(it.id, true)) },
// KMK --> // KMK -->
@ -73,12 +74,12 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
) )
// KMK --> // KMK -->
val onDismissRequest = { bulkFavoriteScreenModel.setDialog(null) } val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) }
when (val dialog = bulkFavoriteState.dialog) { when (val dialog = bulkFavoriteState.dialog) {
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> { is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> {
ChangeCategoryDialog( ChangeCategoryDialog(
initialSelection = dialog.initialSelection, initialSelection = dialog.initialSelection,
onDismissRequest = onDismissRequest, onDismissRequest = onBulkDismissRequest,
onEditCategories = { navigator.push(CategoryScreen()) }, onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, exclude -> onConfirm = { include, exclude ->
bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude) bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude)
@ -87,7 +88,7 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
} }
is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> { is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> {
AllowDuplicateDialog( AllowDuplicateDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onBulkDismissRequest,
onAllowAllDuplicate = { onAllowAllDuplicate = {
bulkFavoriteScreenModel.addFavoriteDuplicate() bulkFavoriteScreenModel.addFavoriteDuplicate()
}, },

View file

@ -1,5 +1,8 @@
package eu.kanade.tachiyomi.ui.browse.migration.search package eu.kanade.tachiyomi.ui.browse.migration.search
import androidx.activity.compose.BackHandler
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Checklist
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -14,18 +17,27 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceFloatingActionButton import eu.kanade.presentation.browse.components.BrowseSourceFloatingActionButton
import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.presentation.components.SelectionToolbar
import eu.kanade.presentation.manga.AllowDuplicateDialog
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen 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.BrowseSourceScreenModel
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterDialog import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterDialog
import eu.kanade.tachiyomi.ui.category.CategoryScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.ui.webview.WebViewScreen import eu.kanade.tachiyomi.ui.webview.WebViewScreen
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import tachiyomi.core.common.Constants import tachiyomi.core.common.Constants
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.source.local.LocalSource import tachiyomi.source.local.LocalSource
data class SourceSearchScreen( data class SourceSearchScreen(
@ -44,15 +56,51 @@ data class SourceSearchScreen(
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
// KMK -->
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
BackHandler(enabled = bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode()
}
// KMK <--
Scaffold( Scaffold(
topBar = { scrollBehavior -> topBar = { scrollBehavior ->
SearchToolbar( // KMK -->
searchQuery = state.toolbarQuery ?: "", if (bulkFavoriteState.selectionMode) {
onChangeSearchQuery = screenModel::setToolbarQuery, SelectionToolbar(
onClickCloseSearch = navigator::pop, selectedCount = bulkFavoriteState.selection.size,
onSearch = screenModel::search, onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
scrollBehavior = scrollBehavior, 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<AppBar.AppBarAction>().builder()
.apply {
add(
AppBar.Action(
title = stringResource(MR.strings.action_bulk_select),
icon = Icons.Outlined.Checklist,
onClick = bulkFavoriteScreenModel::toggleSelectionMode,
),
)
}
.build(),
)
},
// KMK <--
)
}
}, },
floatingActionButton = { floatingActionButton = {
// SY --> // SY -->
@ -96,8 +144,19 @@ data class SourceSearchScreen(
}, },
onHelpClick = { uriHandler.openUri(Constants.URL_HELP) }, onHelpClick = { uriHandler.openUri(Constants.URL_HELP) },
onLocalSourceHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) }, 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)) }, onMangaLongClick = { navigator.push(MangaScreen(it.id, true)) },
// KMK -->
selection = bulkFavoriteState.selection,
// KMK <--
) )
} }
@ -123,5 +182,44 @@ data class SourceSearchScreen(
} }
else -> {} else -> {}
} }
// KMK -->
val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) }
when (val dialog = bulkFavoriteState.dialog) {
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> {
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = onBulkDismissRequest,
onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, exclude ->
bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude)
},
)
}
is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> {
AllowDuplicateDialog(
onDismissRequest = onBulkDismissRequest,
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,
)
}
else -> {}
}
// KMK <--
} }
} }

View file

@ -1,5 +1,6 @@
package exh.md.follows package exh.md.follows
import androidx.activity.compose.BackHandler
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -18,8 +19,11 @@ import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
import eu.kanade.presentation.browse.components.RemoveMangaDialog import eu.kanade.presentation.browse.components.RemoveMangaDialog
import eu.kanade.presentation.category.components.ChangeCategoryDialog import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.components.SelectionToolbar
import eu.kanade.presentation.manga.AllowDuplicateDialog
import eu.kanade.presentation.manga.DuplicateMangaDialog import eu.kanade.presentation.manga.DuplicateMangaDialog
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel
import eu.kanade.tachiyomi.ui.category.CategoryScreen import eu.kanade.tachiyomi.ui.category.CategoryScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen
@ -38,17 +42,39 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
val screenModel = rememberScreenModel { MangaDexFollowsScreenModel(sourceId) } val screenModel = rememberScreenModel { MangaDexFollowsScreenModel(sourceId) }
val state by screenModel.state.collectAsState() 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() } val snackbarHostState = remember { SnackbarHostState() }
Scaffold( Scaffold(
topBar = { scrollBehavior -> topBar = { scrollBehavior ->
BrowseSourceSimpleToolbar( // KMK -->
title = stringResource(SYMR.strings.mangadex_follows), if (bulkFavoriteState.selectionMode) {
displayMode = screenModel.displayMode, SelectionToolbar(
onDisplayModeChange = { screenModel.displayMode = it }, selectedCount = bulkFavoriteState.selection.size,
navigateUp = navigator::pop, onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
scrollBehavior = scrollBehavior, 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 = {
SnackbarHost(hostState = snackbarHostState) SnackbarHost(hostState = snackbarHostState)
@ -69,23 +95,42 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
onWebViewClick = null, onWebViewClick = null,
onHelpClick = null, onHelpClick = null,
onLocalSourceHelpClick = null, onLocalSourceHelpClick = null,
onMangaClick = { navigator.push(MangaScreen(it.id, true)) }, onMangaClick = {
onMangaLongClick = { manga -> // KMK -->
scope.launchIO { if (bulkFavoriteState.selectionMode) {
val duplicateManga = screenModel.getDuplicateLibraryManga(manga) bulkFavoriteScreenModel.toggleSelection(it)
when { } else {
manga.favorite -> screenModel.setDialog(BrowseSourceScreenModel.Dialog.RemoveManga(manga)) // KMK <--
duplicateManga != null -> screenModel.setDialog( navigator.push(MangaScreen(it.id, true))
BrowseSourceScreenModel.Dialog.AddDuplicateManga(
manga,
duplicateManga,
),
)
else -> screenModel.addFavorite(manga)
}
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
} }
}, },
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 +168,44 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
} }
else -> {} else -> {}
} }
// KMK -->
val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) }
when (val dialog = bulkFavoriteState.dialog) {
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> {
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = onBulkDismissRequest,
onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, exclude ->
bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude)
},
)
}
is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> {
AllowDuplicateDialog(
onDismissRequest = onBulkDismissRequest,
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,
)
}
else -> {}
}
// KMK <--
} }
} }

View file

@ -1,20 +1,32 @@
package exh.md.similar package exh.md.similar
import androidx.activity.compose.BackHandler
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar 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.AllowDuplicateDialog
import eu.kanade.presentation.manga.DuplicateMangaDialog
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.category.CategoryScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@ -28,6 +40,18 @@ class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
// KMK -->
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
val scope = rememberCoroutineScope()
val haptic = LocalHapticFeedback.current
BackHandler(enabled = bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode()
}
// KMK <--
val onMangaClick: (Manga) -> Unit = { val onMangaClick: (Manga) -> Unit = {
navigator.push(MangaScreen(it.id, true)) navigator.push(MangaScreen(it.id, true))
} }
@ -36,13 +60,26 @@ class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
Scaffold( Scaffold(
topBar = { scrollBehavior -> topBar = { scrollBehavior ->
BrowseSourceSimpleToolbar( // KMK -->
navigateUp = navigator::pop, if (bulkFavoriteState.selectionMode) {
title = stringResource(SYMR.strings.similar, screenModel.manga.title), SelectionToolbar(
displayMode = screenModel.displayMode, selectedCount = bulkFavoriteState.selection.size,
onDisplayModeChange = { screenModel.displayMode = it }, onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
scrollBehavior = scrollBehavior, 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) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
) { paddingValues -> ) { paddingValues ->
@ -61,9 +98,109 @@ class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
onWebViewClick = null, onWebViewClick = null,
onHelpClick = null, onHelpClick = null,
onLocalSourceHelpClick = null, onLocalSourceHelpClick = null,
onMangaClick = onMangaClick, onMangaClick = { manga ->
onMangaLongClick = onMangaClick, // KMK -->
if (bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelection(manga)
} else {
// KMK <--
onMangaClick(manga)
}
},
onMangaLongClick = { manga ->
// KMK -->
if (!bulkFavoriteState.selectionMode) {
scope.launchIO {
val duplicateManga = bulkFavoriteScreenModel.getDuplicateLibraryManga(manga)
when {
manga.favorite -> bulkFavoriteScreenModel.setDialog(
BulkFavoriteScreenModel.Dialog.RemoveManga(manga)
)
duplicateManga != null -> bulkFavoriteScreenModel.setDialog(
BulkFavoriteScreenModel.Dialog.AddDuplicateManga(
manga,
duplicateManga,
),
)
else -> bulkFavoriteScreenModel.addFavorite(manga)
}
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
}
} else {
// KMK <--
onMangaClick(manga)
}
},
// KMK -->
selection = bulkFavoriteState.selection,
// KMK <--
) )
} }
// KMK -->
val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) }
when (val dialog = bulkFavoriteState.dialog) {
is BulkFavoriteScreenModel.Dialog.AddDuplicateManga -> {
DuplicateMangaDialog(
onDismissRequest = onBulkDismissRequest,
onConfirm = { bulkFavoriteScreenModel.addFavorite(dialog.manga) },
onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) },
)
}
is BulkFavoriteScreenModel.Dialog.RemoveManga -> {
RemoveMangaDialog(
onDismissRequest = onBulkDismissRequest,
onConfirm = {
bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga)
},
mangaToRemove = dialog.manga,
)
}
is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory -> {
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = onBulkDismissRequest,
onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, _ ->
bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga)
bulkFavoriteScreenModel.moveMangaToCategories(dialog.manga, include)
},
)
}
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> {
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = onBulkDismissRequest,
onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, exclude ->
bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude)
},
)
}
is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> {
AllowDuplicateDialog(
onDismissRequest = onBulkDismissRequest,
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,
)
}
else -> {}
}
// KMK <--
} }
} }

View file

@ -1,12 +1,16 @@
package exh.recs package exh.recs
import androidx.activity.compose.BackHandler
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
@ -14,8 +18,17 @@ import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar 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.AllowDuplicateDialog
import eu.kanade.presentation.manga.DuplicateMangaDialog
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
import eu.kanade.tachiyomi.ui.category.CategoryScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@ -27,6 +40,18 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
// KMK -->
val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() }
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
val scope = rememberCoroutineScope()
val haptic = LocalHapticFeedback.current
BackHandler(enabled = bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode()
}
// KMK <--
val onMangaClick: (Manga) -> Unit = { manga -> val onMangaClick: (Manga) -> Unit = { manga ->
openSmartSearch(navigator, manga.ogTitle) openSmartSearch(navigator, manga.ogTitle)
} }
@ -35,13 +60,26 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
Scaffold( Scaffold(
topBar = { scrollBehavior -> topBar = { scrollBehavior ->
BrowseSourceSimpleToolbar( // KMK -->
navigateUp = navigator::pop, if (bulkFavoriteState.selectionMode) {
title = screenModel.manga.title, SelectionToolbar(
displayMode = screenModel.displayMode, selectedCount = bulkFavoriteState.selection.size,
onDisplayModeChange = { screenModel.displayMode = it }, onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode,
scrollBehavior = scrollBehavior, 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) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
) { paddingValues -> ) { paddingValues ->
@ -60,10 +98,110 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
onWebViewClick = null, onWebViewClick = null,
onHelpClick = null, onHelpClick = null,
onLocalSourceHelpClick = null, onLocalSourceHelpClick = null,
onMangaClick = onMangaClick, onMangaClick = { manga ->
onMangaLongClick = onMangaClick, // KMK -->
if (bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelection(manga)
} else {
// KMK <--
onMangaClick(manga)
}
},
onMangaLongClick = { manga ->
// KMK -->
if (!bulkFavoriteState.selectionMode) {
scope.launchIO {
val duplicateManga = bulkFavoriteScreenModel.getDuplicateLibraryManga(manga)
when {
manga.favorite -> bulkFavoriteScreenModel.setDialog(
BulkFavoriteScreenModel.Dialog.RemoveManga(manga)
)
duplicateManga != null -> bulkFavoriteScreenModel.setDialog(
BulkFavoriteScreenModel.Dialog.AddDuplicateManga(
manga,
duplicateManga,
),
)
else -> bulkFavoriteScreenModel.addFavorite(manga)
}
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
}
} else {
// KMK <--
onMangaClick(manga)
}
},
// KMK -->
selection = bulkFavoriteState.selection,
// KMK <--
) )
} }
// KMK -->
val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) }
when (val dialog = bulkFavoriteState.dialog) {
is BulkFavoriteScreenModel.Dialog.AddDuplicateManga -> {
DuplicateMangaDialog(
onDismissRequest = onBulkDismissRequest,
onConfirm = { bulkFavoriteScreenModel.addFavorite(dialog.manga) },
onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) },
)
}
is BulkFavoriteScreenModel.Dialog.RemoveManga -> {
RemoveMangaDialog(
onDismissRequest = onBulkDismissRequest,
onConfirm = {
bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga)
},
mangaToRemove = dialog.manga,
)
}
is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory -> {
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = onBulkDismissRequest,
onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, _ ->
bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga)
bulkFavoriteScreenModel.moveMangaToCategories(dialog.manga, include)
},
)
}
is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> {
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = onBulkDismissRequest,
onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, exclude ->
bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude)
},
)
}
is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> {
AllowDuplicateDialog(
onDismissRequest = onBulkDismissRequest,
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,
)
}
else -> {}
}
// KMK <--
} }
private fun openSmartSearch(navigator: Navigator, title: String) { private fun openSmartSearch(navigator: Navigator, title: String) {