@@ -11,21 +11,30 @@ import { AppRoutes } from '@/base/AppRoute.constants.ts';
|
|||||||
import { Mangas } from '@/features/manga/services/Mangas.ts';
|
import { Mangas } from '@/features/manga/services/Mangas.ts';
|
||||||
import type { MangaIdInfo, MangaThumbnailInfo, MangaTitleInfo } from '@/features/manga/Manga.types.ts';
|
import type { MangaIdInfo, MangaThumbnailInfo, MangaTitleInfo } from '@/features/manga/Manga.types.ts';
|
||||||
import { ListCardAvatar } from '@/base/components/lists/cards/ListCardAvatar.tsx';
|
import { ListCardAvatar } from '@/base/components/lists/cards/ListCardAvatar.tsx';
|
||||||
|
import type { ComponentProps } from 'react';
|
||||||
|
import merge from 'lodash/fp/merge';
|
||||||
|
|
||||||
export const ChapterCardThumbnail = ({
|
export const ChapterCardThumbnail = ({
|
||||||
mangaId,
|
mangaId,
|
||||||
mangaTitle,
|
mangaTitle,
|
||||||
thumbnailUrl,
|
thumbnailUrl,
|
||||||
thumbnailUrlLastFetched,
|
thumbnailUrlLastFetched,
|
||||||
|
slots,
|
||||||
}: MangaThumbnailInfo & {
|
}: MangaThumbnailInfo & {
|
||||||
mangaId: MangaIdInfo['id'];
|
mangaId: MangaIdInfo['id'];
|
||||||
mangaTitle: MangaTitleInfo['title'];
|
mangaTitle: MangaTitleInfo['title'];
|
||||||
|
slots?: {
|
||||||
|
listCardAvatar?: Omit<ComponentProps<typeof ListCardAvatar>, 'alt' | 'iconUrl'>;
|
||||||
|
};
|
||||||
}) => (
|
}) => (
|
||||||
<Link to={AppRoutes.manga.path(mangaId)} style={{ textDecoration: 'none' }}>
|
<Link to={AppRoutes.manga.path(mangaId)} style={{ textDecoration: 'none' }}>
|
||||||
<ListCardAvatar
|
<ListCardAvatar
|
||||||
iconUrl={Mangas.getThumbnailUrl({ thumbnailUrl, thumbnailUrlLastFetched })}
|
iconUrl={Mangas.getThumbnailUrl({ thumbnailUrl, thumbnailUrlLastFetched })}
|
||||||
alt={mangaTitle}
|
alt={mangaTitle}
|
||||||
slots={{ spinnerImageProps: { imgStyle: { imageRendering: 'pixelated' } } }}
|
slots={merge(
|
||||||
|
{ spinnerImageProps: { imgStyle: { imageRendering: 'pixelated' } } },
|
||||||
|
slots?.listCardAvatar?.slots,
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Box from '@mui/material/Box';
|
|||||||
import CardActionArea from '@mui/material/CardActionArea';
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
import Card from '@mui/material/Card';
|
import Card from '@mui/material/Card';
|
||||||
import { Link } from 'react-router-dom';
|
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 { DownloadStateIndicator } from '@/base/components/downloads/DownloadStateIndicator.tsx';
|
||||||
import type { ChapterUpdateListFieldsFragment } from '@/lib/graphql/generated/graphql.ts';
|
import type { ChapterUpdateListFieldsFragment } from '@/lib/graphql/generated/graphql.ts';
|
||||||
import { AppRoutes } from '@/base/AppRoute.constants.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 { ChapterDownloadRetryButton } from '@/features/chapter/components/buttons/ChapterDownloadRetryButton.tsx';
|
||||||
import { Chapters } from '@/features/chapter/services/Chapters.ts';
|
import { Chapters } from '@/features/chapter/services/Chapters.ts';
|
||||||
import { ListCardContent } from '@/base/components/lists/cards/ListCardContent.tsx';
|
import { ListCardContent } from '@/base/components/lists/cards/ListCardContent.tsx';
|
||||||
import { useLingui } from '@lingui/react/macro';
|
|
||||||
import { plural } from '@lingui/core/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(
|
export const ChapterUpdateCard = memo(
|
||||||
({
|
({
|
||||||
chapter,
|
chapter,
|
||||||
otherChapters,
|
otherChapters = STABLE_EMPTY_ARRAY,
|
||||||
}: {
|
}: {
|
||||||
chapter: ChapterUpdateListFieldsFragment;
|
chapter: ChapterUpdateListFieldsFragment;
|
||||||
otherChapters: ChapterUpdateListFieldsFragment[];
|
otherChapters?: ChapterUpdateListFieldsFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const { manga } = chapter;
|
const { manga } = chapter;
|
||||||
|
|
||||||
const { t } = useLingui();
|
const theme = useTheme();
|
||||||
|
|
||||||
const uniqueOtherChapters = useMemo(
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
() => Chapters.removeDuplicates(chapter, otherChapters),
|
|
||||||
[chapter, otherChapters],
|
|
||||||
);
|
|
||||||
const otherChaptersCount = uniqueOtherChapters.length;
|
|
||||||
const firstFewOtherChapters = uniqueOtherChapters.slice(-3);
|
|
||||||
|
|
||||||
const otherChaptersText = (() => {
|
const isGroup = !!otherChapters.length;
|
||||||
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}`,
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
@@ -71,7 +53,7 @@ export const ChapterUpdateCard = memo(
|
|||||||
to={AppRoutes.reader.path(chapter.manga.id, chapter.sourceOrder)}
|
to={AppRoutes.reader.path(chapter.manga.id, chapter.sourceOrder)}
|
||||||
state={Chapters.getReaderOpenChapterLocationState(chapter)}
|
state={Chapters.getReaderOpenChapterLocationState(chapter)}
|
||||||
sx={{
|
sx={{
|
||||||
color: (theme) => theme.palette.text[chapter.isRead ? 'disabled' : 'primary'],
|
color: theme.palette.text[chapter.isRead ? 'disabled' : 'primary'],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListCardContent sx={{ justifyContent: 'space-between' }}>
|
<ListCardContent sx={{ justifyContent: 'space-between' }}>
|
||||||
@@ -82,18 +64,66 @@ export const ChapterUpdateCard = memo(
|
|||||||
mangaTitle={manga.title}
|
mangaTitle={manga.title}
|
||||||
thumbnailUrl={manga.thumbnailUrl}
|
thumbnailUrl={manga.thumbnailUrl}
|
||||||
thumbnailUrlLastFetched={manga.thumbnailUrlLastFetched}
|
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
|
<Stack>
|
||||||
title={manga.title}
|
<ChapterCardMetadata title={manga.title} secondaryText={chapter.name} />
|
||||||
secondaryText={chapter.name}
|
{isGroup && (
|
||||||
ternaryText={otherChaptersText}
|
<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>
|
</Box>
|
||||||
<DownloadStateIndicator chapterId={chapter.id} />
|
<DownloadStateIndicator chapterId={chapter.id} />
|
||||||
<ChapterDownloadRetryButton chapterId={chapter.id} />
|
<ChapterDownloadRetryButton chapterId={chapter.id} />
|
||||||
<ChapterDownloadButton chapterId={chapter.id} isDownloaded={chapter.isDownloaded} />
|
<ChapterDownloadButton chapterId={chapter.id} isDownloaded={chapter.isDownloaded} />
|
||||||
</ListCardContent>
|
</ListCardContent>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
|
{isGroup && (
|
||||||
|
<Collapse in={isExpanded} unmountOnExit>
|
||||||
|
{otherChapters.map((otherChapter) => (
|
||||||
|
<ChapterUpdateCard key={otherChapter.id} chapter={otherChapter} />
|
||||||
|
))}
|
||||||
|
</Collapse>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user