123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- package eu.kanade.presentation.updates
- import androidx.activity.compose.BackHandler
- import androidx.compose.foundation.layout.fillMaxWidth
- import androidx.compose.foundation.layout.padding
- import androidx.compose.material.icons.Icons
- import androidx.compose.material.icons.outlined.FlipToBack
- import androidx.compose.material.icons.outlined.Refresh
- import androidx.compose.material.icons.outlined.SelectAll
- import androidx.compose.material3.Icon
- import androidx.compose.material3.IconButton
- import androidx.compose.material3.SnackbarHost
- import androidx.compose.material3.SnackbarHostState
- import androidx.compose.material3.TopAppBarScrollBehavior
- import androidx.compose.runtime.Composable
- import androidx.compose.runtime.getValue
- import androidx.compose.runtime.mutableStateOf
- import androidx.compose.runtime.remember
- import androidx.compose.runtime.rememberCoroutineScope
- import androidx.compose.runtime.setValue
- import androidx.compose.ui.Modifier
- import androidx.compose.ui.platform.LocalContext
- import androidx.compose.ui.res.stringResource
- import androidx.compose.ui.util.fastAll
- import androidx.compose.ui.util.fastAny
- import eu.kanade.presentation.components.AppBar
- import eu.kanade.presentation.manga.components.ChapterDownloadAction
- import eu.kanade.presentation.manga.components.MangaBottomActionMenu
- import eu.kanade.tachiyomi.R
- import eu.kanade.tachiyomi.data.download.model.Download
- import eu.kanade.tachiyomi.ui.updates.UpdatesItem
- import eu.kanade.tachiyomi.ui.updates.UpdatesState
- import kotlinx.coroutines.delay
- import kotlinx.coroutines.launch
- import tachiyomi.presentation.core.components.FastScrollLazyColumn
- import tachiyomi.presentation.core.components.material.PullRefresh
- import tachiyomi.presentation.core.components.material.Scaffold
- import tachiyomi.presentation.core.screens.EmptyScreen
- import tachiyomi.presentation.core.screens.LoadingScreen
- import kotlin.time.Duration.Companion.seconds
- @Composable
- fun UpdateScreen(
- state: UpdatesState,
- snackbarHostState: SnackbarHostState,
- lastUpdated: Long,
- relativeTime: Int,
- onClickCover: (UpdatesItem) -> Unit,
- onSelectAll: (Boolean) -> Unit,
- onInvertSelection: () -> Unit,
- onUpdateLibrary: () -> Boolean,
- onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit,
- onMultiBookmarkClicked: (List<UpdatesItem>, bookmark: Boolean) -> Unit,
- onMultiMarkAsReadClicked: (List<UpdatesItem>, read: Boolean) -> Unit,
- onMultiDeleteClicked: (List<UpdatesItem>) -> Unit,
- onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit,
- onOpenChapter: (UpdatesItem) -> Unit,
- ) {
- BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })
- val context = LocalContext.current
- Scaffold(
- topBar = { scrollBehavior ->
- UpdatesAppBar(
- onUpdateLibrary = { onUpdateLibrary() },
- actionModeCounter = state.selected.size,
- onSelectAll = { onSelectAll(true) },
- onInvertSelection = { onInvertSelection() },
- onCancelActionMode = { onSelectAll(false) },
- scrollBehavior = scrollBehavior,
- )
- },
- bottomBar = {
- UpdatesBottomBar(
- selected = state.selected,
- onDownloadChapter = onDownloadChapter,
- onMultiBookmarkClicked = onMultiBookmarkClicked,
- onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
- onMultiDeleteClicked = onMultiDeleteClicked,
- )
- },
- snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
- ) { contentPadding ->
- when {
- state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
- state.items.isEmpty() -> EmptyScreen(
- textResource = R.string.information_no_recent,
- modifier = Modifier.padding(contentPadding),
- )
- else -> {
- val scope = rememberCoroutineScope()
- var isRefreshing by remember { mutableStateOf(false) }
- PullRefresh(
- refreshing = isRefreshing,
- onRefresh = {
- val started = onUpdateLibrary()
- if (!started) return@PullRefresh
- scope.launch {
- // Fake refresh status but hide it after a second as it's a long running task
- isRefreshing = true
- delay(1.seconds)
- isRefreshing = false
- }
- },
- enabled = !state.selectionMode,
- indicatorPadding = contentPadding,
- ) {
- FastScrollLazyColumn(
- contentPadding = contentPadding,
- ) {
- if (lastUpdated > 0L) {
- updatesLastUpdatedItem(lastUpdated)
- }
- updatesUiItems(
- uiModels = state.getUiModel(context, relativeTime),
- selectionMode = state.selectionMode,
- onUpdateSelected = onUpdateSelected,
- onClickCover = onClickCover,
- onClickUpdate = onOpenChapter,
- onDownloadChapter = onDownloadChapter,
- )
- }
- }
- }
- }
- }
- }
- @Composable
- private fun UpdatesAppBar(
- modifier: Modifier = Modifier,
- onUpdateLibrary: () -> Unit,
- // For action mode
- actionModeCounter: Int,
- onSelectAll: () -> Unit,
- onInvertSelection: () -> Unit,
- onCancelActionMode: () -> Unit,
- scrollBehavior: TopAppBarScrollBehavior,
- ) {
- AppBar(
- modifier = modifier,
- title = stringResource(R.string.label_recent_updates),
- actions = {
- IconButton(onClick = onUpdateLibrary) {
- Icon(
- imageVector = Icons.Outlined.Refresh,
- contentDescription = stringResource(R.string.action_update_library),
- )
- }
- },
- actionModeCounter = actionModeCounter,
- onCancelActionMode = onCancelActionMode,
- actionModeActions = {
- IconButton(onClick = onSelectAll) {
- Icon(
- imageVector = Icons.Outlined.SelectAll,
- contentDescription = stringResource(R.string.action_select_all),
- )
- }
- IconButton(onClick = onInvertSelection) {
- Icon(
- imageVector = Icons.Outlined.FlipToBack,
- contentDescription = stringResource(R.string.action_select_inverse),
- )
- }
- },
- scrollBehavior = scrollBehavior,
- )
- }
- @Composable
- private fun UpdatesBottomBar(
- selected: List<UpdatesItem>,
- onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit,
- onMultiBookmarkClicked: (List<UpdatesItem>, bookmark: Boolean) -> Unit,
- onMultiMarkAsReadClicked: (List<UpdatesItem>, read: Boolean) -> Unit,
- onMultiDeleteClicked: (List<UpdatesItem>) -> Unit,
- ) {
- MangaBottomActionMenu(
- visible = selected.isNotEmpty(),
- modifier = Modifier.fillMaxWidth(),
- onBookmarkClicked = {
- onMultiBookmarkClicked.invoke(selected, true)
- }.takeIf { selected.fastAny { !it.update.bookmark } },
- onRemoveBookmarkClicked = {
- onMultiBookmarkClicked.invoke(selected, false)
- }.takeIf { selected.fastAll { it.update.bookmark } },
- onMarkAsReadClicked = {
- onMultiMarkAsReadClicked(selected, true)
- }.takeIf { selected.fastAny { !it.update.read } },
- onMarkAsUnreadClicked = {
- onMultiMarkAsReadClicked(selected, false)
- }.takeIf { selected.fastAny { it.update.read || it.update.lastPageRead > 0L } },
- onDownloadClicked = {
- onDownloadChapter(selected, ChapterDownloadAction.START)
- }.takeIf {
- selected.fastAny { it.downloadStateProvider() != Download.State.DOWNLOADED }
- },
- onDeleteClicked = {
- onMultiDeleteClicked(selected)
- }.takeIf { selected.fastAny { it.downloadStateProvider() == Download.State.DOWNLOADED } },
- )
- }
- sealed class UpdatesUiModel {
- data class Header(val date: String) : UpdatesUiModel()
- data class Item(val item: UpdatesItem) : UpdatesUiModel()
- }
|