Show other manga chapter updates collapsed

Follow-up to 38de1e9e08
This commit is contained in:
schroda
2026-07-06 17:15:53 +02:00
parent 84695de684
commit 4629b4f868
2 changed files with 78 additions and 39 deletions

View File

@@ -11,21 +11,30 @@ import { AppRoutes } from '@/base/AppRoute.constants.ts';
import { Mangas } from '@/features/manga/services/Mangas.ts';
import type { MangaIdInfo, MangaThumbnailInfo, MangaTitleInfo } from '@/features/manga/Manga.types.ts';
import { ListCardAvatar } from '@/base/components/lists/cards/ListCardAvatar.tsx';
import type { ComponentProps } from 'react';
import merge from 'lodash/fp/merge';
export const ChapterCardThumbnail = ({
mangaId,
mangaTitle,
thumbnailUrl,
thumbnailUrlLastFetched,
slots,
}: MangaThumbnailInfo & {
mangaId: MangaIdInfo['id'];
mangaTitle: MangaTitleInfo['title'];
slots?: {
listCardAvatar?: Omit<ComponentProps<typeof ListCardAvatar>, 'alt' | 'iconUrl'>;
};
}) => (
<Link to={AppRoutes.manga.path(mangaId)} style={{ textDecoration: 'none' }}>
<ListCardAvatar
iconUrl={Mangas.getThumbnailUrl({ thumbnailUrl, thumbnailUrlLastFetched })}
alt={mangaTitle}
slots={{ spinnerImageProps: { imgStyle: { imageRendering: 'pixelated' } } }}
slots={merge(
{ spinnerImageProps: { imgStyle: { imageRendering: 'pixelated' } } },
slots?.listCardAvatar?.slots,
)}
/>
</Link>
);

View File

@@ -10,7 +10,7 @@ import Box from '@mui/material/Box';
import CardActionArea from '@mui/material/CardActionArea';
import Card from '@mui/material/Card';
import { Link } from 'react-router-dom';
import { memo, useMemo } from 'react';
import { memo, useState } from 'react';
import { DownloadStateIndicator } from '@/base/components/downloads/DownloadStateIndicator.tsx';
import type { ChapterUpdateListFieldsFragment } from '@/lib/graphql/generated/graphql.ts';
import { AppRoutes } from '@/base/AppRoute.constants.ts';
@@ -20,49 +20,31 @@ import { ChapterDownloadButton } from '@/features/chapter/components/buttons/Cha
import { ChapterDownloadRetryButton } from '@/features/chapter/components/buttons/ChapterDownloadRetryButton.tsx';
import { Chapters } from '@/features/chapter/services/Chapters.ts';
import { ListCardContent } from '@/base/components/lists/cards/ListCardContent.tsx';
import { useLingui } from '@lingui/react/macro';
import { plural } from '@lingui/core/macro';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';
import { MUIUtil } from '@/lib/mui/MUI.util.ts';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import Collapse from '@mui/material/Collapse';
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
import { useTheme } from '@mui/material/styles';
export const ChapterUpdateCard = memo(
({
chapter,
otherChapters,
otherChapters = STABLE_EMPTY_ARRAY,
}: {
chapter: ChapterUpdateListFieldsFragment;
otherChapters: ChapterUpdateListFieldsFragment[];
otherChapters?: ChapterUpdateListFieldsFragment[];
}) => {
const { manga } = chapter;
const { t } = useLingui();
const theme = useTheme();
const uniqueOtherChapters = useMemo(
() => Chapters.removeDuplicates(chapter, otherChapters),
[chapter, otherChapters],
);
const otherChaptersCount = uniqueOtherChapters.length;
const firstFewOtherChapters = uniqueOtherChapters.slice(-3);
const [isExpanded, setIsExpanded] = useState(false);
const otherChaptersText = (() => {
if (!otherChaptersCount) {
return '';
}
const firstFewUpdatesString = firstFewOtherChapters
.map((otherChapter) => `#${otherChapter.chapterNumber}`)
.toReversed()
.join(', ');
if (otherChaptersCount > firstFewOtherChapters.length) {
const remainingUpdatesCount = otherChaptersCount - firstFewOtherChapters.length;
return t`Plus chapters ${firstFewUpdatesString} and ${remainingUpdatesCount} more`;
}
return plural(firstFewOtherChapters.length, {
one: `Plus chapter ${firstFewUpdatesString}`,
other: `Plus chapters ${firstFewUpdatesString}`,
});
})();
const isGroup = !!otherChapters.length;
return (
<Card>
@@ -71,7 +53,7 @@ export const ChapterUpdateCard = memo(
to={AppRoutes.reader.path(chapter.manga.id, chapter.sourceOrder)}
state={Chapters.getReaderOpenChapterLocationState(chapter)}
sx={{
color: (theme) => theme.palette.text[chapter.isRead ? 'disabled' : 'primary'],
color: theme.palette.text[chapter.isRead ? 'disabled' : 'primary'],
}}
>
<ListCardContent sx={{ justifyContent: 'space-between' }}>
@@ -82,18 +64,66 @@ export const ChapterUpdateCard = memo(
mangaTitle={manga.title}
thumbnailUrl={manga.thumbnailUrl}
thumbnailUrlLastFetched={manga.thumbnailUrlLastFetched}
slots={
isGroup
? {
listCardAvatar: {
slots: {
spinnerImageProps: {
spinnerStyle: {
height: 74,
aspectRatio: '3 / 4',
},
imgStyle: {
height: 74,
aspectRatio: '3 / 4',
},
},
avatarProps: {
sx: {
height: 74,
},
},
},
},
}
: undefined
}
/>
<ChapterCardMetadata
title={manga.title}
secondaryText={chapter.name}
ternaryText={otherChaptersText}
/>
<Stack>
<ChapterCardMetadata title={manga.title} secondaryText={chapter.name} />
{isGroup && (
<Button
sx={{ width: 'fit-content', ...theme.typography.caption }}
variant="text"
size="small"
endIcon={isExpanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
{...MUIUtil.preventRippleProp()}
onClick={(e) => {
e.preventDefault();
setIsExpanded(!isExpanded);
}}
>
{plural(otherChapters.length, {
one: 'Show # more chapter',
other: 'Show # more chapters',
})}
</Button>
)}
</Stack>
</Box>
<DownloadStateIndicator chapterId={chapter.id} />
<ChapterDownloadRetryButton chapterId={chapter.id} />
<ChapterDownloadButton chapterId={chapter.id} isDownloaded={chapter.isDownloaded} />
</ListCardContent>
</CardActionArea>
{isGroup && (
<Collapse in={isExpanded} unmountOnExit>
{otherChapters.map((otherChapter) => (
<ChapterUpdateCard key={otherChapter.id} chapter={otherChapter} />
))}
</Collapse>
)}
</Card>
);
},