Feature/download ahead while reading (#464)

* Download next chapters when marking chapter as read while reading

* Add "auto delete chapter" settings

* Delete chapter after finished reading

* Delete chapters after marking them as read

* Improve "mark previous as read" action

Send only ids of chapters that are actually unread
This commit is contained in:
schroda
2023-11-19 21:17:35 +01:00
committed by GitHub
parent e26907e2ce
commit d12ac27b61
10 changed files with 240 additions and 31 deletions

View File

@@ -32,10 +32,11 @@ import { getUploadDateString } from '@/util/date';
import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator';
import { DownloadType, UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts';
import { TChapter } from '@/typings.ts';
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
interface IProps {
chapter: TChapter;
chapterIds: number[];
allChapters: TChapter[];
downloadChapter: DownloadType | undefined;
showChapterNumber: boolean;
onSelect: (selected: boolean) => void;
@@ -46,9 +47,11 @@ export const ChapterCard: React.FC<IProps> = (props: IProps) => {
const { t } = useTranslation();
const theme = useTheme();
const { chapter, chapterIds, downloadChapter: dc, showChapterNumber, onSelect, selected } = props;
const { chapter, allChapters, downloadChapter: dc, showChapterNumber, onSelect, selected } = props;
const isSelecting = selected !== null;
const { settings: metadataServerSettings } = useMetadataServerSettings();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const handleMenuClick = (event: React.MouseEvent<HTMLButtonElement>) => {
@@ -67,9 +70,29 @@ export const ChapterCard: React.FC<IProps> = (props: IProps) => {
const sendChange = <Key extends keyof UpdatePatchInput>(key: Key, value: UpdatePatchInput[Key]) => {
handleClose();
const isMarkAsRead = (key === 'isRead' && value) || key === 'markPrevRead';
const shouldAutoDeleteChapters = isMarkAsRead && metadataServerSettings.deleteChaptersManuallyMarkedRead;
if (shouldAutoDeleteChapters) {
const shouldDeleteChapter = ({ isBookmarked, isDownloaded }: TChapter) =>
isDownloaded && (!isBookmarked || metadataServerSettings.deleteChaptersWithBookmark);
const chaptersToDelete = key === 'isRead' ? [chapter] : allChapters;
const chapterIdsToDelete = chaptersToDelete
.filter(shouldDeleteChapter)
.map(({ id: chapterId }) => chapterId);
requestManager.deleteDownloadedChapters(chapterIdsToDelete).response.catch(() => {});
}
if (key === 'markPrevRead') {
const index = chapterIds.findIndex((chapterId) => chapterId === chapter.id);
requestManager.updateChapters(chapterIds.slice(index), { isRead: true });
const index = allChapters.findIndex(({ id: chapterId }) => chapterId === chapter.id);
requestManager.updateChapters(
allChapters
.slice(index)
.filter(({ isRead }) => !isRead)
.map(({ id: chapterId }) => chapterId),
{ isRead: true },
);
return;
}

View File

@@ -22,6 +22,7 @@ import { ChaptersToolbarMenu } from '@/components/manga/ChaptersToolbarMenu';
import { SelectionFAB } from '@/components/manga/SelectionFAB';
import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab';
import { DownloadType, UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts';
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({
listStyle: 'none',
@@ -89,7 +90,8 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
const [options, dispatch] = useChapterOptions(manga.id);
const { data: chaptersData, loading: isLoading, refetch } = requestManager.useGetMangaChapters(manga.id);
const chapters = useMemo(() => chaptersData?.chapters.nodes ?? [], [chaptersData?.chapters.nodes]);
const mangaChapterIds = useMemo(() => chapters.map((chapter) => chapter.id), [chapters]);
const { settings: metadataServerSettings } = useMetadataServerSettings();
useEffect(() => {
if (prevQueueRef.current && queue) {
@@ -164,6 +166,20 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
} else {
actionPromise = requestManager.updateChapters(chapterIds, change).response;
}
const shouldDeleteChapters =
action === 'mark_as_read' && metadataServerSettings.deleteChaptersManuallyMarkedRead;
if (shouldDeleteChapters) {
const chapterIdsToDelete = actionChapters
.filter(
({ chapter }) =>
chapter.isDownloaded &&
(!chapter.isBookmarked || metadataServerSettings.deleteChaptersWithBookmark),
)
.map(({ chapter }) => chapter.id);
requestManager.deleteDownloadedChapters(chapterIdsToDelete).response.catch(() => {});
}
}
actionPromise
@@ -285,7 +301,7 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
return (
<ChapterCard
{...chaptersWithMeta[index]}
chapterIds={mangaChapterIds}
allChapters={chapters}
showChapterNumber={options.showChapterNumber}
onSelect={() => handleSelection(index)}
/>