2022-06-25 22:03:48 +07:00
|
|
|
package eu.kanade.presentation.manga
|
|
|
|
|
|
|
|
import androidx.activity.compose.BackHandler
|
|
|
|
import androidx.compose.animation.AnimatedVisibility
|
2022-07-09 23:37:49 +07:00
|
|
|
import androidx.compose.animation.core.animateFloatAsState
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.animation.fadeIn
|
|
|
|
import androidx.compose.animation.fadeOut
|
|
|
|
import androidx.compose.foundation.layout.Box
|
2022-07-09 23:37:49 +07:00
|
|
|
import androidx.compose.foundation.layout.Column
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
|
|
import androidx.compose.foundation.layout.WindowInsets
|
|
|
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
|
|
import androidx.compose.foundation.layout.asPaddingValues
|
|
|
|
import androidx.compose.foundation.layout.calculateEndPadding
|
|
|
|
import androidx.compose.foundation.layout.calculateStartPadding
|
|
|
|
import androidx.compose.foundation.layout.fillMaxHeight
|
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
|
import androidx.compose.foundation.layout.navigationBars
|
|
|
|
import androidx.compose.foundation.layout.only
|
|
|
|
import androidx.compose.foundation.layout.padding
|
|
|
|
import androidx.compose.foundation.layout.systemBars
|
2022-06-30 20:20:16 +07:00
|
|
|
import androidx.compose.foundation.lazy.LazyListScope
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.foundation.lazy.items
|
|
|
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
|
|
import androidx.compose.foundation.rememberScrollState
|
|
|
|
import androidx.compose.foundation.verticalScroll
|
|
|
|
import androidx.compose.material.icons.Icons
|
|
|
|
import androidx.compose.material.icons.filled.PlayArrow
|
|
|
|
import androidx.compose.material3.Icon
|
|
|
|
import androidx.compose.material3.SnackbarHost
|
|
|
|
import androidx.compose.material3.SnackbarHostState
|
|
|
|
import androidx.compose.material3.Text
|
|
|
|
import androidx.compose.runtime.Composable
|
2022-07-09 23:37:49 +07:00
|
|
|
import androidx.compose.runtime.derivedStateOf
|
|
|
|
import androidx.compose.runtime.getValue
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|
|
|
import androidx.compose.ui.layout.onSizeChanged
|
|
|
|
import androidx.compose.ui.platform.LocalDensity
|
|
|
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
|
|
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
import eu.kanade.domain.chapter.model.Chapter
|
2022-07-18 08:17:40 +06:00
|
|
|
import eu.kanade.presentation.components.ChapterDownloadAction
|
2022-06-25 22:03:48 +07:00
|
|
|
import eu.kanade.presentation.components.ExtendedFloatingActionButton
|
2022-07-12 09:21:00 +07:00
|
|
|
import eu.kanade.presentation.components.LazyColumn
|
2022-07-18 08:17:40 +06:00
|
|
|
import eu.kanade.presentation.components.MangaBottomActionMenu
|
2022-06-25 22:03:48 +07:00
|
|
|
import eu.kanade.presentation.components.Scaffold
|
2022-10-09 23:20:43 +08:00
|
|
|
import eu.kanade.presentation.components.SwipeRefresh
|
2022-10-16 23:15:01 +07:00
|
|
|
import eu.kanade.presentation.components.TwoPanelBox
|
2022-06-25 22:03:48 +07:00
|
|
|
import eu.kanade.presentation.components.VerticalFastScroller
|
|
|
|
import eu.kanade.presentation.manga.components.ChapterHeader
|
2022-07-09 23:37:49 +07:00
|
|
|
import eu.kanade.presentation.manga.components.ExpandableMangaDescription
|
|
|
|
import eu.kanade.presentation.manga.components.MangaActionRow
|
2022-06-25 22:03:48 +07:00
|
|
|
import eu.kanade.presentation.manga.components.MangaChapterListItem
|
2022-07-09 23:37:49 +07:00
|
|
|
import eu.kanade.presentation.manga.components.MangaInfoBox
|
2022-10-14 16:30:14 -04:00
|
|
|
import eu.kanade.presentation.manga.components.MangaToolbar
|
2022-06-25 22:03:48 +07:00
|
|
|
import eu.kanade.presentation.util.isScrolledToEnd
|
|
|
|
import eu.kanade.presentation.util.isScrollingUp
|
|
|
|
import eu.kanade.tachiyomi.R
|
|
|
|
import eu.kanade.tachiyomi.data.download.model.Download
|
|
|
|
import eu.kanade.tachiyomi.source.SourceManager
|
|
|
|
import eu.kanade.tachiyomi.source.getNameForMangaInfo
|
|
|
|
import eu.kanade.tachiyomi.ui.manga.ChapterItem
|
|
|
|
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
fun MangaScreen(
|
|
|
|
state: MangaScreenState.Success,
|
|
|
|
snackbarHostState: SnackbarHostState,
|
2022-10-22 22:15:12 +08:00
|
|
|
isTabletUi: Boolean,
|
2022-06-25 22:03:48 +07:00
|
|
|
onBackClicked: () -> Unit,
|
|
|
|
onChapterClicked: (Chapter) -> Unit,
|
|
|
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
|
|
|
onAddToLibraryClicked: () -> Unit,
|
|
|
|
onWebViewClicked: (() -> Unit)?,
|
|
|
|
onTrackingClicked: (() -> Unit)?,
|
|
|
|
onTagClicked: (String) -> Unit,
|
|
|
|
onFilterButtonClicked: () -> Unit,
|
|
|
|
onRefresh: () -> Unit,
|
|
|
|
onContinueReading: () -> Unit,
|
|
|
|
onSearch: (query: String, global: Boolean) -> Unit,
|
|
|
|
|
|
|
|
// For cover dialog
|
|
|
|
onCoverClicked: () -> Unit,
|
|
|
|
|
|
|
|
// For top action menu
|
|
|
|
onShareClicked: (() -> Unit)?,
|
|
|
|
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
|
|
|
|
onEditCategoryClicked: (() -> Unit)?,
|
|
|
|
onMigrateClicked: (() -> Unit)?,
|
|
|
|
|
|
|
|
// For bottom action menu
|
|
|
|
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
|
|
|
|
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
|
|
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
|
|
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
2022-07-19 03:42:46 +07:00
|
|
|
|
|
|
|
// Chapter selection
|
|
|
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
|
|
|
onAllChapterSelected: (Boolean) -> Unit,
|
|
|
|
onInvertSelection: () -> Unit,
|
2022-06-25 22:03:48 +07:00
|
|
|
) {
|
2022-10-22 22:15:12 +08:00
|
|
|
if (!isTabletUi) {
|
2022-06-25 22:03:48 +07:00
|
|
|
MangaScreenSmallImpl(
|
|
|
|
state = state,
|
|
|
|
snackbarHostState = snackbarHostState,
|
|
|
|
onBackClicked = onBackClicked,
|
|
|
|
onChapterClicked = onChapterClicked,
|
|
|
|
onDownloadChapter = onDownloadChapter,
|
|
|
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
|
|
|
onWebViewClicked = onWebViewClicked,
|
|
|
|
onTrackingClicked = onTrackingClicked,
|
|
|
|
onTagClicked = onTagClicked,
|
2022-10-14 16:30:14 -04:00
|
|
|
onFilterClicked = onFilterButtonClicked,
|
2022-06-25 22:03:48 +07:00
|
|
|
onRefresh = onRefresh,
|
|
|
|
onContinueReading = onContinueReading,
|
|
|
|
onSearch = onSearch,
|
|
|
|
onCoverClicked = onCoverClicked,
|
|
|
|
onShareClicked = onShareClicked,
|
|
|
|
onDownloadActionClicked = onDownloadActionClicked,
|
|
|
|
onEditCategoryClicked = onEditCategoryClicked,
|
|
|
|
onMigrateClicked = onMigrateClicked,
|
|
|
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
|
|
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
|
|
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
|
|
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
2022-07-19 03:42:46 +07:00
|
|
|
onChapterSelected = onChapterSelected,
|
|
|
|
onAllChapterSelected = onAllChapterSelected,
|
|
|
|
onInvertSelection = onInvertSelection,
|
2022-06-25 22:03:48 +07:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
MangaScreenLargeImpl(
|
|
|
|
state = state,
|
|
|
|
snackbarHostState = snackbarHostState,
|
|
|
|
onBackClicked = onBackClicked,
|
|
|
|
onChapterClicked = onChapterClicked,
|
|
|
|
onDownloadChapter = onDownloadChapter,
|
|
|
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
|
|
|
onWebViewClicked = onWebViewClicked,
|
|
|
|
onTrackingClicked = onTrackingClicked,
|
|
|
|
onTagClicked = onTagClicked,
|
|
|
|
onFilterButtonClicked = onFilterButtonClicked,
|
|
|
|
onRefresh = onRefresh,
|
|
|
|
onContinueReading = onContinueReading,
|
|
|
|
onSearch = onSearch,
|
|
|
|
onCoverClicked = onCoverClicked,
|
|
|
|
onShareClicked = onShareClicked,
|
|
|
|
onDownloadActionClicked = onDownloadActionClicked,
|
|
|
|
onEditCategoryClicked = onEditCategoryClicked,
|
|
|
|
onMigrateClicked = onMigrateClicked,
|
|
|
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
|
|
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
|
|
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
|
|
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
2022-07-19 03:42:46 +07:00
|
|
|
onChapterSelected = onChapterSelected,
|
|
|
|
onAllChapterSelected = onAllChapterSelected,
|
|
|
|
onInvertSelection = onInvertSelection,
|
2022-06-25 22:03:48 +07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
private fun MangaScreenSmallImpl(
|
|
|
|
state: MangaScreenState.Success,
|
|
|
|
snackbarHostState: SnackbarHostState,
|
|
|
|
onBackClicked: () -> Unit,
|
|
|
|
onChapterClicked: (Chapter) -> Unit,
|
|
|
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
|
|
|
onAddToLibraryClicked: () -> Unit,
|
|
|
|
onWebViewClicked: (() -> Unit)?,
|
|
|
|
onTrackingClicked: (() -> Unit)?,
|
|
|
|
onTagClicked: (String) -> Unit,
|
2022-10-14 16:30:14 -04:00
|
|
|
onFilterClicked: () -> Unit,
|
2022-06-25 22:03:48 +07:00
|
|
|
onRefresh: () -> Unit,
|
|
|
|
onContinueReading: () -> Unit,
|
|
|
|
onSearch: (query: String, global: Boolean) -> Unit,
|
|
|
|
|
|
|
|
// For cover dialog
|
|
|
|
onCoverClicked: () -> Unit,
|
|
|
|
|
|
|
|
// For top action menu
|
|
|
|
onShareClicked: (() -> Unit)?,
|
|
|
|
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
|
|
|
|
onEditCategoryClicked: (() -> Unit)?,
|
|
|
|
onMigrateClicked: (() -> Unit)?,
|
|
|
|
|
|
|
|
// For bottom action menu
|
|
|
|
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
|
|
|
|
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
|
|
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
|
|
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
2022-07-19 03:42:46 +07:00
|
|
|
|
|
|
|
// Chapter selection
|
|
|
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
|
|
|
onAllChapterSelected: (Boolean) -> Unit,
|
|
|
|
onInvertSelection: () -> Unit,
|
2022-06-25 22:03:48 +07:00
|
|
|
) {
|
|
|
|
val chapterListState = rememberLazyListState()
|
|
|
|
|
2022-07-09 23:37:49 +07:00
|
|
|
val chapters = remember(state) { state.processedChapters.toList() }
|
2022-06-25 22:03:48 +07:00
|
|
|
|
2022-07-09 23:37:49 +07:00
|
|
|
val internalOnBackPressed = {
|
2022-07-19 03:42:46 +07:00
|
|
|
if (chapters.any { it.selected }) {
|
|
|
|
onAllChapterSelected(false)
|
2022-07-09 23:37:49 +07:00
|
|
|
} else {
|
|
|
|
onBackClicked()
|
2022-06-25 22:03:48 +07:00
|
|
|
}
|
2022-07-09 23:37:49 +07:00
|
|
|
}
|
|
|
|
BackHandler(onBack = internalOnBackPressed)
|
2022-06-25 22:03:48 +07:00
|
|
|
|
2022-07-09 23:37:49 +07:00
|
|
|
Scaffold(
|
2022-09-28 09:20:10 +07:00
|
|
|
modifier = Modifier
|
|
|
|
.padding(WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal).asPaddingValues()),
|
2022-07-09 23:37:49 +07:00
|
|
|
topBar = {
|
|
|
|
val firstVisibleItemIndex by remember {
|
|
|
|
derivedStateOf { chapterListState.firstVisibleItemIndex }
|
|
|
|
}
|
|
|
|
val firstVisibleItemScrollOffset by remember {
|
|
|
|
derivedStateOf { chapterListState.firstVisibleItemScrollOffset }
|
|
|
|
}
|
|
|
|
val animatedTitleAlpha by animateFloatAsState(
|
|
|
|
if (firstVisibleItemIndex > 0) 1f else 0f,
|
|
|
|
)
|
|
|
|
val animatedBgAlpha by animateFloatAsState(
|
|
|
|
if (firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0) 1f else 0f,
|
|
|
|
)
|
2022-10-14 16:30:14 -04:00
|
|
|
MangaToolbar(
|
2022-07-09 23:37:49 +07:00
|
|
|
title = state.manga.title,
|
|
|
|
titleAlphaProvider = { animatedTitleAlpha },
|
|
|
|
backgroundAlphaProvider = { animatedBgAlpha },
|
2022-10-14 16:30:14 -04:00
|
|
|
hasFilters = state.manga.chaptersFiltered(),
|
2022-07-09 23:37:49 +07:00
|
|
|
incognitoMode = state.isIncognitoMode,
|
|
|
|
downloadedOnlyMode = state.isDownloadedOnlyMode,
|
2022-07-11 01:29:58 +07:00
|
|
|
onBackClicked = internalOnBackPressed,
|
2022-10-14 16:30:14 -04:00
|
|
|
onClickFilter = onFilterClicked,
|
|
|
|
onClickShare = onShareClicked,
|
|
|
|
onClickDownload = onDownloadActionClicked,
|
|
|
|
onClickEditCategory = onEditCategoryClicked,
|
|
|
|
onClickMigrate = onMigrateClicked,
|
2022-07-19 03:42:46 +07:00
|
|
|
actionModeCounter = chapters.count { it.selected },
|
|
|
|
onSelectAll = { onAllChapterSelected(true) },
|
|
|
|
onInvertSelection = { onInvertSelection() },
|
2022-07-09 23:37:49 +07:00
|
|
|
)
|
|
|
|
},
|
|
|
|
bottomBar = {
|
|
|
|
SharedMangaBottomActionMenu(
|
2022-07-19 03:42:46 +07:00
|
|
|
selected = chapters.filter { it.selected },
|
2022-07-09 23:37:49 +07:00
|
|
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
|
|
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
|
|
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
|
|
|
onDownloadChapter = onDownloadChapter,
|
|
|
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
|
|
|
fillFraction = 1f,
|
|
|
|
)
|
|
|
|
},
|
|
|
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
|
|
|
floatingActionButton = {
|
|
|
|
AnimatedVisibility(
|
2022-07-19 03:42:46 +07:00
|
|
|
visible = chapters.any { !it.chapter.read } && chapters.none { it.selected },
|
2022-07-09 23:37:49 +07:00
|
|
|
enter = fadeIn(),
|
|
|
|
exit = fadeOut(),
|
|
|
|
) {
|
|
|
|
ExtendedFloatingActionButton(
|
|
|
|
text = {
|
|
|
|
val id = if (chapters.any { it.chapter.read }) {
|
|
|
|
R.string.action_resume
|
|
|
|
} else {
|
|
|
|
R.string.action_start
|
|
|
|
}
|
|
|
|
Text(text = stringResource(id))
|
2022-06-25 22:03:48 +07:00
|
|
|
},
|
2022-10-29 23:43:51 +08:00
|
|
|
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
2022-07-09 23:37:49 +07:00
|
|
|
onClick = onContinueReading,
|
|
|
|
expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(),
|
|
|
|
modifier = Modifier
|
|
|
|
.padding(WindowInsets.navigationBars.only(WindowInsetsSides.Bottom).asPaddingValues()),
|
2022-06-25 22:03:48 +07:00
|
|
|
)
|
2022-07-09 23:37:49 +07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
) { contentPadding ->
|
|
|
|
val topPadding = contentPadding.calculateTopPadding()
|
|
|
|
|
|
|
|
SwipeRefresh(
|
2022-10-09 23:20:43 +08:00
|
|
|
refreshing = state.isRefreshingData,
|
2022-07-09 23:37:49 +07:00
|
|
|
onRefresh = onRefresh,
|
2022-10-09 23:20:43 +08:00
|
|
|
enabled = chapters.none { it.selected },
|
2022-07-09 23:37:49 +07:00
|
|
|
indicatorPadding = contentPadding,
|
|
|
|
) {
|
2022-06-25 22:03:48 +07:00
|
|
|
VerticalFastScroller(
|
|
|
|
listState = chapterListState,
|
2022-07-09 23:37:49 +07:00
|
|
|
topContentPadding = topPadding,
|
2022-06-25 22:03:48 +07:00
|
|
|
) {
|
|
|
|
LazyColumn(
|
|
|
|
modifier = Modifier.fillMaxHeight(),
|
|
|
|
state = chapterListState,
|
2022-09-28 09:20:10 +07:00
|
|
|
contentPadding = PaddingValues(
|
|
|
|
bottom = contentPadding.calculateBottomPadding(),
|
|
|
|
),
|
2022-06-25 22:03:48 +07:00
|
|
|
) {
|
2022-07-10 03:20:40 +07:00
|
|
|
item(
|
|
|
|
key = MangaScreenItem.INFO_BOX,
|
|
|
|
contentType = MangaScreenItem.INFO_BOX,
|
|
|
|
) {
|
2022-07-09 23:37:49 +07:00
|
|
|
MangaInfoBox(
|
2022-10-22 22:15:12 +08:00
|
|
|
isTabletUi = false,
|
2022-07-09 23:37:49 +07:00
|
|
|
appBarPadding = topPadding,
|
|
|
|
title = state.manga.title,
|
|
|
|
author = state.manga.author,
|
|
|
|
artist = state.manga.artist,
|
|
|
|
sourceName = remember { state.source.getNameForMangaInfo() },
|
|
|
|
isStubSource = remember { state.source is SourceManager.StubSource },
|
|
|
|
coverDataProvider = { state.manga },
|
|
|
|
status = state.manga.status,
|
|
|
|
onCoverClick = onCoverClicked,
|
|
|
|
doSearch = onSearch,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-07-10 03:20:40 +07:00
|
|
|
item(
|
|
|
|
key = MangaScreenItem.ACTION_ROW,
|
|
|
|
contentType = MangaScreenItem.ACTION_ROW,
|
|
|
|
) {
|
2022-07-09 23:37:49 +07:00
|
|
|
MangaActionRow(
|
|
|
|
favorite = state.manga.favorite,
|
|
|
|
trackingCount = state.trackingCount,
|
|
|
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
|
|
|
onWebViewClicked = onWebViewClicked,
|
|
|
|
onTrackingClicked = onTrackingClicked,
|
|
|
|
onEditCategory = onEditCategoryClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-07-10 03:20:40 +07:00
|
|
|
item(
|
|
|
|
key = MangaScreenItem.DESCRIPTION_WITH_TAG,
|
|
|
|
contentType = MangaScreenItem.DESCRIPTION_WITH_TAG,
|
|
|
|
) {
|
2022-07-09 23:37:49 +07:00
|
|
|
ExpandableMangaDescription(
|
|
|
|
defaultExpandState = state.isFromSource,
|
|
|
|
description = state.manga.description,
|
|
|
|
tagsProvider = { state.manga.genre },
|
|
|
|
onTagClicked = onTagClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-07-10 03:20:40 +07:00
|
|
|
item(
|
|
|
|
key = MangaScreenItem.CHAPTER_HEADER,
|
|
|
|
contentType = MangaScreenItem.CHAPTER_HEADER,
|
|
|
|
) {
|
2022-07-09 23:37:49 +07:00
|
|
|
ChapterHeader(
|
|
|
|
chapterCount = chapters.size,
|
2022-10-14 16:30:14 -04:00
|
|
|
onClick = onFilterClicked,
|
2022-07-09 23:37:49 +07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-06-30 20:20:16 +07:00
|
|
|
sharedChapterItems(
|
|
|
|
chapters = chapters,
|
|
|
|
onChapterClicked = onChapterClicked,
|
|
|
|
onDownloadChapter = onDownloadChapter,
|
2022-07-19 03:42:46 +07:00
|
|
|
onChapterSelected = onChapterSelected,
|
2022-06-30 20:20:16 +07:00
|
|
|
)
|
2022-06-25 22:03:48 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
fun MangaScreenLargeImpl(
|
|
|
|
state: MangaScreenState.Success,
|
|
|
|
snackbarHostState: SnackbarHostState,
|
|
|
|
onBackClicked: () -> Unit,
|
|
|
|
onChapterClicked: (Chapter) -> Unit,
|
|
|
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
|
|
|
onAddToLibraryClicked: () -> Unit,
|
|
|
|
onWebViewClicked: (() -> Unit)?,
|
|
|
|
onTrackingClicked: (() -> Unit)?,
|
|
|
|
onTagClicked: (String) -> Unit,
|
|
|
|
onFilterButtonClicked: () -> Unit,
|
|
|
|
onRefresh: () -> Unit,
|
|
|
|
onContinueReading: () -> Unit,
|
|
|
|
onSearch: (query: String, global: Boolean) -> Unit,
|
|
|
|
|
|
|
|
// For cover dialog
|
|
|
|
onCoverClicked: () -> Unit,
|
|
|
|
|
|
|
|
// For top action menu
|
|
|
|
onShareClicked: (() -> Unit)?,
|
|
|
|
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
|
|
|
|
onEditCategoryClicked: (() -> Unit)?,
|
|
|
|
onMigrateClicked: (() -> Unit)?,
|
|
|
|
|
|
|
|
// For bottom action menu
|
|
|
|
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
|
|
|
|
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
|
|
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
|
|
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
2022-07-19 03:42:46 +07:00
|
|
|
|
|
|
|
// Chapter selection
|
|
|
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
|
|
|
onAllChapterSelected: (Boolean) -> Unit,
|
|
|
|
onInvertSelection: () -> Unit,
|
2022-06-25 22:03:48 +07:00
|
|
|
) {
|
|
|
|
val layoutDirection = LocalLayoutDirection.current
|
|
|
|
val density = LocalDensity.current
|
|
|
|
|
2022-07-29 18:47:09 +07:00
|
|
|
val chapters = remember(state) { state.processedChapters.toList() }
|
|
|
|
|
2022-06-25 22:03:48 +07:00
|
|
|
val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
|
|
|
|
val (topBarHeight, onTopBarHeightChanged) = remember { mutableStateOf(0) }
|
|
|
|
SwipeRefresh(
|
2022-10-09 23:20:43 +08:00
|
|
|
refreshing = state.isRefreshingData,
|
2022-06-25 22:03:48 +07:00
|
|
|
onRefresh = onRefresh,
|
2022-10-09 23:20:43 +08:00
|
|
|
enabled = chapters.none { it.selected },
|
2022-06-25 22:03:48 +07:00
|
|
|
indicatorPadding = PaddingValues(
|
|
|
|
start = insetPadding.calculateStartPadding(layoutDirection),
|
|
|
|
top = with(density) { topBarHeight.toDp() },
|
|
|
|
end = insetPadding.calculateEndPadding(layoutDirection),
|
|
|
|
),
|
|
|
|
) {
|
|
|
|
val chapterListState = rememberLazyListState()
|
|
|
|
|
|
|
|
val internalOnBackPressed = {
|
2022-07-19 03:42:46 +07:00
|
|
|
if (chapters.any { it.selected }) {
|
|
|
|
onAllChapterSelected(false)
|
2022-06-25 22:03:48 +07:00
|
|
|
} else {
|
|
|
|
onBackClicked()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BackHandler(onBack = internalOnBackPressed)
|
|
|
|
|
|
|
|
Scaffold(
|
|
|
|
modifier = Modifier.padding(insetPadding),
|
|
|
|
topBar = {
|
2022-10-14 16:30:14 -04:00
|
|
|
MangaToolbar(
|
2022-06-25 22:03:48 +07:00
|
|
|
modifier = Modifier.onSizeChanged { onTopBarHeightChanged(it.height) },
|
|
|
|
title = state.manga.title,
|
2022-07-19 03:42:46 +07:00
|
|
|
titleAlphaProvider = { if (chapters.any { it.selected }) 1f else 0f },
|
2022-06-25 22:03:48 +07:00
|
|
|
backgroundAlphaProvider = { 1f },
|
2022-10-14 16:30:14 -04:00
|
|
|
hasFilters = state.manga.chaptersFiltered(),
|
2022-06-25 22:03:48 +07:00
|
|
|
incognitoMode = state.isIncognitoMode,
|
|
|
|
downloadedOnlyMode = state.isDownloadedOnlyMode,
|
|
|
|
onBackClicked = internalOnBackPressed,
|
2022-10-14 16:30:14 -04:00
|
|
|
onClickFilter = onFilterButtonClicked,
|
|
|
|
onClickShare = onShareClicked,
|
|
|
|
onClickDownload = onDownloadActionClicked,
|
|
|
|
onClickEditCategory = onEditCategoryClicked,
|
|
|
|
onClickMigrate = onMigrateClicked,
|
2022-07-19 03:42:46 +07:00
|
|
|
actionModeCounter = chapters.count { it.selected },
|
|
|
|
onSelectAll = { onAllChapterSelected(true) },
|
|
|
|
onInvertSelection = { onInvertSelection() },
|
2022-06-25 22:03:48 +07:00
|
|
|
)
|
|
|
|
},
|
|
|
|
bottomBar = {
|
|
|
|
Box(
|
|
|
|
modifier = Modifier.fillMaxWidth(),
|
|
|
|
contentAlignment = Alignment.BottomEnd,
|
|
|
|
) {
|
2022-06-30 20:20:16 +07:00
|
|
|
SharedMangaBottomActionMenu(
|
2022-07-19 03:42:46 +07:00
|
|
|
selected = chapters.filter { it.selected },
|
2022-06-30 20:20:16 +07:00
|
|
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
|
|
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
|
|
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
|
|
|
onDownloadChapter = onDownloadChapter,
|
|
|
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
|
|
|
fillFraction = 0.5f,
|
2022-06-25 22:03:48 +07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
|
|
|
floatingActionButton = {
|
|
|
|
AnimatedVisibility(
|
2022-07-19 03:42:46 +07:00
|
|
|
visible = chapters.any { !it.chapter.read } && chapters.none { it.selected },
|
2022-06-25 22:03:48 +07:00
|
|
|
enter = fadeIn(),
|
|
|
|
exit = fadeOut(),
|
|
|
|
) {
|
|
|
|
ExtendedFloatingActionButton(
|
|
|
|
text = {
|
|
|
|
val id = if (chapters.any { it.chapter.read }) {
|
|
|
|
R.string.action_resume
|
|
|
|
} else {
|
|
|
|
R.string.action_start
|
|
|
|
}
|
2022-06-25 11:20:34 -04:00
|
|
|
Text(text = stringResource(id))
|
2022-06-25 22:03:48 +07:00
|
|
|
},
|
2022-10-29 23:43:51 +08:00
|
|
|
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
2022-06-25 22:03:48 +07:00
|
|
|
onClick = onContinueReading,
|
|
|
|
expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(),
|
|
|
|
modifier = Modifier
|
|
|
|
.padding(WindowInsets.navigationBars.only(WindowInsetsSides.Bottom).asPaddingValues()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
) { contentPadding ->
|
2022-10-16 23:15:01 +07:00
|
|
|
TwoPanelBox(
|
|
|
|
startContent = {
|
|
|
|
Column(
|
|
|
|
modifier = Modifier
|
|
|
|
.verticalScroll(rememberScrollState()),
|
|
|
|
) {
|
|
|
|
MangaInfoBox(
|
2022-10-22 22:15:12 +08:00
|
|
|
isTabletUi = true,
|
2022-10-16 23:15:01 +07:00
|
|
|
appBarPadding = contentPadding.calculateTopPadding(),
|
|
|
|
title = state.manga.title,
|
|
|
|
author = state.manga.author,
|
|
|
|
artist = state.manga.artist,
|
|
|
|
sourceName = remember { state.source.getNameForMangaInfo() },
|
|
|
|
isStubSource = remember { state.source is SourceManager.StubSource },
|
|
|
|
coverDataProvider = { state.manga },
|
|
|
|
status = state.manga.status,
|
|
|
|
onCoverClick = onCoverClicked,
|
|
|
|
doSearch = onSearch,
|
|
|
|
)
|
|
|
|
MangaActionRow(
|
|
|
|
favorite = state.manga.favorite,
|
|
|
|
trackingCount = state.trackingCount,
|
|
|
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
|
|
|
onWebViewClicked = onWebViewClicked,
|
|
|
|
onTrackingClicked = onTrackingClicked,
|
|
|
|
onEditCategory = onEditCategoryClicked,
|
|
|
|
)
|
|
|
|
ExpandableMangaDescription(
|
|
|
|
defaultExpandState = true,
|
|
|
|
description = state.manga.description,
|
|
|
|
tagsProvider = { state.manga.genre },
|
|
|
|
onTagClicked = onTagClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
endContent = {
|
|
|
|
VerticalFastScroller(
|
|
|
|
listState = chapterListState,
|
|
|
|
topContentPadding = contentPadding.calculateTopPadding(),
|
2022-06-25 22:03:48 +07:00
|
|
|
) {
|
2022-10-16 23:15:01 +07:00
|
|
|
LazyColumn(
|
|
|
|
modifier = Modifier.fillMaxHeight(),
|
|
|
|
state = chapterListState,
|
|
|
|
contentPadding = PaddingValues(
|
|
|
|
top = contentPadding.calculateTopPadding(),
|
|
|
|
bottom = contentPadding.calculateBottomPadding(),
|
|
|
|
),
|
2022-07-10 03:20:40 +07:00
|
|
|
) {
|
2022-10-16 23:15:01 +07:00
|
|
|
item(
|
|
|
|
key = MangaScreenItem.CHAPTER_HEADER,
|
|
|
|
contentType = MangaScreenItem.CHAPTER_HEADER,
|
|
|
|
) {
|
|
|
|
ChapterHeader(
|
|
|
|
chapterCount = chapters.size,
|
|
|
|
onClick = onFilterButtonClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
sharedChapterItems(
|
|
|
|
chapters = chapters,
|
|
|
|
onChapterClicked = onChapterClicked,
|
|
|
|
onDownloadChapter = onDownloadChapter,
|
|
|
|
onChapterSelected = onChapterSelected,
|
2022-06-25 22:03:48 +07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2022-10-16 23:15:01 +07:00
|
|
|
},
|
|
|
|
)
|
2022-06-25 22:03:48 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-30 20:20:16 +07:00
|
|
|
@Composable
|
|
|
|
private fun SharedMangaBottomActionMenu(
|
2022-07-19 03:42:46 +07:00
|
|
|
selected: List<ChapterItem>,
|
2022-09-28 09:20:10 +07:00
|
|
|
modifier: Modifier = Modifier,
|
2022-06-30 20:20:16 +07:00
|
|
|
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
|
|
|
|
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
|
|
|
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
|
|
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
|
|
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
|
|
|
fillFraction: Float,
|
|
|
|
) {
|
|
|
|
MangaBottomActionMenu(
|
|
|
|
visible = selected.isNotEmpty(),
|
2022-09-28 09:20:10 +07:00
|
|
|
modifier = modifier.fillMaxWidth(fillFraction),
|
2022-06-30 20:20:16 +07:00
|
|
|
onBookmarkClicked = {
|
|
|
|
onMultiBookmarkClicked.invoke(selected.map { it.chapter }, true)
|
|
|
|
}.takeIf { selected.any { !it.chapter.bookmark } },
|
|
|
|
onRemoveBookmarkClicked = {
|
|
|
|
onMultiBookmarkClicked.invoke(selected.map { it.chapter }, false)
|
|
|
|
}.takeIf { selected.all { it.chapter.bookmark } },
|
|
|
|
onMarkAsReadClicked = {
|
|
|
|
onMultiMarkAsReadClicked(selected.map { it.chapter }, true)
|
|
|
|
}.takeIf { selected.any { !it.chapter.read } },
|
|
|
|
onMarkAsUnreadClicked = {
|
|
|
|
onMultiMarkAsReadClicked(selected.map { it.chapter }, false)
|
2022-10-08 13:26:25 -04:00
|
|
|
}.takeIf { selected.any { it.chapter.read || it.chapter.lastPageRead > 0L } },
|
2022-06-30 20:20:16 +07:00
|
|
|
onMarkPreviousAsReadClicked = {
|
|
|
|
onMarkPreviousAsReadClicked(selected[0].chapter)
|
|
|
|
}.takeIf { selected.size == 1 },
|
|
|
|
onDownloadClicked = {
|
|
|
|
onDownloadChapter!!(selected.toList(), ChapterDownloadAction.START)
|
|
|
|
}.takeIf {
|
|
|
|
onDownloadChapter != null && selected.any { it.downloadState != Download.State.DOWNLOADED }
|
|
|
|
},
|
|
|
|
onDeleteClicked = {
|
|
|
|
onMultiDeleteClicked(selected.map { it.chapter })
|
|
|
|
}.takeIf {
|
|
|
|
onDownloadChapter != null && selected.any { it.downloadState == Download.State.DOWNLOADED }
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun LazyListScope.sharedChapterItems(
|
|
|
|
chapters: List<ChapterItem>,
|
|
|
|
onChapterClicked: (Chapter) -> Unit,
|
|
|
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
2022-07-19 03:42:46 +07:00
|
|
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
2022-06-30 20:20:16 +07:00
|
|
|
) {
|
2022-07-10 03:20:40 +07:00
|
|
|
items(
|
|
|
|
items = chapters,
|
2022-09-26 17:23:04 -04:00
|
|
|
key = { "chapter-${it.chapter.id}" },
|
2022-07-10 03:20:40 +07:00
|
|
|
contentType = { MangaScreenItem.CHAPTER },
|
|
|
|
) { chapterItem ->
|
2022-06-30 20:20:16 +07:00
|
|
|
val haptic = LocalHapticFeedback.current
|
|
|
|
MangaChapterListItem(
|
2022-07-10 03:20:40 +07:00
|
|
|
title = chapterItem.chapterTitleString,
|
|
|
|
date = chapterItem.dateUploadString,
|
|
|
|
readProgress = chapterItem.readProgressString,
|
|
|
|
scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() },
|
|
|
|
read = chapterItem.chapter.read,
|
|
|
|
bookmark = chapterItem.chapter.bookmark,
|
2022-07-19 03:42:46 +07:00
|
|
|
selected = chapterItem.selected,
|
2022-07-10 03:20:40 +07:00
|
|
|
downloadStateProvider = { chapterItem.downloadState },
|
|
|
|
downloadProgressProvider = { chapterItem.downloadProgress },
|
2022-06-30 20:20:16 +07:00
|
|
|
onLongClick = {
|
2022-07-19 03:42:46 +07:00
|
|
|
onChapterSelected(chapterItem, !chapterItem.selected, true, true)
|
|
|
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
2022-06-30 20:20:16 +07:00
|
|
|
},
|
|
|
|
onClick = {
|
|
|
|
onChapterItemClick(
|
|
|
|
chapterItem = chapterItem,
|
|
|
|
chapters = chapters,
|
2022-07-19 03:42:46 +07:00
|
|
|
onToggleSelection = { onChapterSelected(chapterItem, !chapterItem.selected, true, false) },
|
2022-06-30 20:20:16 +07:00
|
|
|
onChapterClicked = onChapterClicked,
|
|
|
|
)
|
|
|
|
},
|
|
|
|
onDownloadClick = if (onDownloadChapter != null) {
|
|
|
|
{ onDownloadChapter(listOf(chapterItem), it) }
|
2022-09-10 23:57:03 -04:00
|
|
|
} else {
|
|
|
|
null
|
|
|
|
},
|
2022-06-30 20:20:16 +07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onChapterItemClick(
|
2022-06-25 22:03:48 +07:00
|
|
|
chapterItem: ChapterItem,
|
|
|
|
chapters: List<ChapterItem>,
|
2022-07-19 03:42:46 +07:00
|
|
|
onToggleSelection: (Boolean) -> Unit,
|
2022-06-25 22:03:48 +07:00
|
|
|
onChapterClicked: (Chapter) -> Unit,
|
|
|
|
) {
|
|
|
|
when {
|
2022-07-19 03:42:46 +07:00
|
|
|
chapterItem.selected -> onToggleSelection(false)
|
|
|
|
chapters.any { it.selected } -> onToggleSelection(true)
|
2022-06-25 22:03:48 +07:00
|
|
|
else -> onChapterClicked(chapterItem.chapter)
|
|
|
|
}
|
|
|
|
}
|