komikku/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt
Cuong Tran f6be3a494c
BulkSelection: “Select all” for all screens
(cherry picked from commit bddcc089df738e23331b351cc20c8425cb316c74)
2024-04-26 12:44:49 +07:00

227 lines
8.2 KiB
Kotlin

package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.material.icons.outlined.Public
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid
import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid
import eu.kanade.presentation.browse.components.BrowseSourceEHentaiList
import eu.kanade.presentation.browse.components.BrowseSourceList
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.util.formattedMessage
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel
import exh.metadata.metadata.RaisedSearchMetadata
import exh.source.isEhBasedSource
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.StateFlow
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.model.StubSource
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.EmptyScreenAction
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.source.local.LocalSource
@Composable
fun BrowseSourceContent(
source: Source?,
mangaList: LazyPagingItems<StateFlow</* SY --> */Pair<Manga, RaisedSearchMetadata?>/* SY <-- */>>,
columns: GridCells,
// SY -->
ehentaiBrowseDisplayMode: Boolean,
// SY <--
displayMode: LibraryDisplayMode,
snackbarHostState: SnackbarHostState,
contentPadding: PaddingValues,
// SY -->
onWebViewClick: (() -> Unit)?,
onHelpClick: (() -> Unit)?,
onLocalSourceHelpClick: (() -> Unit)?,
// SY <--
onMangaClick: (Manga) -> Unit,
onMangaLongClick: (Manga) -> Unit,
// KMK -->
selection: List<Manga>,
browseSourceState: BrowseSourceScreenModel.State,
// KMK <--
) {
val context = LocalContext.current
val errorState = mangaList.loadState.refresh.takeIf { it is LoadState.Error }
?: mangaList.loadState.append.takeIf { it is LoadState.Error }
val getErrorMessage: (LoadState.Error) -> String = { state ->
with(context) { state.error.formattedMessage }
}
LaunchedEffect(errorState) {
if (mangaList.itemCount > 0 && errorState != null && errorState is LoadState.Error) {
val result = snackbarHostState.showSnackbar(
message = getErrorMessage(errorState),
actionLabel = context.stringResource(MR.strings.action_retry),
duration = SnackbarDuration.Indefinite,
)
when (result) {
SnackbarResult.Dismissed -> snackbarHostState.currentSnackbarData?.dismiss()
SnackbarResult.ActionPerformed -> mangaList.retry()
}
}
}
if (mangaList.itemCount <= 0 && errorState != null && errorState is LoadState.Error) {
EmptyScreen(
modifier = Modifier.padding(contentPadding),
message = getErrorMessage(errorState),
actions = if (source is LocalSource /* SY --> */ && onLocalSourceHelpClick != null /* SY <-- */) {
persistentListOf(
EmptyScreenAction(
stringRes = MR.strings.local_source_help_guide,
icon = Icons.AutoMirrored.Outlined.HelpOutline,
onClick = onLocalSourceHelpClick,
),
)
} else {
listOfNotNull(
EmptyScreenAction(
stringRes = MR.strings.action_retry,
icon = Icons.Outlined.Refresh,
onClick = mangaList::refresh,
),
// SY -->
if (onWebViewClick != null) {
EmptyScreenAction(
MR.strings.action_open_in_web_view,
icon = Icons.Outlined.Public,
onClick = onWebViewClick,
)
} else {
null
},
if (onHelpClick != null) {
EmptyScreenAction(
MR.strings.label_help,
icon = Icons.AutoMirrored.Outlined.HelpOutline,
onClick = onHelpClick,
)
} else {
null
},
// SY <--
).toImmutableList()
},
)
return
}
if (mangaList.itemCount == 0 && mangaList.loadState.refresh is LoadState.Loading) {
LoadingScreen(
modifier = Modifier.padding(contentPadding),
)
// KMK -->
browseSourceState.mangaDisplayingList.clear()
// KMK <--
return
}
// KMK -->
for (idx in browseSourceState.mangaDisplayingList.size..<mangaList.itemCount) {
mangaList[idx]?.collectAsState()?.value?.first?.let { manga ->
if (!browseSourceState.mangaDisplayingList.map { it.id }.contains(manga.id)) {
browseSourceState.mangaDisplayingList.add(manga)
}
}
}
// KMK <--
// SY -->
if (source?.isEhBasedSource() == true && ehentaiBrowseDisplayMode) {
BrowseSourceEHentaiList(
mangaList = mangaList,
contentPadding = contentPadding,
onMangaClick = onMangaClick,
onMangaLongClick = onMangaLongClick,
)
return
}
// SY <--
when (displayMode) {
LibraryDisplayMode.ComfortableGrid -> {
BrowseSourceComfortableGrid(
mangaList = mangaList,
columns = columns,
contentPadding = contentPadding,
onMangaClick = onMangaClick,
onMangaLongClick = onMangaLongClick,
// KMK -->
selection = selection,
// KMK <--
)
}
LibraryDisplayMode.List -> {
BrowseSourceList(
mangaList = mangaList,
contentPadding = contentPadding,
onMangaClick = onMangaClick,
onMangaLongClick = onMangaLongClick,
// KMK -->
selection = selection,
// KMK <--
)
}
LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
BrowseSourceCompactGrid(
mangaList = mangaList,
columns = columns,
contentPadding = contentPadding,
onMangaClick = onMangaClick,
onMangaLongClick = onMangaLongClick,
// KMK -->
selection = selection,
// KMK <--
)
}
}
}
@Composable
internal fun MissingSourceScreen(
source: StubSource,
navigateUp: () -> Unit,
) {
Scaffold(
topBar = { scrollBehavior ->
AppBar(
title = source.name,
navigateUp = navigateUp,
scrollBehavior = scrollBehavior,
)
},
) { paddingValues ->
EmptyScreen(
message = stringResource(MR.strings.source_not_installed, source.toString()),
modifier = Modifier.padding(paddingValues),
)
}
}