mirror of
https://github.com/mihonapp/mihon.git
synced 2025-10-09 12:59:34 +02:00
Support mass migration in 'Browse -> Migrate' (#2338)
This commit is contained in:
@@ -1,84 +0,0 @@
|
|||||||
package eu.kanade.presentation.browse
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import eu.kanade.presentation.components.AppBar
|
|
||||||
import eu.kanade.presentation.manga.components.BaseMangaListItem
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreenModel
|
|
||||||
import tachiyomi.domain.manga.model.Manga
|
|
||||||
import tachiyomi.i18n.MR
|
|
||||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MigrateMangaScreen(
|
|
||||||
navigateUp: () -> Unit,
|
|
||||||
title: String?,
|
|
||||||
state: MigrateMangaScreenModel.State,
|
|
||||||
onClickItem: (Manga) -> Unit,
|
|
||||||
onClickCover: (Manga) -> Unit,
|
|
||||||
) {
|
|
||||||
Scaffold(
|
|
||||||
topBar = { scrollBehavior ->
|
|
||||||
AppBar(
|
|
||||||
title = title,
|
|
||||||
navigateUp = navigateUp,
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) { contentPadding ->
|
|
||||||
if (state.isEmpty) {
|
|
||||||
EmptyScreen(
|
|
||||||
stringRes = MR.strings.empty_screen,
|
|
||||||
modifier = Modifier.padding(contentPadding),
|
|
||||||
)
|
|
||||||
return@Scaffold
|
|
||||||
}
|
|
||||||
|
|
||||||
MigrateMangaContent(
|
|
||||||
contentPadding = contentPadding,
|
|
||||||
state = state,
|
|
||||||
onClickItem = onClickItem,
|
|
||||||
onClickCover = onClickCover,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MigrateMangaContent(
|
|
||||||
contentPadding: PaddingValues,
|
|
||||||
state: MigrateMangaScreenModel.State,
|
|
||||||
onClickItem: (Manga) -> Unit,
|
|
||||||
onClickCover: (Manga) -> Unit,
|
|
||||||
) {
|
|
||||||
FastScrollLazyColumn(
|
|
||||||
contentPadding = contentPadding,
|
|
||||||
) {
|
|
||||||
items(state.titles) { manga ->
|
|
||||||
MigrateMangaItem(
|
|
||||||
manga = manga,
|
|
||||||
onClickItem = onClickItem,
|
|
||||||
onClickCover = onClickCover,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MigrateMangaItem(
|
|
||||||
manga: Manga,
|
|
||||||
onClickItem: (Manga) -> Unit,
|
|
||||||
onClickCover: (Manga) -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
BaseMangaListItem(
|
|
||||||
modifier = modifier,
|
|
||||||
manga = manga,
|
|
||||||
onClickItem = { onClickItem(manga) },
|
|
||||||
onClickCover = { onClickCover(manga) },
|
|
||||||
)
|
|
||||||
}
|
|
@@ -1,21 +1,41 @@
|
|||||||
package eu.kanade.tachiyomi.ui.browse.migration.manga
|
package eu.kanade.tachiyomi.ui.browse.migration.manga
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.outlined.ArrowForward
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
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.MigrateMangaScreen
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.manga.components.BaseMangaListItem
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import mihon.feature.migration.config.MigrationConfigScreen
|
import mihon.feature.migration.config.MigrationConfigScreen
|
||||||
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||||
|
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||||
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||||
|
import tachiyomi.presentation.core.util.selectedBackground
|
||||||
|
import tachiyomi.presentation.core.util.shouldExpandFAB
|
||||||
|
|
||||||
data class MigrateMangaScreen(
|
data class MigrateMangaScreen(
|
||||||
private val sourceId: Long,
|
private val sourceId: Long,
|
||||||
@@ -34,13 +54,59 @@ data class MigrateMangaScreen(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
MigrateMangaScreen(
|
BackHandler(enabled = state.selectionMode) {
|
||||||
navigateUp = navigator::pop,
|
screenModel.clearSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = { scrollBehavior ->
|
||||||
|
AppBar(
|
||||||
title = state.source!!.name,
|
title = state.source!!.name,
|
||||||
|
navigateUp = {
|
||||||
|
if (state.selectionMode) {
|
||||||
|
screenModel.clearSelection()
|
||||||
|
} else {
|
||||||
|
navigator.pop()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
if (state.selectionMode) {
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
text = { Text(text = stringResource(MR.strings.migrationConfigScreen_continueButtonText)) },
|
||||||
|
icon = {
|
||||||
|
Icon(imageVector = Icons.AutoMirrored.Outlined.ArrowForward, contentDescription = null)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
val selection = state.selection
|
||||||
|
screenModel.clearSelection()
|
||||||
|
navigator.push(MigrationConfigScreen(selection))
|
||||||
|
},
|
||||||
|
expanded = lazyListState.shouldExpandFAB(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) { contentPadding ->
|
||||||
|
if (state.isEmpty) {
|
||||||
|
EmptyScreen(
|
||||||
|
stringRes = MR.strings.empty_screen,
|
||||||
|
modifier = Modifier.padding(contentPadding),
|
||||||
|
)
|
||||||
|
return@Scaffold
|
||||||
|
}
|
||||||
|
|
||||||
|
MigrateMangaContent(
|
||||||
|
lazyListState = lazyListState,
|
||||||
|
contentPadding = contentPadding,
|
||||||
state = state,
|
state = state,
|
||||||
onClickItem = { navigator.push(MigrationConfigScreen(it.id)) },
|
onClickItem = screenModel::toggleSelection,
|
||||||
onClickCover = { navigator.push(MangaScreen(it.id)) },
|
onClickCover = { navigator.push(MangaScreen(it.id)) },
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
screenModel.events.collectLatest { event ->
|
screenModel.events.collectLatest { event ->
|
||||||
@@ -52,4 +118,43 @@ data class MigrateMangaScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MigrateMangaContent(
|
||||||
|
lazyListState: LazyListState,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
|
state: MigrateMangaScreenModel.State,
|
||||||
|
onClickItem: (Manga) -> Unit,
|
||||||
|
onClickCover: (Manga) -> Unit,
|
||||||
|
) {
|
||||||
|
FastScrollLazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
) {
|
||||||
|
items(state.titles) { manga ->
|
||||||
|
MigrateMangaItem(
|
||||||
|
manga = manga,
|
||||||
|
isSelected = manga.id in state.selection,
|
||||||
|
onClickItem = onClickItem,
|
||||||
|
onClickCover = onClickCover,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MigrateMangaItem(
|
||||||
|
manga: Manga,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onClickItem: (Manga) -> Unit,
|
||||||
|
onClickCover: (Manga) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
BaseMangaListItem(
|
||||||
|
modifier = modifier.selectedBackground(isSelected),
|
||||||
|
manga = manga,
|
||||||
|
onClickItem = { onClickItem(manga) },
|
||||||
|
onClickCover = { onClickCover(manga) },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.receiveAsFlow
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import mihon.core.common.utils.mutate
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.manga.interactor.GetFavorites
|
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
@@ -57,9 +58,23 @@ class MigrateMangaScreenModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toggleSelection(item: Manga) {
|
||||||
|
mutableState.update { state ->
|
||||||
|
val selection = state.selection.mutate { list ->
|
||||||
|
if (!list.remove(item.id)) list.add(item.id)
|
||||||
|
}
|
||||||
|
state.copy(selection = selection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearSelection() {
|
||||||
|
mutableState.update { it.copy(selection = emptySet()) }
|
||||||
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class State(
|
data class State(
|
||||||
val source: Source? = null,
|
val source: Source? = null,
|
||||||
|
val selection: Set<Long> = emptySet(),
|
||||||
private val titleList: ImmutableList<Manga>? = null,
|
private val titleList: ImmutableList<Manga>? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -71,6 +86,8 @@ class MigrateMangaScreenModel(
|
|||||||
|
|
||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
get() = titles.isEmpty()
|
get() = titles.isEmpty()
|
||||||
|
|
||||||
|
val selectionMode = selection.isNotEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user