+
{currentIndex === index && children}
);
diff --git a/src/components/util/Toast.tsx b/src/components/util/Toast.tsx
index e45eba6e..7036df2f 100644
--- a/src/components/util/Toast.tsx
+++ b/src/components/util/Toast.tsx
@@ -21,9 +21,9 @@ function Transition(props: SlideProps) {
return
;
}
-interface IToastProps{
- message: string
- severity: Severity
+interface IToastProps {
+ message: string;
+ severity: Severity;
}
export function Toast(props: IToastProps) {
@@ -61,15 +61,20 @@ export default function makeToast(message: string, severity: Severity) {
setTimeout(() => removeToast(container.id), 3500);
}
-export function makeToaster(
- [toasts, setToasts] : [React.ReactElement[],
- (arg0: React.ReactElement[]) => void],
-): [React.ReactElement[], ((message: string, severity: Severity) => void)] {
- return [toasts, (message: string, severity: Severity) => {
- setToasts([
]);
- }];
+export function makeToaster([toasts, setToasts]: [
+ React.ReactElement[],
+ (arg0: React.ReactElement[]) => void,
+]): [React.ReactElement[], (message: string, severity: Severity) => void] {
+ return [
+ toasts,
+ (message: string, severity: Severity) => {
+ setToasts([
+
,
+ ]);
+ },
+ ];
}
diff --git a/src/components/util/helpers.ts b/src/components/util/helpers.ts
index b0630d29..65c2c7cc 100644
--- a/src/components/util/helpers.ts
+++ b/src/components/util/helpers.ts
@@ -6,14 +6,14 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// eslint-disable-next-line import/prefer-default-export
-export const pluralize = (count: number, input: string | { one: string, many: string }) => {
+export const pluralize = (count: number, input: string | { one: string; many: string }) => {
if (typeof input === 'string') {
return `${input}${count === 1 ? '' : 's'}`;
}
return input[count === 1 ? 'one' : 'many'];
};
-export const interpolate = (count: number, input: { one: string, many: string }) => {
+export const interpolate = (count: number, input: { one: string; many: string }) => {
const text = count === 1 ? input.one : input.many;
return text.replaceAll('%count%', count.toString());
};
diff --git a/src/screens/DownloadQueue.tsx b/src/screens/DownloadQueue.tsx
index c5b8ef28..21d636e9 100644
--- a/src/screens/DownloadQueue.tsx
+++ b/src/screens/DownloadQueue.tsx
@@ -11,16 +11,12 @@ import DeleteIcon from '@mui/icons-material/Delete';
import DragHandle from '@mui/icons-material/DragHandle';
import PauseIcon from '@mui/icons-material/Pause';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
-import {
- Card, CardActionArea, Stack,
-} from '@mui/material';
+import { Card, CardActionArea, Stack } from '@mui/material';
import IconButton from '@mui/material/IconButton';
import NavbarContext from 'components/context/NavbarContext';
import EmptyView from 'components/util/EmptyView';
import React, { useContext, useEffect } from 'react';
-import {
- DragDropContext, Draggable, Droppable, DropResult,
-} from 'react-beautiful-dnd';
+import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd';
import client from 'util/client';
import Typography from '@mui/material/Typography';
@@ -56,8 +52,7 @@ const DownloadQueue: React.FC = () => {
}, []);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- const onDragEnd = (result: DropResult) => {
- };
+ const onDragEnd = (result: DropResult) => {};
if (queue.length === 0) {
return
;
@@ -65,14 +60,15 @@ const DownloadQueue: React.FC = () => {
const handleDelete = (chapter: IChapter) => {
// required to stop before deleting otherwise the download kept going. Server issue?
- client.get('/api/v1/downloads/stop')
- .then(() => Promise.all([
+ client.get('/api/v1/downloads/stop').then(() =>
+ Promise.all([
// remove from download queue
client.delete(`/api/v1/download/${chapter.mangaId}/chapter/${chapter.index}`),
// delete partial download, should be handle server side?
// bug: The folder and the last image downloaded are not deleted
client.delete(`/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}`),
- ]));
+ ]),
+ );
};
return (
@@ -101,22 +97,38 @@ const DownloadQueue: React.FC = () => {
>
-
+
{item.manga.title}
-
+
{item.chapter.name}
diff --git a/src/screens/Extensions.tsx b/src/screens/Extensions.tsx
index 163e147c..6ebcb05b 100644
--- a/src/screens/Extensions.tsx
+++ b/src/screens/Extensions.tsx
@@ -5,9 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-import React, {
- useContext, useEffect, useState, useMemo, useRef,
-} from 'react';
+import React, { useContext, useEffect, useState, useMemo, useRef } from 'react';
import { fromEvent } from 'file-selector';
import IconButton from '@mui/material/IconButton';
import AddIcon from '@mui/icons-material/Add';
@@ -30,7 +28,7 @@ const EXTENSIONS = 1;
const allLangs: string[] = [];
interface GroupedExtension {
- [key: string]: IExtension[]
+ [key: string]: IExtension[];
}
function groupExtensions(extensions: IExtension[]) {
@@ -39,7 +37,9 @@ function groupExtensions(extensions: IExtension[]) {
extensions.forEach((extension) => {
if (sortedExtenions[extension.lang] === undefined) {
sortedExtenions[extension.lang] = [];
- if (extension.lang !== 'all') { allLangs.push(extension.lang); }
+ if (extension.lang !== 'all') {
+ allLangs.push(extension.lang);
+ }
}
if (extension.installed) {
if (extension.hasUpdate) {
@@ -67,7 +67,10 @@ function groupExtensions(extensions: IExtension[]) {
export default function MangaExtensions() {
const inputRef = useRef(null);
const { setTitle, setAction } = useContext(NavbarContext);
- const [shownLangs, setShownLangs] = useLocalStorage('shownExtensionLangs', extensionDefaultLangs());
+ const [shownLangs, setShownLangs] = useLocalStorage(
+ 'shownExtensionLangs',
+ extensionDefaultLangs(),
+ );
const [showNsfw] = useLocalStorage('showNsfw', true);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
@@ -78,10 +81,7 @@ export default function MangaExtensions() {
setAction(
<>
- inputRef.current?.click()}
- size="large"
- >
+ inputRef.current?.click()} size="large">
('/api/v1/extension/list');
+ const {
+ data: allExtensions,
+ mutate,
+ loading,
+ } = useQuery('/api/v1/extension/list');
- const filteredExtensions = useMemo(() => (allExtensions ?? []).filter((ext) => {
- const nsfwFilter = showNsfw || !ext.isNsfw;
- if (!query) return nsfwFilter;
- return nsfwFilter && ext.name.toLowerCase().includes(query.toLowerCase());
- }), [allExtensions, showNsfw, query]);
+ const filteredExtensions = useMemo(
+ () =>
+ (allExtensions ?? []).filter((ext) => {
+ const nsfwFilter = showNsfw || !ext.isNsfw;
+ if (!query) return nsfwFilter;
+ return nsfwFilter && ext.name.toLowerCase().includes(query.toLowerCase());
+ }),
+ [allExtensions, showNsfw, query],
+ );
- const groupedExtensions = useMemo(() => groupExtensions(filteredExtensions)
- .filter((group) => group[EXTENSIONS].length > 0)
- .filter((group) => ['installed', 'updates pending', 'all', ...shownLangs].includes(group[LANGUAGE])), [shownLangs, filteredExtensions]);
+ const groupedExtensions = useMemo(
+ () =>
+ groupExtensions(filteredExtensions)
+ .filter((group) => group[EXTENSIONS].length > 0)
+ .filter((group) =>
+ ['installed', 'updates pending', 'all', ...shownLangs].includes(
+ group[LANGUAGE],
+ ),
+ ),
+ [shownLangs, filteredExtensions],
+ );
const flatRenderItems: (IExtension | string)[] = groupedExtensions.flat(2);
@@ -119,8 +135,10 @@ export default function MangaExtensions() {
}
makeToast('Installing Extension File....', 'info');
- client.post('/api/v1/extension/install',
- formData, { headers: { 'Content-Type': 'multipart/form-data' } })
+ client
+ .post('/api/v1/extension/install', formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ })
.then(() => {
makeToast('Installed extension successfully!', 'success');
mutate();
@@ -175,7 +193,7 @@ export default function MangaExtensions() {
}}
totalCount={flatRenderItems.length}
itemContent={(index) => {
- if (typeof (flatRenderItems[index]) === 'string') {
+ if (typeof flatRenderItems[index] === 'string') {
const item = flatRenderItems[index] as string;
return (
;
+ return (
+
+ );
}
if (loading) {
@@ -109,11 +112,13 @@ export default function Library() {
{tabs.map((tab) => (
- {tab === activeTab && (mangaError
- ? (
-
- )
- : (
+ {tab === activeTab &&
+ (mangaError ? (
+
+ ) : (
{
const autofetchedRef = useRef(false);
const {
- data: manga, error, loading, isValidating, mutate,
+ data: manga,
+ error,
+ loading,
+ isValidating,
+ mutate,
} = useQuery(`/api/v1/manga/${id}/?onlineFetch=false`);
const [refresh, { loading: refreshing }] = useRefreshManga(id);
- useSetDefaultBackTo(manga?.inLibrary === false && manga.sourceId != null ? `/sources/${manga.sourceId}/popular` : '/library');
+ useSetDefaultBackTo(
+ manga?.inLibrary === false && manga.sourceId != null
+ ? `/sources/${manga.sourceId}/popular`
+ : '/library',
+ );
useEffect(() => {
// Automatically fetch manga from source if data is older then 24 hours
@@ -45,9 +49,9 @@ const Manga: React.FC = () => {
// not update age for some reason (ie. error on source side)
if (manga == null) return;
if (
- manga.inLibrary
- && (manga.age > AUTOFETCH_AGE || manga.chaptersAge > AUTOFETCH_AGE)
- && autofetchedRef.current === false
+ manga.inLibrary &&
+ (manga.age > AUTOFETCH_AGE || manga.chaptersAge > AUTOFETCH_AGE) &&
+ autofetchedRef.current === false
) {
autofetchedRef.current = true;
refresh();
@@ -59,29 +63,28 @@ const Manga: React.FC = () => {
}, [manga?.title]);
if (error && !manga) {
- return (
-
- );
+ return ;
}
return (
{error && !isValidating && !refreshing && (
-
- Could not fetch manga data
-
- {error.message ?? error}
- >
- )}
+
+ Could not fetch manga data
+
+ {error.message ?? error}
+ >
+ }
>
mutate()}>
)}
- {(manga && (refreshing || isValidating)) && (
+ {manga && (refreshing || isValidating) && (
diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx
index d04ed3cb..3a41e0cc 100644
--- a/src/screens/Reader.tsx
+++ b/src/screens/Reader.tsx
@@ -6,9 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import CircularProgress from '@mui/material/CircularProgress';
-import React, {
- useCallback, useContext, useEffect, useState,
-} from 'react';
+import React, { useCallback, useContext, useEffect, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom';
import HorizontalPager from 'components/reader/pager/HorizontalPager';
import PageNumber from 'components/reader/PageNumber';
@@ -53,7 +51,7 @@ const getReaderComponent = (readerType: ReaderType) => {
}
};
-const range = (n:number) => Array.from({ length: n }, (value, key) => key);
+const range = (n: number) => Array.from({ length: n }, (value, key) => key);
const initialChapter = () => ({
pageCount: -1,
index: -1,
@@ -67,22 +65,26 @@ export default function Reader() {
const [serverAddress] = useLocalStorage('serverBaseURL', '');
- const { chapterIndex, mangaId } = useParams<{ chapterIndex: string, mangaId: string }>();
- const [manga, setManga] = useState({ id: +mangaId, title: '', thumbnailUrl: '' });
+ const { chapterIndex, mangaId } = useParams<{ chapterIndex: string; mangaId: string }>();
+ const [manga, setManga] = useState({
+ id: +mangaId,
+ title: '',
+ thumbnailUrl: '',
+ });
const [chapter, setChapter] = useState(initialChapter());
const [curPage, setCurPage] = useState(0);
const { setOverride, setTitle } = useContext(NavbarContext);
- const {
- settings: defaultSettings,
- loading: areDefaultSettingsLoading,
- } = useDefaultReaderSettings();
+ const { settings: defaultSettings, loading: areDefaultSettingsLoading } =
+ useDefaultReaderSettings();
const [settings, setSettings] = useState(getReaderSettingsFor(manga, defaultSettings));
const [isMangaLoading, setIsMangaLoading] = useState(true);
const setSettingValue = (key: keyof IReaderSettings, value: string | boolean) => {
setSettings({ ...settings, [key]: value });
- requestUpdateMangaMetadata(manga, [[key, value]]).catch(() => makeToast('Failed to save the reader settings to the server', 'warning'));
+ requestUpdateMangaMetadata(manga, [[key, value]]).catch(() =>
+ makeToast('Failed to save the reader settings to the server', 'warning'),
+ );
};
useEffect(() => {
@@ -95,27 +97,27 @@ export default function Reader() {
useEffect(() => {
if (!areDefaultSettingsLoading && !isMangaLoading) {
- checkAndHandleMissingStoredReaderSettings(manga, 'manga', defaultSettings).catch(() => {});
+ checkAndHandleMissingStoredReaderSettings(manga, 'manga', defaultSettings).catch(
+ () => {},
+ );
setSettings(getReaderSettingsFor(manga, defaultSettings));
}
}, [areDefaultSettingsLoading, isMangaLoading]);
useEffect(() => {
// set the custom navbar
- setOverride(
- {
- status: true,
- value: (
-
- ),
- },
- );
+ setOverride({
+ status: true,
+ value: (
+
+ ),
+ });
// clean up for when we leave the reader
return () => setOverride({ status: false, value: });
@@ -123,7 +125,8 @@ export default function Reader() {
useEffect(() => {
setIsMangaLoading(true);
- client.get(`/api/v1/manga/${mangaId}/`)
+ client
+ .get(`/api/v1/manga/${mangaId}/`)
.then((response) => response.data)
.then((data: IManga) => {
setManga(data);
@@ -133,9 +136,10 @@ export default function Reader() {
useEffect(() => {
setChapter(initialChapter);
- client.get(`/api/v1/manga/${mangaId}/chapter/${chapterIndex}`)
+ client
+ .get(`/api/v1/manga/${mangaId}/chapter/${chapterIndex}`)
.then((response) => response.data)
- .then((data:IChapter) => {
+ .then((data: IChapter) => {
setChapter(data);
if (data.lastPageRead === data.pageCount - 1) {
@@ -166,22 +170,32 @@ export default function Reader() {
formData.append('read', 'true');
client.patch(`/api/v1/manga/${manga.id}/chapter/${chapter.index}`, formData);
- history.replace({ pathname: `/manga/${manga.id}/chapter/${chapter.index + 1}`, state: history.location.state });
+ history.replace({
+ pathname: `/manga/${manga.id}/chapter/${chapter.index + 1}`,
+ state: history.location.state,
+ });
}
}, [chapter.index, chapter.chapterCount, chapter.pageCount, manga.id]);
const prevChapter = useCallback(() => {
if (chapter.index > 1) {
- history.replace({ pathname: `/manga/${manga.id}/chapter/${chapter.index - 1}`, state: history.location.state });
+ history.replace({
+ pathname: `/manga/${manga.id}/chapter/${chapter.index - 1}`,
+ state: history.location.state,
+ });
}
}, [chapter.index, manga.id]);
// return spinner while chpater data is loading
if (chapter.pageCount === -1) {
return (
-
@@ -196,15 +210,11 @@ export default function Reader() {
const ReaderComponent = getReaderComponent(settings.readerType);
// last page, also probably read = true, we will load the first page.
- const initialPage = (chapter.lastPageRead === chapter.pageCount - 1) ? 0 : chapter.lastPageRead;
+ const initialPage = chapter.lastPageRead === chapter.pageCount - 1 ? 0 : chapter.lastPageRead;
return (
-
+
{
- if (result.indexOf(source.lang) === -1) { result.push(source.lang); }
+ if (result.indexOf(source.lang) === -1) {
+ result.push(source.lang);
+ }
});
result.sort(langSortCmp);
@@ -38,7 +43,10 @@ const SearchAll: React.FC = () => {
const [triggerUpdate, setTriggerUpdate] = useState(2);
const [mangas, setMangas] = useState({});
- const [shownLangs, setShownLangs] = useLocalStorage('shownSourceLangs', sourceDefualtLangs());
+ const [shownLangs, setShownLangs] = useLocalStorage(
+ 'shownSourceLangs',
+ sourceDefualtLangs(),
+ );
const [showNsfw] = useLocalStorage('showNsfw', true);
const [sources, setSources] = useState([]);
@@ -56,35 +64,46 @@ const SearchAll: React.FC = () => {
setAction(
<>
- >
- ,
+ >,
);
}, []);
useEffect(() => {
- client.get('/api/v1/source/list')
+ client
+ .get('/api/v1/source/list')
.then((response) => response.data)
.then((data) => {
- setSources(data.sort((a: { displayName: string; }, b: { displayName: string; }) => {
- if (a.displayName < b.displayName) { return -1; }
- if (a.displayName > b.displayName) { return 1; }
- return 0;
- })); setFetchedSources(true);
+ setSources(
+ data.sort((a: { displayName: string }, b: { displayName: string }) => {
+ if (a.displayName < b.displayName) {
+ return -1;
+ }
+ if (a.displayName > b.displayName) {
+ return 1;
+ }
+ return 0;
+ }),
+ );
+ setFetchedSources(true);
});
}, []);
async function doIT(elem: any[]) {
- elem.map((ele) => limit.add(async () => {
- const response = await client.get(`/api/v1/source/${ele.id}/search?searchTerm=${query || ''}&pageNum=1`);
- const data = await response.data;
- const tmp = mangas;
- tmp[ele.id] = data.mangaList;
- setMangas(tmp);
- const tmp2 = fetched;
- tmp2[ele.id] = true;
- setFetched(tmp2);
- setResetUI(1);
- }));
+ elem.map((ele) =>
+ limit.add(async () => {
+ const response = await client.get(
+ `/api/v1/source/${ele.id}/search?searchTerm=${query || ''}&pageNum=1`,
+ );
+ const data = await response.data;
+ const tmp = mangas;
+ tmp[ele.id] = data.mangaList;
+ setMangas(tmp);
+ const tmp2 = fetched;
+ tmp2[ele.id] = true;
+ setFetched(tmp2);
+ setResetUI(1);
+ }),
+ );
}
useEffect(() => {
@@ -98,7 +117,11 @@ const SearchAll: React.FC = () => {
setFetched({});
setMangas({});
// eslint-disable-next-line max-len
- doIT(sources.filter(({ lang }) => shownLangs.indexOf(lang) !== -1).filter((source) => showNsfw || !source.isNsfw));
+ doIT(
+ sources
+ .filter(({ lang }) => shownLangs.indexOf(lang) !== -1)
+ .filter((source) => showNsfw || !source.isNsfw),
+ );
}, [triggerUpdate]);
useEffect(() => {
@@ -137,9 +160,7 @@ const SearchAll: React.FC = () => {
setTitle('Sources');
setAction(
<>
-
+
{
return (
<>
{/* eslint-disable-next-line max-len */}
- {sources.filter(({ lang }) => shownLangs.indexOf(lang) !== -1).filter((source) => showNsfw || !source.isNsfw).sort((a, b) => {
- const af = fetched[a.id];
- const bf = fetched[b.id];
- if (af && !bf) { return -1; }
- if (!af && bf) { return 1; }
- if (!af && !bf) { return 0; }
+ {sources
+ .filter(({ lang }) => shownLangs.indexOf(lang) !== -1)
+ .filter((source) => showNsfw || !source.isNsfw)
+ .sort((a, b) => {
+ const af = fetched[a.id];
+ const bf = fetched[b.id];
+ if (af && !bf) {
+ return -1;
+ }
+ if (!af && bf) {
+ return 1;
+ }
+ if (!af && !bf) {
+ return 0;
+ }
- const al = mangas[a.id].length === 0;
- const bl = mangas[b.id].length === 0;
- if (al && !bl) { return 1; }
- if (bl && !al) { return -1; }
- return 0;
- }).map(({ lang, id, displayName }) => (
- (
+ const al = mangas[a.id].length === 0;
+ const bl = mangas[b.id].length === 0;
+ if (al && !bl) {
+ return 1;
+ }
+ if (bl && !al) {
+ return -1;
+ }
+ return 0;
+ })
+ .map(({ lang, id, displayName }) => (
<>
{
to={`/sources/${id}/popular/?R&query=${query}`}
sx={{ p: 3 }}
>
-
- {displayName}
-
+ {displayName}
{langCodeToName(lang)}
@@ -195,13 +227,11 @@ const SearchAll: React.FC = () => {
inLibraryIndicator
/>
>
- )
- ))}
-
+ ))}
>
);
}
- return (<>>);
+ return <>>;
};
export default SearchAll;
diff --git a/src/screens/Settings.tsx b/src/screens/Settings.tsx
index d1a29b41..65486cc3 100644
--- a/src/screens/Settings.tsx
+++ b/src/screens/Settings.tsx
@@ -38,7 +38,10 @@ import ListItemLink from 'components/util/ListItemLink';
export default function Settings() {
const { setTitle, setAction } = useContext(NavbarContext);
- useEffect(() => { setTitle('Settings'); setAction(<>>); }, []);
+ useEffect(() => {
+ setTitle('Settings');
+ setAction(<>>);
+ }, []);
const { darkTheme, setDarkTheme } = useContext(DarkTheme);
const [serverAddress, setServerAddress] = useLocalStorage('serverBaseURL', '');
@@ -193,9 +196,7 @@ export default function Settings() {
)}
- >
- ,
+ >,
);
return () => {
@@ -152,8 +159,12 @@ export default function SourceMangas(props: { popular: boolean }) {
useEffect(() => {
if (query) {
setSearch(true);
- } else { setSearch(false); }
- if (Noreset === undefined) { setInit(null); }
+ } else {
+ setSearch(false);
+ }
+ if (Noreset === undefined) {
+ setInit(null);
+ }
}, [query]);
useEffect(() => {
@@ -163,16 +174,27 @@ export default function SourceMangas(props: { popular: boolean }) {
}, 1000);
return () => clearTimeout(delayDebounceFn);
}
- if (Search !== undefined) { setInit(null); }
+ if (Search !== undefined) {
+ setInit(null);
+ }
return () => {};
}, [Search, query]);
useEffect(() => {
if (lastPageNum !== 0) {
const sourceType = props.popular ? 'popular' : 'latest';
- client.get(`/api/v1/source/${sourceId}/${query !== undefined || Search || Noreset === null ? 'search' : sourceType}${query !== undefined || Search || Noreset === null ? `?searchTerm=${query || ''}&pageNum=${lastPageNum}` : `/${lastPageNum}`}`)
+ client
+ .get(
+ `/api/v1/source/${sourceId}/${
+ query !== undefined || Search || Noreset === null ? 'search' : sourceType
+ }${
+ query !== undefined || Search || Noreset === null
+ ? `?searchTerm=${query || ''}&pageNum=${lastPageNum}`
+ : `/${lastPageNum}`
+ }`,
+ )
.then((response) => response.data)
- .then((data: { mangaList: IManga[], hasNextPage: boolean }) => {
+ .then((data: { mangaList: IManga[]; hasNextPage: boolean }) => {
setMangas([
...mangas,
...data.mangaList.map((it) => ({
@@ -180,11 +202,14 @@ export default function SourceMangas(props: { popular: boolean }) {
thumbnailUrl: it.thumbnailUrl,
id: it.id,
inLibrary: it.inLibrary,
- }))]);
+ })),
+ ]);
setHasNextPage(data.hasNextPage);
setFetched(true);
});
- } else { setLastPageNum(1); }
+ } else {
+ setLastPageNum(1);
+ }
}, [lastPageNum]);
let message;
@@ -196,7 +221,9 @@ export default function SourceMangas(props: { popular: boolean }) {
messageExtra = (
<>
Check out
- Local source guide
+
+ Local source guide
+
>
);
}
diff --git a/src/screens/Sources.tsx b/src/screens/Sources.tsx
index 7bd40985..fd66c982 100644
--- a/src/screens/Sources.tsx
+++ b/src/screens/Sources.tsx
@@ -10,7 +10,10 @@ import LangSelect from 'components/navbar/action/LangSelect';
import SourceCard from 'components/SourceCard';
import NavbarContext from 'components/context/NavbarContext';
import {
- sourceDefualtLangs, sourceForcedDefaultLangs, langCodeToName, langSortCmp,
+ sourceDefualtLangs,
+ sourceForcedDefaultLangs,
+ langCodeToName,
+ langSortCmp,
} from 'util/language';
import useLocalStorage from 'util/useLocalStorage';
import LoadingPlaceholder from 'components/util/LoadingPlaceholder';
@@ -23,7 +26,9 @@ function sourceToLangList(sources: ISource[]) {
const result: string[] = [];
sources.forEach((source) => {
- if (result.indexOf(source.lang) === -1) { result.push(source.lang); }
+ if (result.indexOf(source.lang) === -1) {
+ result.push(source.lang);
+ }
});
result.sort(langSortCmp);
@@ -33,7 +38,9 @@ function sourceToLangList(sources: ISource[]) {
function groupByLang(sources: ISource[]) {
const result = {} as any;
sources.forEach((source) => {
- if (result[source.lang] === undefined) { result[source.lang] = [] as ISource[]; }
+ if (result[source.lang] === undefined) {
+ result[source.lang] = [] as ISource[];
+ }
result[source.lang].push(source);
});
@@ -43,7 +50,10 @@ function groupByLang(sources: ISource[]) {
export default function Sources() {
const { setTitle, setAction } = useContext(NavbarContext);
- const [shownLangs, setShownLangs] = useLocalStorage('shownSourceLangs', sourceDefualtLangs());
+ const [shownLangs, setShownLangs] = useLocalStorage(
+ 'shownSourceLangs',
+ sourceDefualtLangs(),
+ );
const [showNsfw] = useLocalStorage('showNsfw', true);
const { data: sources, loading } = useQuery('/api/v1/source/list');
@@ -70,10 +80,7 @@ export default function Sources() {
setTitle('Sources');
setAction(
<>
- history.push('/sources/all/search/')}
- size="large"
- >
+ history.push('/sources/all/search/')} size="large">
;
if (sources?.length === 0) {
- return (No sources found. Install Some Extensions first.
);
+ return No sources found. Install Some Extensions first.
;
}
return (
<>
{/* eslint-disable-next-line max-len */}
- {Object.entries(groupByLang(sources ?? [])).sort((a, b) => langSortCmp(a[0], b[0])).map(([lang, list]) => (
- shownLangs.indexOf(lang) !== -1 && (
-
- {langCodeToName(lang)}
- {(list as ISource[])
- .filter((source) => showNsfw || !source.isNsfw)
- .map((source) => (
-
- ))}
-
- )
- ))}
+ {Object.entries(groupByLang(sources ?? []))
+ .sort((a, b) => langSortCmp(a[0], b[0]))
+ .map(
+ ([lang, list]) =>
+ shownLangs.indexOf(lang) !== -1 && (
+
+
+ {langCodeToName(lang)}
+
+ {(list as ISource[])
+ .filter((source) => showNsfw || !source.isNsfw)
+ .map((source) => (
+
+ ))}
+
+ ),
+ )}
>
);
}
diff --git a/src/screens/Updates.tsx b/src/screens/Updates.tsx
index 06d9dd31..ff1583fd 100644
--- a/src/screens/Updates.tsx
+++ b/src/screens/Updates.tsx
@@ -17,9 +17,7 @@ import NavbarContext from 'components/context/NavbarContext';
import DownloadStateIndicator from 'components/molecules/DownloadStateIndicator';
import EmptyView from 'components/util/EmptyView';
import LoadingPlaceholder from 'components/util/LoadingPlaceholder';
-import React, {
- useContext, useEffect, useRef, useState,
-} from 'react';
+import React, { useContext, useEffect, useRef, useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import client from 'util/client';
import useLocalStorage from 'util/useLocalStorage';
@@ -30,10 +28,12 @@ function epochToDate(epoch: number) {
return date;
}
-function isTheSameDay(first:Date, second:Date) {
- return first.getDate() === second.getDate()
- && first.getMonth() === second.getMonth()
- && first.getFullYear() === second.getFullYear();
+function isTheSameDay(first: Date, second: Date) {
+ return (
+ first.getDate() === second.getDate() &&
+ first.getMonth() === second.getMonth() &&
+ first.getFullYear() === second.getFullYear()
+ );
}
function getDateString(date: Date) {
@@ -46,8 +46,9 @@ function getDateString(date: Date) {
return date.toLocaleDateString();
}
-function groupByDate(updates: IMangaChapter[]):
-[string, { item: IMangaChapter, globalIdx: number }[] ][] {
+function groupByDate(
+ updates: IMangaChapter[],
+): [string, { item: IMangaChapter; globalIdx: number }[]][] {
if (updates.length === 0) return [];
const groups = {};
@@ -63,7 +64,10 @@ function groupByDate(updates: IMangaChapter[]):
return Object.keys(groups).map((key) => [key, groups[key]]);
}
-const baseWebsocketUrl = JSON.parse(window.localStorage.getItem('serverBaseURL')!).replace('http', 'ws');
+const baseWebsocketUrl = JSON.parse(window.localStorage.getItem('serverBaseURL')!).replace(
+ 'http',
+ 'ws',
+);
const initialQueue = {
status: 'Stopped',
queue: [],
@@ -104,13 +108,11 @@ const Updates: React.FC = () => {
useEffect(() => {
if (hasNextPage) {
- client.get(`/api/v1/update/recentChapters/${lastPageNum}`)
+ client
+ .get(`/api/v1/update/recentChapters/${lastPageNum}`)
.then((response) => response.data)
.then(({ hasNextPage: fetchedHasNextPage, page }: PaginatedList) => {
- setUpdateEntries([
- ...updateEntries,
- ...page,
- ]);
+ setUpdateEntries([...updateEntries, ...page]);
setHasNextPage(fetchedHasNextPage);
setFetched(true);
});
@@ -122,7 +124,7 @@ const Updates: React.FC = () => {
const scrollHandler = () => {
if (lastEntry.current) {
const rect = lastEntry.current.getBoundingClientRect();
- if (((rect.y + rect.height) / window.innerHeight < 2) && hasNextPage) {
+ if ((rect.y + rect.height) / window.innerHeight < 2 && hasNextPage) {
setLastPageNum(lastPageNum + 1);
}
}
@@ -134,8 +136,12 @@ const Updates: React.FC = () => {
};
}, [hasNextPage, updateEntries]);
- if (!fetched) { return ; }
- if (fetched && updateEntries.length === 0) { return ; }
+ if (!fetched) {
+ return ;
+ }
+ if (fetched && updateEntries.length === 0) {
+ return ;
+ }
const downloadForChapter = (chapter: IChapter) => {
const { index, mangaId } = chapter;
@@ -170,7 +176,10 @@ const Updates: React.FC = () => {
>
{
{manga.title}
-
+
{chapter.name}
diff --git a/src/screens/settings/About.tsx b/src/screens/settings/About.tsx
index c75a379f..9e590984 100644
--- a/src/screens/settings/About.tsx
+++ b/src/screens/settings/About.tsx
@@ -10,7 +10,10 @@ import { useQuery } from 'util/client';
export default function About() {
const { setTitle, setAction } = useContext(NavbarContext);
- useEffect(() => { setTitle('About'); setAction(<>>); }, []);
+ useEffect(() => {
+ setTitle('About');
+ setAction(<>>);
+ }, []);
const { data: about } = useQuery('/api/v1/settings/about');
diff --git a/src/screens/settings/Backup.tsx b/src/screens/settings/Backup.tsx
index e02c4532..d8213e4c 100644
--- a/src/screens/settings/Backup.tsx
+++ b/src/screens/settings/Backup.tsx
@@ -17,7 +17,10 @@ import NavbarContext from 'components/context/NavbarContext';
export default function Backup() {
const { setTitle, setAction } = useContext(NavbarContext);
- useEffect(() => { setTitle('Backup'); setAction(<>>); }, []);
+ useEffect(() => {
+ setTitle('Backup');
+ setAction(<>>);
+ }, []);
const { baseURL } = client.defaults;
@@ -27,8 +30,10 @@ export default function Backup() {
formData.append('backup.proto.gz', file);
makeToast('Restoring backup....', 'info');
- client.post('/api/v1/backup/import/file',
- formData, { headers: { 'Content-Type': 'multipart/form-data' } })
+ client
+ .post('/api/v1/backup/import/file', formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ })
.then(() => makeToast('Backup restore finished!', 'success'))
.catch(() => makeToast('Backup restore failed!', 'error'));
} else if (file.name.toLowerCase().endsWith('json')) {
@@ -81,12 +86,7 @@ export default function Backup() {
/>
-
+
>
-
);
}
diff --git a/src/screens/settings/Categories.tsx b/src/screens/settings/Categories.tsx
index f38f9f17..c6e3a7ba 100644
--- a/src/screens/settings/Categories.tsx
+++ b/src/screens/settings/Categories.tsx
@@ -7,18 +7,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-import React, {
- useMemo, useState, useContext, useEffect,
-} from 'react';
+import React, { useMemo, useState, useContext, useEffect } from 'react';
+import { List, ListItem, ListItemText, ListItemIcon, IconButton } from '@mui/material';
import {
- List,
- ListItem,
- ListItemText,
- ListItemIcon,
- IconButton,
-} from '@mui/material';
-import {
- DragDropContext, Droppable, Draggable, DropResult, DraggingStyle, NotDraggingStyle,
+ DragDropContext,
+ Droppable,
+ Draggable,
+ DropResult,
+ DraggingStyle,
+ NotDraggingStyle,
} from 'react-beautiful-dnd';
import DragHandleIcon from '@mui/icons-material/DragHandle';
import EditIcon from '@mui/icons-material/Edit';
@@ -37,8 +34,11 @@ import FormControlLabel from '@mui/material/FormControlLabel';
import NavbarContext from 'components/context/NavbarContext';
import client, { useQuery } from 'util/client';
-const getItemStyle = (isDragging: boolean,
- draggableStyle: DraggingStyle | NotDraggingStyle | undefined, palette: Palette) => ({
+const getItemStyle = (
+ isDragging: boolean,
+ draggableStyle: DraggingStyle | NotDraggingStyle | undefined,
+ palette: Palette,
+) => ({
// styles we need to apply on draggables
...draggableStyle,
@@ -49,11 +49,14 @@ const getItemStyle = (isDragging: boolean,
export default function Categories() {
const { setTitle, setAction } = useContext(NavbarContext);
- useEffect(() => { setTitle('Categories'); setAction(<>>); }, []);
+ useEffect(() => {
+ setTitle('Categories');
+ setAction(<>>);
+ }, []);
const { data, mutate } = useQuery('/api/v1/category/');
const categories = useMemo(() => {
- const res = [...data ?? []];
+ const res = [...(data ?? [])];
if (res.length > 0 && res[0].name === 'Default') {
res.shift();
}
@@ -84,11 +87,7 @@ export default function Categories() {
return;
}
- categoryReorder(
- categories,
- result.source.index,
- result.destination.index,
- );
+ categoryReorder(categories, result.source.index, result.destination.index);
};
const resetDialog = () => {
@@ -102,7 +101,7 @@ export default function Categories() {
setDialogOpen(true);
};
- const handleEditDialogOpen = (index:number) => {
+ const handleEditDialogOpen = (index: number) => {
setDialogName(categories[index].name);
setDialogDefault(categories[index].default);
setCategoryToEdit(index);
@@ -121,19 +120,16 @@ export default function Categories() {
formData.append('default', dialogDefault.toString());
if (categoryToEdit === -1) {
- client.post('/api/v1/category/', formData)
- .finally(() => mutate());
+ client.post('/api/v1/category/', formData).finally(() => mutate());
} else {
const category = categories[categoryToEdit];
- client.patch(`/api/v1/category/${category.id}`, formData)
- .finally(() => mutate());
+ client.patch(`/api/v1/category/${category.id}`, formData).finally(() => mutate());
}
};
- const deleteCategory = (index:number) => {
+ const deleteCategory = (index: number) => {
const category = categories[index];
- client.delete(`/api/v1/category/${category.id}`)
- .finally(() => mutate());
+ client.delete(`/api/v1/category/${category.id}`).finally(() => mutate());
};
return (
@@ -163,9 +159,7 @@ export default function Categories() {
-
+
{
handleEditDialogOpen(index);
@@ -219,13 +213,13 @@ export default function Categories() {
onChange={(e) => setDialogName(e.target.value)}
/>
setDialogDefault(e.target.checked)}
color="default"
/>
- )}
+ }
label="Default category when adding new manga to library"
/>
diff --git a/src/screens/settings/DefaultReaderSettings.tsx b/src/screens/settings/DefaultReaderSettings.tsx
index 4664c41d..bcea0a0d 100644
--- a/src/screens/settings/DefaultReaderSettings.tsx
+++ b/src/screens/settings/DefaultReaderSettings.tsx
@@ -31,22 +31,31 @@ export default function DefaultReaderSettings() {
const { metadata, settings, loading } = useDefaultReaderSettings();
const setSettingValue = (key: keyof IReaderSettings, value: string | boolean) => {
- requestUpdateServerMetadata(metadata ?? {}, [[key, value]]).catch(() => makeToast('Failed to save the default reader settings to the server', 'warning'));
+ requestUpdateServerMetadata(metadata ?? {}, [[key, value]]).catch(() =>
+ makeToast('Failed to save the default reader settings to the server', 'warning'),
+ );
};
if (loading) {
return (
-
);
}
- checkAndHandleMissingStoredReaderSettings({ meta: metadata }, 'server', getDefaultSettings())
- .catch(() => {});
+ checkAndHandleMissingStoredReaderSettings(
+ { meta: metadata },
+ 'server',
+ getDefaultSettings(),
+ ).catch(() => {});
return (
{
@@ -26,16 +25,17 @@ const createTheme = (dark?: boolean) => {
},
});
- const tachideskTheme = createMuiTheme({
- palette: {
- custom: {
- main: dark ? baseTheme.palette.common.black : baseTheme.palette.common.white,
- light: dark ? baseTheme.palette.grey[900] : baseTheme.palette.grey[100],
+ const tachideskTheme = createMuiTheme(
+ {
+ palette: {
+ custom: {
+ main: dark ? baseTheme.palette.common.black : baseTheme.palette.common.white,
+ light: dark ? baseTheme.palette.grey[900] : baseTheme.palette.grey[100],
+ },
},
- },
- components: {
- MuiCssBaseline: {
- styleOverrides: `
+ components: {
+ MuiCssBaseline: {
+ styleOverrides: `
*::-webkit-scrollbar {
width: 10px;
background: ${dark ? '#222' : '#e1e1e1'};
@@ -46,9 +46,11 @@ const createTheme = (dark?: boolean) => {
border-radius: 5px;
}
`,
+ },
},
},
- }, baseTheme);
+ baseTheme,
+ );
return tachideskTheme;
};
diff --git a/src/typings.d.ts b/src/typings.d.ts
index ed1871eb..442560bd 100644
--- a/src/typings.d.ts
+++ b/src/typings.d.ts
@@ -6,57 +6,57 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
interface IExtension {
- name: string
- pkgName: string
- versionName: string
- versionCode: number
- lang: string
- isNsfw: boolean
- apkName: string
- iconUrl: string
- installed: boolean
- hasUpdate: boolean
- obsolete: boolean
+ name: string;
+ pkgName: string;
+ versionName: string;
+ versionCode: number;
+ lang: string;
+ isNsfw: boolean;
+ apkName: string;
+ iconUrl: string;
+ installed: boolean;
+ hasUpdate: boolean;
+ obsolete: boolean;
}
interface ISource {
- id: string
- name: string
- lang: string
- iconUrl: string
- supportsLatest: boolean
- isConfigurable: boolean
- isNsfw: boolean
- displayName: string
+ id: string;
+ name: string;
+ lang: string;
+ iconUrl: string;
+ supportsLatest: boolean;
+ isConfigurable: boolean;
+ isNsfw: boolean;
+ displayName: string;
}
interface ISourceFilters {
- type: string
- filter: ISourceFilter
+ type: string;
+ filter: ISourceFilter;
}
interface ISourceFilter {
- name: string
- state: number | string | boolean | ISourceFilters[] | IState
- values?: string[]
- displayValues?: string[]
- selected?: ISelected
+ name: string;
+ state: number | string | boolean | ISourceFilters[] | IState;
+ values?: string[];
+ displayValues?: string[];
+ selected?: ISelected;
}
interface ISelected {
- displayname: string
- value: string
- _value: string
+ displayname: string;
+ value: string;
+ _value: string;
}
interface IState {
- ascending: boolean
- index: number
+ ascending: boolean;
+ index: number;
}
interface IMetadataMigration {
- appKeyPrefix?: { oldPrefix: string, newPrefix: string }
- keys?: { oldKey: string, newKey: string }[]
+ appKeyPrefix?: { oldPrefix: string; newPrefix: string };
+ keys?: { oldKey: string; newKey: string }[];
}
interface IMetadata {
@@ -64,7 +64,7 @@ interface IMetadata {
}
interface IMetadataHolder {
- meta?: IMetadata
+ meta?: IMetadata;
}
type AllowedMetadataValueTypes = string | boolean | number | undefined;
@@ -76,211 +76,211 @@ type AppMetadataKeys = MangaMetadataKeys;
type MetadataKeyValuePair = [AppMetadataKeys, AllowedMetadataValueTypes];
interface IMangaCard {
- id: number
- title: string
- thumbnailUrl: string
- unreadCount?: number
- downloadCount?: number
- inLibrary?: boolean
- meta?: IMetadata
+ id: number;
+ title: string;
+ thumbnailUrl: string;
+ unreadCount?: number;
+ downloadCount?: number;
+ inLibrary?: boolean;
+ meta?: IMetadata;
}
interface IManga {
- id: number
- sourceId: string
+ id: number;
+ sourceId: string;
- url: string
- title: string
- thumbnailUrl: string
+ url: string;
+ title: string;
+ thumbnailUrl: string;
- artist: string
- author: string
- description: string
- genre: string[]
- status: string
+ artist: string;
+ author: string;
+ description: string;
+ genre: string[];
+ status: string;
- inLibrary: boolean
- source: ISource
+ inLibrary: boolean;
+ source: ISource;
- meta: IMetadata
+ meta: IMetadata;
- realUrl: string
- freshData: boolean
- unreadCount?: number
- downloadCount?: number
+ realUrl: string;
+ freshData: boolean;
+ unreadCount?: number;
+ downloadCount?: number;
- age: number
- chaptersAge: number
+ age: number;
+ chaptersAge: number;
}
interface IChapter {
- id: number
- url: string
- name: string
- uploadDate: number
- chapterNumber: number
- scanlator: string
- mangaId: number
- read: boolean
- bookmarked: boolean
- lastPageRead: number
- lastReadAt: number
- index: number
- fetchedAt: number
- chapterCount: number
- pageCount: number
- downloaded: boolean
- meta: IAppMetadata
+ id: number;
+ url: string;
+ name: string;
+ uploadDate: number;
+ chapterNumber: number;
+ scanlator: string;
+ mangaId: number;
+ read: boolean;
+ bookmarked: boolean;
+ lastPageRead: number;
+ lastReadAt: number;
+ index: number;
+ fetchedAt: number;
+ chapterCount: number;
+ pageCount: number;
+ downloaded: boolean;
+ meta: IAppMetadata;
}
interface IMangaChapter {
- manga: IManga
- chapter: IChapter
+ manga: IManga;
+ chapter: IChapter;
}
interface IPartialChapter {
- pageCount: number
- index: number
- chapterCount: number
- lastPageRead: number
+ pageCount: number;
+ index: number;
+ chapterCount: number;
+ lastPageRead: number;
}
interface ICategory {
- id: number
- order: number
- name: string
- default: boolean
- meta: IAppMetadata
+ id: number;
+ order: number;
+ name: string;
+ default: boolean;
+ meta: IAppMetadata;
}
interface INavbarOverride {
- status: boolean
- value: any
+ status: boolean;
+ value: any;
}
type ReaderType =
-'ContinuesVertical' |
-'Webtoon' |
-'SingleVertical' |
-'SingleRTL' |
-'SingleLTR' |
-'DoubleVertical' |
-'DoubleRTL' |
-'DoubleLTR' |
-'ContinuesHorizontalLTR' |
-'ContinuesHorizontalRTL';
+ | 'ContinuesVertical'
+ | 'Webtoon'
+ | 'SingleVertical'
+ | 'SingleRTL'
+ | 'SingleLTR'
+ | 'DoubleVertical'
+ | 'DoubleRTL'
+ | 'DoubleLTR'
+ | 'ContinuesHorizontalLTR'
+ | 'ContinuesHorizontalRTL';
-interface IReaderSettings{
- staticNav: boolean
- showPageNumber: boolean
- loadNextOnEnding: boolean
- readerType: ReaderType
+interface IReaderSettings {
+ staticNav: boolean;
+ showPageNumber: boolean;
+ loadNextOnEnding: boolean;
+ readerType: ReaderType;
}
interface IReaderPage {
- index: number
- src: string
+ index: number;
+ src: string;
}
interface IReaderProps {
- pages: Array
- pageCount: number
- setCurPage: React.Dispatch>
- curPage: number
- initialPage: number
- settings: IReaderSettings
- manga: IMangaCard | IManga
- chapter: IChapter | IPartialChapter
- nextChapter: () => void
- prevChapter: () => void
+ pages: Array;
+ pageCount: number;
+ setCurPage: React.Dispatch>;
+ curPage: number;
+ initialPage: number;
+ settings: IReaderSettings;
+ manga: IMangaCard | IManga;
+ chapter: IChapter | IPartialChapter;
+ nextChapter: () => void;
+ prevChapter: () => void;
}
interface IAbout {
- name: string
- version: string
- revision: string
- buildType: 'Stable' | 'Preview'
- buildTime: number
- github: string
- discord: string
+ name: string;
+ version: string;
+ revision: string;
+ buildType: 'Stable' | 'Preview';
+ buildTime: number;
+ github: string;
+ discord: string;
}
-interface IDownloadChapter{
- chapterIndex: number
- mangaId: number
- state: 'Queued' | 'Downloading' | 'Finished' | 'Error'
- progress: number
- chapter: IChapter
- manga: IManga
+interface IDownloadChapter {
+ chapterIndex: number;
+ mangaId: number;
+ state: 'Queued' | 'Downloading' | 'Finished' | 'Error';
+ progress: number;
+ chapter: IChapter;
+ manga: IManga;
}
interface IQueue {
- status: 'Stopped' | 'Started'
- queue: IDownloadChapter[]
+ status: 'Stopped' | 'Started';
+ queue: IDownloadChapter[];
}
interface IUpdateStatus {
- running: boolean
+ running: boolean;
statusMap: {
- COMPLETE?: IManga[],
- RUNNING?: IManga[],
- PENDING?: IManga[]
- }
+ COMPLETE?: IManga[];
+ RUNNING?: IManga[];
+ PENDING?: IManga[];
+ };
}
interface PreferenceProps {
- key: string
- title: string
- summary: string
- defaultValue: any
- currentValue: any
- defaultValueType: string
+ key: string;
+ title: string;
+ summary: string;
+ defaultValue: any;
+ currentValue: any;
+ defaultValueType: string;
// intetnal props
- updateValue: any
+ updateValue: any;
}
interface TwoStatePreferenceProps extends PreferenceProps {
-
// intetnal props
- type: 'Switch' | 'Checkbox'
+ type: 'Switch' | 'Checkbox';
}
+
interface CheckBoxPreferenceProps extends PreferenceProps {}
interface SwitchPreferenceCompatProps extends PreferenceProps {}
interface ListPreferenceProps extends PreferenceProps {
- entries: string[]
- entryValues: string[]
+ entries: string[];
+ entryValues: string[];
}
interface MultiSelectListPreferenceProps extends PreferenceProps {
- entries: string[]
- entryValues: string[]
+ entries: string[];
+ entryValues: string[];
}
interface EditTextPreferenceProps extends PreferenceProps {
- dialogTitle: string
- dialogMessage: string
- text: string
+ dialogTitle: string;
+ dialogMessage: string;
+ text: string;
}
interface SourcePreferences {
- type: string
- props: any
+ type: string;
+ props: any;
}
interface NavbarItem {
- path: string,
- title:string,
- SelectedIconComponent: OverridableComponent>,
- IconComponent: OverridableComponent>,
- show: 'mobile' | 'desktop' | 'both'
+ path: string;
+ title: string;
+ SelectedIconComponent: OverridableComponent>;
+ IconComponent: OverridableComponent>;
+ show: 'mobile' | 'desktop' | 'both';
}
interface PaginatedList {
- page: T[],
- hasNextPage: boolean
+ page: T[];
+ hasNextPage: boolean;
}
type NullAndUndefined = T | null | undefined;
@@ -288,20 +288,20 @@ type NullAndUndefined = T | null | undefined;
type ChapterSortMode = 'fetchedAt' | 'source';
interface ChapterListOptions {
- active: boolean
- unread: NullAndUndefined
- downloaded: NullAndUndefined
- bookmarked: NullAndUndefined
- reverse: boolean
- sortBy: ChapterSortMode
- showChapterNumber: boolean
+ active: boolean;
+ unread: NullAndUndefined;
+ downloaded: NullAndUndefined;
+ bookmarked: NullAndUndefined;
+ reverse: boolean;
+ sortBy: ChapterSortMode;
+ showChapterNumber: boolean;
}
type ChapterOptionsReducerAction =
-{ type: 'filter', filterType:string, filterValue: NullAndUndefined }
-| { type: 'sortBy', sortBy: ChapterSortMode }
-| { type: 'sortReverse' }
-| { type: 'showChapterNumber' };
+ | { type: 'filter'; filterType: string; filterValue: NullAndUndefined }
+ | { type: 'sortBy'; sortBy: ChapterSortMode }
+ | { type: 'sortReverse' }
+ | { type: 'showChapterNumber' };
type LibrarySortMode = 'sortToRead' | 'sortAlph' | 'sortID';
@@ -313,21 +313,21 @@ enum GridLayout {
interface LibraryOptions {
// display options
- showDownloadBadge: boolean
- showUnreadBadge: boolean
- gridLayout: GridLayout
- SourcegridLayout: GridLayout
+ showDownloadBadge: boolean;
+ showUnreadBadge: boolean;
+ gridLayout: GridLayout;
+ SourcegridLayout: GridLayout;
// filter options
- downloaded: NullAndUndefined
- unread: NullAndUndefined
- sorts: NullAndUndefined
- sortDesc: NullAndUndefined
+ downloaded: NullAndUndefined;
+ unread: NullAndUndefined;
+ sorts: NullAndUndefined;
+ sortDesc: NullAndUndefined;
}
interface BatchChaptersChange {
- delete?: boolean
- isRead?: boolean
- isBookmarked?: boolean
- lastPageRead?: number
+ delete?: boolean;
+ isRead?: boolean;
+ isBookmarked?: boolean;
+ lastPageRead?: number;
}
diff --git a/src/util/client.tsx b/src/util/client.tsx
index f6a2394c..bff62681 100644
--- a/src/util/client.tsx
+++ b/src/util/client.tsx
@@ -13,7 +13,11 @@ const { hostname, port, protocol } = window.location;
// if port is 3000 it's probably running from webpack devlopment server
let inferredPort;
-if (port === '3000') { inferredPort = '4567'; } else { inferredPort = port; }
+if (port === '3000') {
+ inferredPort = '4567';
+} else {
+ inferredPort = port;
+}
const baseURL = storage.getItem('serverBaseURL', `${protocol}//${hostname}:${inferredPort}`);
@@ -42,10 +46,7 @@ export async function fetcher(path: string) {
return res.data as T;
}
-export const useQuery = <
- D extends any = any,
- E extends any = any,
->(
+export const useQuery = (
key: string,
config?: SWRConfiguration,
): SWRResponse & { loading: boolean } => {
diff --git a/src/util/date.ts b/src/util/date.ts
index 4e631fe6..d19495df 100644
--- a/src/util/date.ts
+++ b/src/util/date.ts
@@ -47,13 +47,10 @@ export const getUploadDateString = (date: Date | number) => {
const addTimeString = wasUploadedToday || wasUploadedYesterday;
const timeString = addTimeString
- ? uploadDate.toLocaleTimeString(
- undefined,
- {
- hour: '2-digit',
- minute: '2-digit',
- },
- )
+ ? uploadDate.toLocaleTimeString(undefined, {
+ hour: '2-digit',
+ minute: '2-digit',
+ })
: '';
if (wasUploadedToday) {
diff --git a/src/util/language.tsx b/src/util/language.tsx
index fc85784c..92b4ab07 100644
--- a/src/util/language.tsx
+++ b/src/util/language.tsx
@@ -74,7 +74,6 @@ export const ISOLanguages = [
{ code: 'bs', name: 'Bosnian', nativeName: 'bosanski' },
{ code: 'sv', name: 'Swedish', nativeName: 'svenska' },
{ code: 'sv', name: 'Swedish', nativeName: 'svenska' },
-
];
export function langCodeToName(code: string): string {
@@ -84,8 +83,10 @@ export function langCodeToName(code: string): string {
let result = `language with code: ${code}`;
for (let i = 0; i < ISOLanguages.length; i++) {
- if (ISOLanguages[i].code === proccessedCode
- || ISOLanguages[i].code === code.toLocaleLowerCase()) {
+ if (
+ ISOLanguages[i].code === proccessedCode ||
+ ISOLanguages[i].code === code.toLocaleLowerCase()
+ ) {
result = ISOLanguages[i].nativeName;
}
}
@@ -98,23 +99,15 @@ function defaultNativeLang() {
}
export function extensionDefaultLangs() {
- return [
- defaultNativeLang(),
- 'all',
- ];
+ return [defaultNativeLang(), 'all'];
}
export function sourceDefualtLangs() {
- return [
- defaultNativeLang(),
- 'localsourcelang',
- ];
+ return [defaultNativeLang(), 'localsourcelang'];
}
export function sourceForcedDefaultLangs(): string[] {
- return [
- 'localsourcelang',
- ];
+ return ['localsourcelang'];
}
export const langSortCmp = (a: string, b: string) => {
diff --git a/src/util/localStorage.tsx b/src/util/localStorage.tsx
index 1861a5a0..9233e41b 100644
--- a/src/util/localStorage.tsx
+++ b/src/util/localStorage.tsx
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-function getItem(key: string, defaultValue: T) : T {
+function getItem(key: string, defaultValue: T): T {
try {
const item = window.localStorage.getItem(key);
@@ -16,7 +16,8 @@ function getItem(key: string, defaultValue: T) : T {
window.localStorage.setItem(key, JSON.stringify(defaultValue));
/* eslint-disable no-empty */
- } finally { }
+ } finally {
+ }
return defaultValue;
}
@@ -25,7 +26,8 @@ function setItem(key: string, value: T): void {
window.localStorage.setItem(key, JSON.stringify(value));
// eslint-disable-next-line no-empty
- } finally { }
+ } finally {
+ }
}
export default { getItem, setItem };
diff --git a/src/util/metadata.ts b/src/util/metadata.ts
index 2039a5a3..878845b8 100644
--- a/src/util/metadata.ts
+++ b/src/util/metadata.ts
@@ -13,13 +13,12 @@ const APP_METADATA_KEY_PREFIX = 'webUI_';
const migrations: IMetadataMigration[] = [
{
- keys: [
- { oldKey: 'loadNextonEnding', newKey: 'loadNextOnEnding' },
- ],
+ keys: [{ oldKey: 'loadNextonEnding', newKey: 'loadNextOnEnding' }],
},
];
-const getMetadataKey = (key: string, appPrefix: string = APP_METADATA_KEY_PREFIX) => `${appPrefix}${key}`;
+const getMetadataKey = (key: string, appPrefix: string = APP_METADATA_KEY_PREFIX) =>
+ `${appPrefix}${key}`;
const doesMetadataKeyExistIn = (
meta: IMetadata | undefined,
@@ -27,9 +26,7 @@ const doesMetadataKeyExistIn = (
appPrefix?: string,
): boolean => Object.prototype.hasOwnProperty.call(meta ?? {}, getMetadataKey(key, appPrefix));
-const convertValueFromMetadata = <
- T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes,
->(
+const convertValueFromMetadata = (
value: string,
): T => {
if (!Number.isNaN(+value)) {
@@ -74,8 +71,9 @@ const applyAppKeyPrefixMigration = (meta: IMetadata, migration: IMetadataMigrati
const oldAppMetadata = getAppMetadataFrom(meta, oldPrefix);
const newAppMetadata = getAppMetadataFrom(meta, newPrefix);
- const missingMetadataKeys = Object.keys(oldAppMetadata)
- .filter((key) => !Object.keys(newAppMetadata).includes(key));
+ const missingMetadataKeys = Object.keys(oldAppMetadata).filter(
+ (key) => !Object.keys(newAppMetadata).includes(key),
+ );
const isMissingOldMetadata = missingMetadataKeys.length;
if (isMissingOldMetadata) {
@@ -219,16 +217,25 @@ export const requestUpdateMetadata = async (
keysToValues: [AppMetadataKeys, AllowedMetadataValueTypes][],
endpointToMutate?: string,
wrapWithMetaKey?: boolean,
-): Promise => Promise.all(keysToValues.map(
- ([key, value]) => requestUpdateMetadataValue(
- endpoint, metadataHolder, key, value, endpointToMutate, wrapWithMetaKey,
- ),
-));
+): Promise =>
+ Promise.all(
+ keysToValues.map(([key, value]) =>
+ requestUpdateMetadataValue(
+ endpoint,
+ metadataHolder,
+ key,
+ value,
+ endpointToMutate,
+ wrapWithMetaKey,
+ ),
+ ),
+ );
export const requestUpdateServerMetadata = async (
serverMetadata: IMetadata,
keysToValues: MetadataKeyValuePair[],
-): Promise => requestUpdateMetadata('', { meta: serverMetadata }, keysToValues, '/meta', false);
+): Promise =>
+ requestUpdateMetadata('', { meta: serverMetadata }, keysToValues, '/meta', false);
export const requestUpdateMangaMetadata = async (
manga: IMangaCard | IManga,
@@ -238,7 +245,12 @@ export const requestUpdateMangaMetadata = async (
export const requestUpdateChapterMetadata = async (
mangaChapter: IMangaChapter,
keysToValues: MetadataKeyValuePair[],
-): Promise => requestUpdateMetadata(`/manga/${mangaChapter.manga.id}/chapter/${mangaChapter.chapter.index}`, mangaChapter.chapter, keysToValues);
+): Promise =>
+ requestUpdateMetadata(
+ `/manga/${mangaChapter.manga.id}/chapter/${mangaChapter.chapter.index}`,
+ mangaChapter.chapter,
+ keysToValues,
+ );
export const requestUpdateCategoryMetadata = async (
category: ICategory,
diff --git a/src/util/readerSettings.ts b/src/util/readerSettings.ts
index ee82cf92..9803731f 100644
--- a/src/util/readerSettings.ts
+++ b/src/util/readerSettings.ts
@@ -6,27 +6,32 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
-import { getMetadataFrom, requestUpdateMangaMetadata, requestUpdateServerMetadata } from 'util/metadata';
+import {
+ getMetadataFrom,
+ requestUpdateMangaMetadata,
+ requestUpdateServerMetadata,
+} from 'util/metadata';
import { useQuery } from 'util/client';
-export const getDefaultSettings = (forceUndefined: boolean = false) => ({
- staticNav: forceUndefined ? undefined : false,
- showPageNumber: forceUndefined ? undefined : true,
- continuesPageGap: forceUndefined ? undefined : false,
- loadNextOnEnding: forceUndefined ? undefined : false,
- readerType: forceUndefined ? undefined : 'ContinuesVertical',
-} as IReaderSettings);
+export const getDefaultSettings = (forceUndefined: boolean = false) =>
+ ({
+ staticNav: forceUndefined ? undefined : false,
+ showPageNumber: forceUndefined ? undefined : true,
+ continuesPageGap: forceUndefined ? undefined : false,
+ loadNextOnEnding: forceUndefined ? undefined : false,
+ readerType: forceUndefined ? undefined : 'ContinuesVertical',
+ } as IReaderSettings);
const getReaderSettingsWithDefaultValueFallback = (
meta?: IMetadata,
defaultSettings?: IReaderSettings,
applyMetadataMigration: boolean = true,
): IReaderSettings => ({
- ...getMetadataFrom(
+ ...(getMetadataFrom(
{ meta },
Object.entries(defaultSettings ?? getDefaultSettings()) as MetadataKeyValuePair[],
applyMetadataMigration,
- ) as unknown as IReaderSettings,
+ ) as unknown as IReaderSettings),
});
export const getReaderSettingsFromMetadata = (
@@ -44,9 +49,9 @@ export const getReaderSettingsFor = (
): IReaderSettings => getReaderSettingsFromMetadata(meta, defaultSettings, applyMetadataMigration);
export const useDefaultReaderSettings = (): {
- metadata?: IMetadata,
- settings: IReaderSettings,
- loading: boolean
+ metadata?: IMetadata;
+ settings: IReaderSettings;
+ loading: boolean;
} => {
const { data: meta, loading } = useQuery('/api/v1/meta');
const settings = getReaderSettingsWithDefaultValueFallback(meta);
@@ -66,12 +71,13 @@ export const checkAndHandleMissingStoredReaderSettings = async (
metadataHolderType: 'manga' | 'server',
defaultSettings: IReaderSettings,
): Promise => {
- const meta = metadataHolder.meta ?? metadataHolder as IMetadata;
+ const meta = metadataHolder.meta ?? (metadataHolder as IMetadata);
const settingsToCheck = getReaderSettingsFor({ meta }, getDefaultSettings(true), false);
const newSettings = getReaderSettingsFor({ meta }, defaultSettings);
- const undefinedSettings = Object.entries(settingsToCheck)
- .filter((setting) => setting[1] === undefined);
+ const undefinedSettings = Object.entries(settingsToCheck).filter(
+ (setting) => setting[1] === undefined,
+ );
const settingsToUpdate: MetadataKeyValuePair[] = [];
undefinedSettings.forEach((setting) => {
diff --git a/src/util/useBackTo.ts b/src/util/useBackTo.ts
index 6a382a5b..6abc9693 100644
--- a/src/util/useBackTo.ts
+++ b/src/util/useBackTo.ts
@@ -10,7 +10,7 @@ import { useLocation } from 'react-router-dom';
export const BACK = '__BACK__';
-const useBackTo = (): { url?: string, back: boolean } => {
+const useBackTo = (): { url?: string; back: boolean } => {
const location = useLocation<{ backLink?: string }>();
const { defaultBackTo } = useNavBarContext();
diff --git a/src/util/useLocalStorage.tsx b/src/util/useLocalStorage.tsx
index 5eff05ae..0d5659c5 100644
--- a/src/util/useLocalStorage.tsx
+++ b/src/util/useLocalStorage.tsx
@@ -5,14 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-import React, {
- useState,
- Dispatch,
- SetStateAction,
- useReducer,
- Reducer,
- useCallback,
-} from 'react';
+import React, { useState, Dispatch, SetStateAction, useReducer, Reducer, useCallback } from 'react';
import storage from 'util/localStorage';
// eslint-disable-next-line max-len
@@ -21,19 +14,18 @@ export default function useLocalStorage(
defaultValue: T | (() => T),
): [T, Dispatch>] {
const initialState = defaultValue instanceof Function ? defaultValue() : defaultValue;
- const [storedValue, setStoredValue] = useState(
- storage.getItem(key, initialState),
- );
+ const [storedValue, setStoredValue] = useState(storage.getItem(key, initialState));
const setValue = useCallback>>(
- ((value) => {
+ (value) => {
setStoredValue((prevValue) => {
// Allow value to be a function so we have same API as useState
const valueToStore = value instanceof Function ? value(prevValue) : value;
storage.setItem(key, valueToStore);
return valueToStore;
});
- }), [key],
+ },
+ [key],
);
return [storedValue, setValue];
diff --git a/yarn.lock b/yarn.lock
index a94700b2..7c7dd9bd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4906,6 +4906,11 @@ eslint-config-airbnb@18.2.1:
object.assign "^4.1.2"
object.entries "^1.1.2"
+eslint-config-prettier@^8.6.0:
+ version "8.6.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207"
+ integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==
+
eslint-config-react-app@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz#ccff9fc8e36b322902844cbd79197982be355a0e"
@@ -4987,6 +4992,13 @@ eslint-plugin-no-relative-import-paths@^1.5.2:
resolved "https://registry.yarnpkg.com/eslint-plugin-no-relative-import-paths/-/eslint-plugin-no-relative-import-paths-1.5.2.tgz#c35f2fd0bf2a6a57b268193ed7df63ff7000134e"
integrity sha512-wMlL+TVuDhKk1plP+w3L4Hc7+u89vUkrOYq6/0ARjcYqwc9/YaS9uEXNzaqAk+WLoEgakzNL5JgJJw6m4qd5zw==
+eslint-plugin-prettier@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"
+ integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==
+ dependencies:
+ prettier-linter-helpers "^1.0.0"
+
eslint-plugin-react-hooks@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04"
@@ -5344,6 +5356,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+fast-diff@^1.1.2:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
+ integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
+
fast-glob@^3.1.1:
version "3.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
@@ -9338,6 +9355,18 @@ prepend-http@^1.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
+prettier-linter-helpers@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+ dependencies:
+ fast-diff "^1.1.2"
+
+prettier@^2.8.2:
+ version "2.8.2"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.2.tgz#c4ea1b5b454d7c4b59966db2e06ed7eec5dfd160"
+ integrity sha512-BtRV9BcncDyI2tsuS19zzhzoxD8Dh8LiCx7j7tHzrkz8GFXAexeWFdi22mjE1d16dftH2qNaytVxqiRTGlMfpw==
+
pretty-bytes@^5.3.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"