Add button to quickly select all chapters
This commit is contained in:
@@ -6,11 +6,12 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, CircularProgress, Stack, styled } from '@mui/material';
|
import { CircularProgress, Stack, styled, Tooltip } from '@mui/material';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import React, { ComponentProps, useMemo, useState } from 'react';
|
import React, { ComponentProps, useMemo, useState } from 'react';
|
||||||
import { Virtuoso } from 'react-virtuoso';
|
import { Virtuoso } from 'react-virtuoso';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
import { TChapter, TManga, TranslationKey } from '@/typings';
|
import { TChapter, TManga, TranslationKey } from '@/typings';
|
||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
import { ChapterCard } from '@/components/manga/ChapterCard';
|
import { ChapterCard } from '@/components/manga/ChapterCard';
|
||||||
@@ -24,6 +25,16 @@ import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab';
|
|||||||
import { DownloadType, UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts';
|
import { DownloadType, UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts';
|
||||||
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
||||||
|
|
||||||
|
const ChapterListHeader = styled(Stack)(({ theme }) => ({
|
||||||
|
margin: 8,
|
||||||
|
marginBottom: 0,
|
||||||
|
marginRight: '10px',
|
||||||
|
minHeight: 40,
|
||||||
|
[theme.breakpoints.down('md')]: {
|
||||||
|
marginRight: 0,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({
|
const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({
|
||||||
listStyle: 'none',
|
listStyle: 'none',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
@@ -97,6 +108,9 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
|
|||||||
const nextChapterIndexToRead = (manga.lastReadChapter?.sourceOrder ?? 0) + 1;
|
const nextChapterIndexToRead = (manga.lastReadChapter?.sourceOrder ?? 0) + 1;
|
||||||
const isLatestChapterRead = manga.chapters.totalCount === manga.lastReadChapter?.sourceOrder;
|
const isLatestChapterRead = manga.chapters.totalCount === manga.lastReadChapter?.sourceOrder;
|
||||||
|
|
||||||
|
const areAllChaptersSelected = selection?.length === chapters.length;
|
||||||
|
const areNoneChaptersSelected = !selection;
|
||||||
|
|
||||||
const handleSelection = (index: number) => {
|
const handleSelection = (index: number) => {
|
||||||
const chapter = visibleChapters[index];
|
const chapter = visibleChapters[index];
|
||||||
if (!chapter) return;
|
if (!chapter) return;
|
||||||
@@ -111,14 +125,18 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectAll = () => {
|
const handleSelectAll = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (selection === null) return;
|
const selectAll = event.target.checked;
|
||||||
setSelection(visibleChapters.map((c) => c.id));
|
switch (selectAll) {
|
||||||
};
|
case true:
|
||||||
|
setSelection(visibleChapters.map((c) => c.id));
|
||||||
const handleClear = () => {
|
break;
|
||||||
if (selection === null) return;
|
case false:
|
||||||
setSelection(null);
|
setSelection(null);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFabAction: ComponentProps<typeof SelectionFAB>['onAction'] = (action, actionChapters) => {
|
const handleFabAction: ComponentProps<typeof SelectionFAB>['onAction'] = (action, actionChapters) => {
|
||||||
@@ -219,36 +237,27 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack direction="column" sx={{ position: 'relative' }}>
|
<Stack direction="column" sx={{ position: 'relative' }}>
|
||||||
<Stack
|
<ChapterListHeader direction="row" alignItems="center" justifyContent="space-between">
|
||||||
direction="row"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="space-between"
|
|
||||||
sx={{
|
|
||||||
m: 1,
|
|
||||||
mb: 0,
|
|
||||||
mr: 2,
|
|
||||||
minHeight: 40,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="h5">
|
<Typography variant="h5">
|
||||||
{`${visibleChapters.length} ${t('chapter.title', {
|
{`${visibleChapters.length} ${t('chapter.title', {
|
||||||
count: visibleChapters.length,
|
count: visibleChapters.length,
|
||||||
})}`}
|
})}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{selection === null ? (
|
<Stack direction="row" sx={{ paddingRight: '24px' }}>
|
||||||
<ChaptersToolbarMenu options={options} optionsDispatch={dispatch} />
|
<ChaptersToolbarMenu options={options} optionsDispatch={dispatch} />
|
||||||
) : (
|
<Tooltip
|
||||||
<Stack direction="row">
|
title={t(!areAllChaptersSelected ? 'global.button.select_all' : 'global.button.clear')}
|
||||||
<Button size="small" onClick={handleSelectAll}>
|
>
|
||||||
{t('global.button.select_all')}
|
<Checkbox
|
||||||
</Button>
|
sx={{ padding: '8px' }}
|
||||||
<Button size="small" onClick={handleClear}>
|
checked={areAllChaptersSelected}
|
||||||
{t('global.button.clear')}
|
indeterminate={!areAllChaptersSelected && !areNoneChaptersSelected}
|
||||||
</Button>
|
onChange={handleSelectAll}
|
||||||
</Stack>
|
/>
|
||||||
)}
|
</Tooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</ChapterListHeader>
|
||||||
|
|
||||||
{noChaptersFound && <EmptyView message={t('chapter.error.label.no_chapter_found')} />}
|
{noChaptersFound && <EmptyView message={t('chapter.error.label.no_chapter_found')} />}
|
||||||
{noChaptersMatchingFilter && <EmptyView message={t('chapter.error.label.no_matches')} />}
|
{noChaptersMatchingFilter && <EmptyView message={t('chapter.error.label.no_matches')} />}
|
||||||
|
|||||||
Reference in New Issue
Block a user