Feature/reader skip duplicate chapters (#262)
* Skip duplicate chapters in the Reader * Debounce "end of page" handling in the reader * Update keyboard event listener In case the "go" function changed, the listeners have to be updated
This commit is contained in:
@@ -25,7 +25,7 @@ import Collapse from '@mui/material/Collapse';
|
|||||||
import { styled } from '@mui/system';
|
import { styled } from '@mui/system';
|
||||||
import useBackTo from 'util/useBackTo';
|
import useBackTo from 'util/useBackTo';
|
||||||
import ReaderSettingsOptions from 'components/reader/ReaderSettingsOptions';
|
import ReaderSettingsOptions from 'components/reader/ReaderSettingsOptions';
|
||||||
import { IChapter, IManga, IMangaCard, IReaderSettings } from 'typings';
|
import { ChapterOffset, IChapter, IManga, IMangaCard, IReaderSettings } from 'typings';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const Root = styled('div')(({ theme }) => ({
|
const Root = styled('div')(({ theme }) => ({
|
||||||
@@ -119,6 +119,8 @@ interface IProps {
|
|||||||
chapter: IChapter;
|
chapter: IChapter;
|
||||||
curPage: number;
|
curPage: number;
|
||||||
scrollToPage: (page: number) => void;
|
scrollToPage: (page: number) => void;
|
||||||
|
openNextChapter: (offset: ChapterOffset, setHistory: (nextChapterIndex: number) => void) => Promise<void>;
|
||||||
|
retrievingNextChapter: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ReaderNavBar(props: IProps) {
|
export default function ReaderNavBar(props: IProps) {
|
||||||
@@ -131,7 +133,8 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
}>();
|
}>();
|
||||||
const { prevDrawerOpen, prevSettingsCollapseOpen } = location.state ?? {};
|
const { prevDrawerOpen, prevSettingsCollapseOpen } = location.state ?? {};
|
||||||
|
|
||||||
const { settings, setSettingValue, manga, chapter, curPage, scrollToPage } = props;
|
const { settings, setSettingValue, manga, chapter, curPage, scrollToPage, openNextChapter, retrievingNextChapter } =
|
||||||
|
props;
|
||||||
|
|
||||||
const [drawerOpen, setDrawerOpen] = useState(settings.staticNav || prevDrawerOpen);
|
const [drawerOpen, setDrawerOpen] = useState(settings.staticNav || prevDrawerOpen);
|
||||||
const [updateDrawerOnRender, setUpdateDrawerOnRender] = useState(true);
|
const [updateDrawerOnRender, setUpdateDrawerOnRender] = useState(true);
|
||||||
@@ -139,6 +142,8 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
||||||
const [settingsCollapseOpen, setSettingsCollapseOpen] = useState(prevSettingsCollapseOpen ?? true);
|
const [settingsCollapseOpen, setSettingsCollapseOpen] = useState(prevSettingsCollapseOpen ?? true);
|
||||||
|
|
||||||
|
const disableChapterNavButtons = retrievingNextChapter;
|
||||||
|
|
||||||
const updateSettingValue = (key: keyof IReaderSettings, value: string | boolean) => {
|
const updateSettingValue = (key: keyof IReaderSettings, value: string | boolean) => {
|
||||||
// prevent closing the navBar when updating the "staticNav" setting
|
// prevent closing the navBar when updating the "staticNav" setting
|
||||||
setUpdateDrawerOnRender(key !== 'staticNav');
|
setUpdateDrawerOnRender(key !== 'staticNav');
|
||||||
@@ -254,6 +259,7 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
staticNav={settings.staticNav}
|
staticNav={settings.staticNav}
|
||||||
showPageNumber={settings.showPageNumber}
|
showPageNumber={settings.showPageNumber}
|
||||||
loadNextOnEnding={settings.loadNextOnEnding}
|
loadNextOnEnding={settings.loadNextOnEnding}
|
||||||
|
skipDupChapters={settings.skipDupChapters}
|
||||||
readerType={settings.readerType}
|
readerType={settings.readerType}
|
||||||
/>
|
/>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
@@ -261,7 +267,11 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
<Navigation>
|
<Navigation>
|
||||||
<PageNavigation>
|
<PageNavigation>
|
||||||
<span>{t('reader.page_info.label.currently_on_page')}</span>
|
<span>{t('reader.page_info.label.currently_on_page')}</span>
|
||||||
<FormControl size="small" sx={{ margin: '0 5px' }} disabled={chapter.pageCount === -1}>
|
<FormControl
|
||||||
|
size="small"
|
||||||
|
sx={{ margin: '0 5px' }}
|
||||||
|
disabled={disableChapterNavButtons || chapter.pageCount === -1}
|
||||||
|
>
|
||||||
<Select
|
<Select
|
||||||
MenuProps={MenuProps}
|
MenuProps={MenuProps}
|
||||||
value={chapter.pageCount > -1 ? curPage : ''}
|
value={chapter.pageCount > -1 ? curPage : ''}
|
||||||
@@ -286,20 +296,26 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
<IconButton
|
<IconButton
|
||||||
title={t('reader.button.previous_chapter')}
|
title={t('reader.button.previous_chapter')}
|
||||||
sx={{ gridArea: 'pre' }}
|
sx={{ gridArea: 'pre' }}
|
||||||
disabled={chapter.index <= 1}
|
disabled={disableChapterNavButtons || chapter.index <= 1}
|
||||||
onClick={() => {
|
onClick={() =>
|
||||||
history.replace({
|
openNextChapter(ChapterOffset.PREV, (prevChapterIndex) => {
|
||||||
pathname: `/manga/${manga.id}/chapter/${chapter.index - 1}`,
|
history.replace({
|
||||||
state: {
|
pathname: `/manga/${manga.id}/chapter/${prevChapterIndex}`,
|
||||||
prevDrawerOpen: drawerOpen,
|
state: {
|
||||||
prevSettingsCollapseOpen: settingsCollapseOpen,
|
prevDrawerOpen: drawerOpen,
|
||||||
},
|
prevSettingsCollapseOpen: settingsCollapseOpen,
|
||||||
});
|
},
|
||||||
}}
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<KeyboardArrowLeftIcon />
|
<KeyboardArrowLeftIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<FormControl sx={{ gridArea: 'current' }} size="small" disabled={chapter.index < 1}>
|
<FormControl
|
||||||
|
sx={{ gridArea: 'current' }}
|
||||||
|
size="small"
|
||||||
|
disabled={disableChapterNavButtons || chapter.index < 1}
|
||||||
|
>
|
||||||
<Select
|
<Select
|
||||||
MenuProps={MenuProps}
|
MenuProps={MenuProps}
|
||||||
value={chapter.index >= 1 ? chapter.index : ''}
|
value={chapter.index >= 1 ? chapter.index : ''}
|
||||||
@@ -328,15 +344,21 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
<IconButton
|
<IconButton
|
||||||
title={t('reader.button.next_chapter')}
|
title={t('reader.button.next_chapter')}
|
||||||
sx={{ gridArea: 'next' }}
|
sx={{ gridArea: 'next' }}
|
||||||
disabled={chapter.index < 1 || chapter.index >= chapter.chapterCount}
|
disabled={
|
||||||
|
disableChapterNavButtons ||
|
||||||
|
chapter.index < 1 ||
|
||||||
|
chapter.index >= chapter.chapterCount
|
||||||
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
history.replace({
|
openNextChapter(ChapterOffset.NEXT, (nextChapterIndex) =>
|
||||||
pathname: `/manga/${manga.id}/chapter/${chapter.index + 1}`,
|
history.replace({
|
||||||
state: {
|
pathname: `/manga/${manga.id}/chapter/${nextChapterIndex}`,
|
||||||
prevDrawerOpen: drawerOpen,
|
state: {
|
||||||
prevSettingsCollapseOpen: settingsCollapseOpen,
|
prevDrawerOpen: drawerOpen,
|
||||||
},
|
prevSettingsCollapseOpen: settingsCollapseOpen,
|
||||||
});
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<KeyboardArrowRightIcon />
|
<KeyboardArrowRightIcon />
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export default function ReaderSettingsOptions({
|
|||||||
loadNextOnEnding,
|
loadNextOnEnding,
|
||||||
readerType,
|
readerType,
|
||||||
showPageNumber,
|
showPageNumber,
|
||||||
|
skipDupChapters,
|
||||||
setSettingValue,
|
setSettingValue,
|
||||||
}: IProps) {
|
}: IProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -59,6 +60,16 @@ export default function ReaderSettingsOptions({
|
|||||||
/>
|
/>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText primary={t('reader.settings.label.skip_dup_chapters')} />
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<Switch
|
||||||
|
edge="end"
|
||||||
|
checked={skipDupChapters}
|
||||||
|
onChange={(e) => setSettingValue('skipDupChapters', e.target.checked)}
|
||||||
|
/>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
</ListItem>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText primary={t('reader.settings.label.reader_type')} />
|
<ListItemText primary={t('reader.settings.label.reader_type')} />
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export default function DoublePagedPager(props: IReaderProps) {
|
|||||||
document.removeEventListener('keydown', keyboardControl);
|
document.removeEventListener('keydown', keyboardControl);
|
||||||
selfRef.current?.removeEventListener('click', clickControl);
|
selfRef.current?.removeEventListener('click', clickControl);
|
||||||
};
|
};
|
||||||
}, [selfRef, curPage, settings.readerType]);
|
}, [selfRef, curPage, settings.readerType, prevChapter, nextChapter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurPage(initialPage);
|
setCurPage(initialPage);
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export default function HorizontalPager(props: IReaderProps) {
|
|||||||
document.removeEventListener('scroll', handleLoadNextonEnding);
|
document.removeEventListener('scroll', handleLoadNextonEnding);
|
||||||
selfRef.current?.removeEventListener('mousedown', clickControl);
|
selfRef.current?.removeEventListener('mousedown', clickControl);
|
||||||
};
|
};
|
||||||
}, [selfRef, curPage]);
|
}, [selfRef, curPage, prevChapter, nextChapter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export default function PagedReader(props: IReaderProps) {
|
|||||||
document.removeEventListener('keydown', keyboardControl);
|
document.removeEventListener('keydown', keyboardControl);
|
||||||
selfRef.current?.removeEventListener('click', clickControl);
|
selfRef.current?.removeEventListener('click', clickControl);
|
||||||
};
|
};
|
||||||
}, [selfRef, curPage, settings.readerType]);
|
}, [selfRef, curPage, settings.readerType, prevChapter, nextChapter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Delay scrolling to next cycle
|
// Delay scrolling to next cycle
|
||||||
|
|||||||
@@ -42,10 +42,17 @@ export default function VerticalPager(props: IReaderProps) {
|
|||||||
const pagesRef = useRef<HTMLDivElement[]>([]);
|
const pagesRef = useRef<HTMLDivElement[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let handlingEndOfPage = false;
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
if (!selfRef.current) return;
|
if (!selfRef.current) return;
|
||||||
|
|
||||||
if (isAtBottom()) {
|
if (isAtBottom()) {
|
||||||
|
if (handlingEndOfPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlingEndOfPage = true;
|
||||||
|
|
||||||
// If scroll is moved all the way to the bottom
|
// If scroll is moved all the way to the bottom
|
||||||
// This handles cases when last page is show, but is smaller then
|
// This handles cases when last page is show, but is smaller then
|
||||||
// window, in which case it would never get marked as read.
|
// window, in which case it would never get marked as read.
|
||||||
@@ -58,6 +65,8 @@ export default function VerticalPager(props: IReaderProps) {
|
|||||||
nextChapter();
|
nextChapter();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
handlingEndOfPage = false;
|
||||||
|
|
||||||
// Update current page in parent
|
// Update current page in parent
|
||||||
const currentPage = findCurrentPageIndex(selfRef.current);
|
const currentPage = findCurrentPageIndex(selfRef.current);
|
||||||
if (currentPage !== currentPageRef.current) {
|
if (currentPage !== currentPageRef.current) {
|
||||||
@@ -71,7 +80,7 @@ export default function VerticalPager(props: IReaderProps) {
|
|||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('scroll', handleScroll);
|
window.removeEventListener('scroll', handleScroll);
|
||||||
};
|
};
|
||||||
}, [settings.loadNextOnEnding]);
|
}, [settings.loadNextOnEnding, nextChapter]);
|
||||||
|
|
||||||
const go = useCallback(
|
const go = useCallback(
|
||||||
(direction: 'up' | 'down') => {
|
(direction: 'up' | 'down') => {
|
||||||
@@ -114,7 +123,7 @@ export default function VerticalPager(props: IReaderProps) {
|
|||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('keydown', handleKeyboard);
|
document.removeEventListener('keydown', handleKeyboard);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [go]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Delay scrolling to next cycle
|
// Delay scrolling to next cycle
|
||||||
|
|||||||
@@ -304,6 +304,12 @@
|
|||||||
"next_chapter": "Next Chapter",
|
"next_chapter": "Next Chapter",
|
||||||
"previous_chapter": "Previous Chapter"
|
"previous_chapter": "Previous Chapter"
|
||||||
},
|
},
|
||||||
|
"error": {
|
||||||
|
"label": {
|
||||||
|
"unable_to_get_next_chapter_skip_dup": "Unable to get the next chapter - \"$t(reader.settings.label.skip_dup_chapters)\" option is enabled",
|
||||||
|
"unable_to_get_prev_chapter_skip_dup": "Unable to get the previous chapter - \"$t(reader.settings.label.skip_dup_chapters)\" option is enabled"
|
||||||
|
}
|
||||||
|
},
|
||||||
"page_info": {
|
"page_info": {
|
||||||
"label": {
|
"label": {
|
||||||
"currently_on_page": "Currently on page",
|
"currently_on_page": "Currently on page",
|
||||||
@@ -321,6 +327,7 @@
|
|||||||
"load_next_chapter": "Load next chapter at ending",
|
"load_next_chapter": "Load next chapter at ending",
|
||||||
"reader_type": "Reader Type",
|
"reader_type": "Reader Type",
|
||||||
"show_page_number": "Show page number",
|
"show_page_number": "Show page number",
|
||||||
|
"skip_dup_chapters": "Skip duplicate chapters",
|
||||||
"static_navigation": "Static Navigation"
|
"static_navigation": "Static Navigation"
|
||||||
},
|
},
|
||||||
"reader_type": {
|
"reader_type": {
|
||||||
@@ -424,4 +431,4 @@
|
|||||||
},
|
},
|
||||||
"title": "Updates"
|
"title": "Updates"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,9 +25,44 @@ import {
|
|||||||
useDefaultReaderSettings,
|
useDefaultReaderSettings,
|
||||||
} from 'util/readerSettings';
|
} from 'util/readerSettings';
|
||||||
import makeToast from 'components/util/Toast';
|
import makeToast from 'components/util/Toast';
|
||||||
import { IChapter, IManga, IMangaCard, IPartialChapter, IReaderSettings, ReaderType } from 'typings';
|
import {
|
||||||
|
ChapterOffset,
|
||||||
|
IChapter,
|
||||||
|
IManga,
|
||||||
|
IMangaCard,
|
||||||
|
IPartialChapter,
|
||||||
|
IReaderSettings,
|
||||||
|
ReaderType,
|
||||||
|
TranslationKey,
|
||||||
|
} from 'typings';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const isDupChapter = async (chapterIndex: number, currentChapter: IChapter) => {
|
||||||
|
const nextChapter = (await client.get<IChapter>(`/api/v1/manga/${currentChapter.mangaId}/chapter/${chapterIndex}`))
|
||||||
|
.data;
|
||||||
|
|
||||||
|
return nextChapter.chapterNumber === currentChapter.chapterNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In case duplicated chapters should be skipped the function will check all next/prev chapters until
|
||||||
|
* - a non duplicated chapter was found
|
||||||
|
* - no prev/next chapter exists => chapter request will fail and error will be raised up
|
||||||
|
*/
|
||||||
|
const getOffsetChapter = async (
|
||||||
|
chapterIndex: number,
|
||||||
|
currentChapter: IChapter,
|
||||||
|
skipDupChapters: boolean,
|
||||||
|
offset: ChapterOffset,
|
||||||
|
): Promise<number> => {
|
||||||
|
const shouldSkipChapter = skipDupChapters && (await isDupChapter(chapterIndex, currentChapter));
|
||||||
|
if (shouldSkipChapter) {
|
||||||
|
return getOffsetChapter(chapterIndex + offset, currentChapter, skipDupChapters, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chapterIndex;
|
||||||
|
};
|
||||||
|
|
||||||
const getReaderComponent = (readerType: ReaderType) => {
|
const getReaderComponent = (readerType: ReaderType) => {
|
||||||
switch (readerType) {
|
switch (readerType) {
|
||||||
case 'ContinuesVertical':
|
case 'ContinuesVertical':
|
||||||
@@ -81,6 +116,7 @@ export default function Reader() {
|
|||||||
const [curPage, setCurPage] = useState<number>(0);
|
const [curPage, setCurPage] = useState<number>(0);
|
||||||
const [pageToScrollTo, setPageToScrollTo] = useState<number | undefined>(undefined);
|
const [pageToScrollTo, setPageToScrollTo] = useState<number | undefined>(undefined);
|
||||||
const { setOverride, setTitle } = useContext(NavbarContext);
|
const { setOverride, setTitle } = useContext(NavbarContext);
|
||||||
|
const [retrievingNextChapter, setRetrievingNextChapter] = useState(false);
|
||||||
|
|
||||||
const { settings: defaultSettings, loading: areDefaultSettingsLoading } = useDefaultReaderSettings();
|
const { settings: defaultSettings, loading: areDefaultSettingsLoading } = useDefaultReaderSettings();
|
||||||
const [settings, setSettings] = useState(getReaderSettingsFor(manga, defaultSettings));
|
const [settings, setSettings] = useState(getReaderSettingsFor(manga, defaultSettings));
|
||||||
@@ -93,6 +129,32 @@ export default function Reader() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openNextChapter = useCallback(
|
||||||
|
async (offset: ChapterOffset, setHistory: (nextChapterIndex: number) => void) => {
|
||||||
|
setRetrievingNextChapter(true);
|
||||||
|
try {
|
||||||
|
setHistory(
|
||||||
|
await getOffsetChapter(
|
||||||
|
chapter.index + offset,
|
||||||
|
chapter as IChapter,
|
||||||
|
settings.skipDupChapters,
|
||||||
|
offset,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
const offsetToTranslationKeyMap: { [chapterOffset in ChapterOffset]: TranslationKey } = {
|
||||||
|
[ChapterOffset.PREV]: 'reader.error.label.unable_to_get_prev_chapter_skip_dup',
|
||||||
|
[ChapterOffset.NEXT]: 'reader.error.label.unable_to_get_next_chapter_skip_dup',
|
||||||
|
};
|
||||||
|
|
||||||
|
makeToast(t(offsetToTranslationKeyMap[offset]) as string, 'error');
|
||||||
|
} finally {
|
||||||
|
setRetrievingNextChapter(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[chapter, settings],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!manga?.title || (chapter as IChapter)?.name === t('global.label.loading')) {
|
if (!manga?.title || (chapter as IChapter)?.name === t('global.label.loading')) {
|
||||||
setTitle(t('reader.title'));
|
setTitle(t('reader.title'));
|
||||||
@@ -120,13 +182,15 @@ export default function Reader() {
|
|||||||
chapter={chapter as IChapter}
|
chapter={chapter as IChapter}
|
||||||
curPage={curPage}
|
curPage={curPage}
|
||||||
scrollToPage={setPageToScrollTo}
|
scrollToPage={setPageToScrollTo}
|
||||||
|
openNextChapter={openNextChapter}
|
||||||
|
retrievingNextChapter={retrievingNextChapter}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// clean up for when we leave the reader
|
// clean up for when we leave the reader
|
||||||
return () => setOverride({ status: false, value: <div /> });
|
return () => setOverride({ status: false, value: <div /> });
|
||||||
}, [manga, chapter, settings, curPage, chapterIndex]);
|
}, [manga, chapter, settings, curPage, chapterIndex, retrievingNextChapter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsMangaLoading(true);
|
setIsMangaLoading(true);
|
||||||
@@ -175,21 +239,25 @@ export default function Reader() {
|
|||||||
formData.append('read', 'true');
|
formData.append('read', 'true');
|
||||||
client.patch(`/api/v1/manga/${manga.id}/chapter/${chapter.index}`, formData);
|
client.patch(`/api/v1/manga/${manga.id}/chapter/${chapter.index}`, formData);
|
||||||
|
|
||||||
history.replace({
|
openNextChapter(ChapterOffset.NEXT, (nextChapterIndex) =>
|
||||||
pathname: `/manga/${manga.id}/chapter/${chapter.index + 1}`,
|
history.replace({
|
||||||
state: history.location.state,
|
pathname: `/manga/${manga.id}/chapter/${nextChapterIndex}`,
|
||||||
});
|
state: history.location.state,
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [chapter.index, chapter.chapterCount, chapter.pageCount, manga.id]);
|
}, [chapter.index, chapter.chapterCount, chapter.pageCount, manga.id, settings.skipDupChapters]);
|
||||||
|
|
||||||
const prevChapter = useCallback(() => {
|
const prevChapter = useCallback(() => {
|
||||||
if (chapter.index > 1) {
|
if (chapter.index > 1) {
|
||||||
history.replace({
|
openNextChapter(ChapterOffset.PREV, (prevChapterIndex) =>
|
||||||
pathname: `/manga/${manga.id}/chapter/${chapter.index - 1}`,
|
history.replace({
|
||||||
state: history.location.state,
|
pathname: `/manga/${manga.id}/chapter/${prevChapterIndex}`,
|
||||||
});
|
state: history.location.state,
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [chapter.index, manga.id]);
|
}, [chapter.index, manga.id, settings.skipDupChapters]);
|
||||||
|
|
||||||
// return spinner while chpater data is loading
|
// return spinner while chpater data is loading
|
||||||
if (chapter.pageCount === -1) {
|
if (chapter.pageCount === -1) {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export default function DefaultReaderSettings() {
|
|||||||
staticNav={settings.staticNav}
|
staticNav={settings.staticNav}
|
||||||
showPageNumber={settings.showPageNumber}
|
showPageNumber={settings.showPageNumber}
|
||||||
loadNextOnEnding={settings.loadNextOnEnding}
|
loadNextOnEnding={settings.loadNextOnEnding}
|
||||||
|
skipDupChapters={settings.skipDupChapters}
|
||||||
readerType={settings.readerType}
|
readerType={settings.readerType}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -202,9 +202,15 @@ export interface IReaderSettings {
|
|||||||
staticNav: boolean;
|
staticNav: boolean;
|
||||||
showPageNumber: boolean;
|
showPageNumber: boolean;
|
||||||
loadNextOnEnding: boolean;
|
loadNextOnEnding: boolean;
|
||||||
|
skipDupChapters: boolean;
|
||||||
readerType: ReaderType;
|
readerType: ReaderType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ChapterOffset {
|
||||||
|
PREV = -1,
|
||||||
|
NEXT = 1,
|
||||||
|
}
|
||||||
|
|
||||||
export interface ISearchSettings {
|
export interface ISearchSettings {
|
||||||
ignoreFilters: boolean;
|
ignoreFilters: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const getDefaultSettings = (forceUndefined: boolean = false) =>
|
|||||||
showPageNumber: forceUndefined ? undefined : true,
|
showPageNumber: forceUndefined ? undefined : true,
|
||||||
continuesPageGap: forceUndefined ? undefined : false,
|
continuesPageGap: forceUndefined ? undefined : false,
|
||||||
loadNextOnEnding: forceUndefined ? undefined : false,
|
loadNextOnEnding: forceUndefined ? undefined : false,
|
||||||
|
skipDupChapters: forceUndefined ? undefined : true,
|
||||||
readerType: forceUndefined ? undefined : 'ContinuesVertical',
|
readerType: forceUndefined ? undefined : 'ContinuesVertical',
|
||||||
} as IReaderSettings);
|
} as IReaderSettings);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user