increase prettier line length to 120 (#233)
* [Prettier] Increase max line length to 120 100 is quite low, especially on modern monitors. The length is also reached quite quickly with 4 spaces per tab depending on the indentation depth * [Prettier] Increase max line length to 120 - Fix formatting
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"printWidth": 120,
|
||||
"semi": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
|
||||
@@ -22,17 +22,7 @@ interface IProps {
|
||||
|
||||
export default function ExtensionCard(props: IProps) {
|
||||
const {
|
||||
extension: {
|
||||
name,
|
||||
lang,
|
||||
versionName,
|
||||
installed,
|
||||
hasUpdate,
|
||||
obsolete,
|
||||
pkgName,
|
||||
iconUrl,
|
||||
isNsfw,
|
||||
},
|
||||
extension: { name, lang, versionName, installed, hasUpdate, obsolete, pkgName, iconUrl, isNsfw },
|
||||
notifyInstall,
|
||||
} = props;
|
||||
const [installedState, setInstalledState] = useState<string>(() => {
|
||||
@@ -123,12 +113,7 @@ export default function ExtensionCard(props: IProps) {
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
{langPress} {versionName}
|
||||
{isNsfw && (
|
||||
<Typography
|
||||
variant="caption"
|
||||
display="inline"
|
||||
gutterBottom
|
||||
color="red"
|
||||
>
|
||||
<Typography variant="caption" display="inline" gutterBottom color="red">
|
||||
{' 18+'}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
@@ -100,10 +100,7 @@ const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref)
|
||||
const cols = Math.ceil(dimensions / ItemWidth);
|
||||
return (
|
||||
<Grid item columns={cols} xs={1}>
|
||||
<Link
|
||||
to={mangaLinkTo}
|
||||
style={gridLayout === GridLayout.Comfortable ? { textDecoration: 'none' } : {}}
|
||||
>
|
||||
<Link to={mangaLinkTo} style={gridLayout === GridLayout.Comfortable ? { textDecoration: 'none' } : {}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@@ -132,16 +129,12 @@ const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref)
|
||||
}}
|
||||
>
|
||||
{inLibraryIndicator && inLibrary && (
|
||||
<Typography
|
||||
sx={{ backgroundColor: 'primary.dark', zIndex: '1' }}
|
||||
>
|
||||
<Typography sx={{ backgroundColor: 'primary.dark', zIndex: '1' }}>
|
||||
In library
|
||||
</Typography>
|
||||
)}
|
||||
{showUnreadBadge && unread! > 0 && (
|
||||
<Typography sx={{ backgroundColor: 'primary.dark' }}>
|
||||
{unread}
|
||||
</Typography>
|
||||
<Typography sx={{ backgroundColor: 'primary.dark' }}>{unread}</Typography>
|
||||
)}
|
||||
{showDownloadBadge && downloadCount! > 0 && (
|
||||
<Typography
|
||||
@@ -266,14 +259,10 @@ const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref)
|
||||
</Box>
|
||||
<BadgeContainer>
|
||||
{inLibraryIndicator && inLibrary && (
|
||||
<Typography sx={{ backgroundColor: 'primary.dark' }}>
|
||||
In library
|
||||
</Typography>
|
||||
<Typography sx={{ backgroundColor: 'primary.dark' }}>In library</Typography>
|
||||
)}
|
||||
{showUnreadBadge && unread! > 0 && (
|
||||
<Typography sx={{ backgroundColor: 'primary.dark' }}>
|
||||
{unread}
|
||||
</Typography>
|
||||
<Typography sx={{ backgroundColor: 'primary.dark' }}>{unread}</Typography>
|
||||
)}
|
||||
{showDownloadBadge && downloadCount! > 0 && (
|
||||
<Typography
|
||||
|
||||
@@ -97,12 +97,7 @@ const SourceCard: React.FC<IProps> = (props: IProps) => {
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
{langCodeToName(lang)}
|
||||
{isNsfw && (
|
||||
<Typography
|
||||
variant="caption"
|
||||
display="inline"
|
||||
gutterBottom
|
||||
color="red"
|
||||
>
|
||||
<Typography variant="caption" display="inline" gutterBottom color="red">
|
||||
{' 18+'}
|
||||
</Typography>
|
||||
)}
|
||||
@@ -113,29 +108,18 @@ const SourceCard: React.FC<IProps> = (props: IProps) => {
|
||||
<>
|
||||
<MobileWidthButtons>
|
||||
{supportsLatest && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={(e) => redirectTo(e, `/sources/${id}/latest/`)}
|
||||
>
|
||||
<Button variant="outlined" onClick={(e) => redirectTo(e, `/sources/${id}/latest/`)}>
|
||||
Latest
|
||||
</Button>
|
||||
)}
|
||||
</MobileWidthButtons>
|
||||
<WiderWidthButtons>
|
||||
{supportsLatest && (
|
||||
<Button
|
||||
component={Link}
|
||||
to={`/sources/${id}/latest/`}
|
||||
variant="outlined"
|
||||
>
|
||||
<Button component={Link} to={`/sources/${id}/latest/`} variant="outlined">
|
||||
Latest
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
component={Link}
|
||||
to={`/sources/${id}/popular/`}
|
||||
variant="outlined"
|
||||
>
|
||||
<Button component={Link} to={`/sources/${id}/popular/`} variant="outlined">
|
||||
Browse
|
||||
</Button>
|
||||
</WiderWidthButtons>
|
||||
|
||||
@@ -17,9 +17,7 @@ interface IProps extends RadioInputProps {
|
||||
|
||||
const SortRadioInput: React.FC<IProps> = ({ sortDescending, ...rest }) => (
|
||||
<RadioInput
|
||||
checkedIcon={
|
||||
sortDescending ? <ArrowDownward color="primary" /> : <ArrowUpward color="primary" />
|
||||
}
|
||||
checkedIcon={sortDescending ? <ArrowDownward color="primary" /> : <ArrowUpward color="primary" />}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -24,10 +24,7 @@ const unreadFilter = (unread: NullAndUndefined<boolean>, { unreadCount }: IManga
|
||||
}
|
||||
};
|
||||
|
||||
const downloadedFilter = (
|
||||
downloaded: NullAndUndefined<boolean>,
|
||||
{ downloadCount }: IMangaCard,
|
||||
): boolean => {
|
||||
const downloadedFilter = (downloaded: NullAndUndefined<boolean>, { downloadCount }: IMangaCard): boolean => {
|
||||
switch (downloaded) {
|
||||
case true:
|
||||
return !!downloadCount && downloadCount >= 1;
|
||||
@@ -117,9 +114,7 @@ const LibraryMangaGrid: React.FC<IMangaGridProps & { lastLibraryUpdate: number }
|
||||
);
|
||||
|
||||
const showFilteredOutMessage =
|
||||
(unread != null || downloaded != null || query) &&
|
||||
filteredManga.length === 0 &&
|
||||
mangas.length > 0;
|
||||
(unread != null || downloaded != null || query) && filteredManga.length === 0 && mangas.length > 0;
|
||||
|
||||
return (
|
||||
<MangaGrid
|
||||
|
||||
@@ -34,10 +34,7 @@ interface IProps {
|
||||
const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
|
||||
const { options, setOptions } = useLibraryOptionsContext();
|
||||
|
||||
const handleFilterChange = <T extends keyof LibraryOptions>(
|
||||
key: T,
|
||||
value: LibraryOptions[T],
|
||||
) => {
|
||||
const handleFilterChange = <T extends keyof LibraryOptions>(key: T, value: LibraryOptions[T]) => {
|
||||
setOptions((v) => ({ ...v, [key]: value }));
|
||||
};
|
||||
|
||||
@@ -85,17 +82,13 @@ const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
|
||||
<>
|
||||
<FormLabel>Display mode</FormLabel>
|
||||
<RadioGroup
|
||||
onChange={(e) =>
|
||||
handleFilterChange('gridLayout', Number(e.target.value))
|
||||
}
|
||||
onChange={(e) => handleFilterChange('gridLayout', Number(e.target.value))}
|
||||
value={gridLayout}
|
||||
>
|
||||
<RadioInput
|
||||
label="Compact grid"
|
||||
value={GridLayout.Compact}
|
||||
checked={
|
||||
gridLayout == null || gridLayout === GridLayout.Compact
|
||||
}
|
||||
checked={gridLayout == null || gridLayout === GridLayout.Compact}
|
||||
/>
|
||||
<RadioInput
|
||||
label="Comfortable grid"
|
||||
@@ -113,16 +106,12 @@ const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
|
||||
<CheckboxInput
|
||||
label="Unread Badges"
|
||||
checked={showUnreadBadge === true}
|
||||
onChange={() =>
|
||||
handleFilterChange('showUnreadBadge', !showUnreadBadge)
|
||||
}
|
||||
onChange={() => handleFilterChange('showUnreadBadge', !showUnreadBadge)}
|
||||
/>
|
||||
<CheckboxInput
|
||||
label="Download Badges"
|
||||
checked={showDownloadBadge === true}
|
||||
onChange={() =>
|
||||
handleFilterChange('showDownloadBadge', !showDownloadBadge)
|
||||
}
|
||||
onChange={() => handleFilterChange('showDownloadBadge', !showDownloadBadge)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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 LibraryOptionsContext, {
|
||||
DefaultLibraryOptions,
|
||||
} from 'components/context/LibraryOptionsContext';
|
||||
import LibraryOptionsContext, { DefaultLibraryOptions } from 'components/context/LibraryOptionsContext';
|
||||
import React from 'react';
|
||||
import useLocalStorage from 'util/useLocalStorage';
|
||||
|
||||
@@ -16,16 +14,9 @@ interface IProps {
|
||||
}
|
||||
|
||||
const LibraryOptionsContextProvider: React.FC<IProps> = ({ children }) => {
|
||||
const [options, setOptions] = useLocalStorage<LibraryOptions>(
|
||||
'libraryOptions',
|
||||
DefaultLibraryOptions,
|
||||
);
|
||||
const [options, setOptions] = useLocalStorage<LibraryOptions>('libraryOptions', DefaultLibraryOptions);
|
||||
|
||||
return (
|
||||
<LibraryOptionsContext.Provider value={{ options, setOptions }}>
|
||||
{children}
|
||||
</LibraryOptionsContext.Provider>
|
||||
);
|
||||
return <LibraryOptionsContext.Provider value={{ options, setOptions }}>{children}</LibraryOptionsContext.Provider>;
|
||||
};
|
||||
|
||||
export default LibraryOptionsContextProvider;
|
||||
|
||||
@@ -22,10 +22,7 @@ function Progress({ progress }: IProgressProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const baseWebsocketUrl = JSON.parse(window.localStorage.getItem('serverBaseURL')!).replace(
|
||||
'http',
|
||||
'ws',
|
||||
);
|
||||
const baseWebsocketUrl = JSON.parse(window.localStorage.getItem('serverBaseURL')!).replace('http', 'ws');
|
||||
|
||||
interface IUpdateCheckerProps {
|
||||
handleFinishedUpdate: (time: number) => void;
|
||||
@@ -58,8 +55,7 @@ function UpdateChecker({ handleFinishedUpdate }: IUpdateCheckerProps) {
|
||||
const { running, statusMap } = JSON.parse(e.data) as IUpdateStatus;
|
||||
const { COMPLETE = [], RUNNING = [], PENDING = [] } = statusMap;
|
||||
|
||||
const currentProgress =
|
||||
100 * (COMPLETE.length / (COMPLETE.length + RUNNING.length + PENDING.length));
|
||||
const currentProgress = 100 * (COMPLETE.length / (COMPLETE.length + RUNNING.length + PENDING.length));
|
||||
|
||||
const isUpdateFinished = currentProgress === 100;
|
||||
const ignoreFaultyMessage = !updateStarted && !running && isUpdateFinished;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const baseWebsocketUrl = JSON.parse(window.localStorage.getItem('serverBaseURL')!).replace(
|
||||
'http',
|
||||
'ws',
|
||||
);
|
||||
const baseWebsocketUrl = JSON.parse(window.localStorage.getItem('serverBaseURL')!).replace('http', 'ws');
|
||||
|
||||
const useSubscription = <T>(path: string, callback?: (newValue: T) => boolean | void) => {
|
||||
const [state, setState] = useState<T | undefined>();
|
||||
|
||||
@@ -42,14 +42,7 @@ interface IProps {
|
||||
const ChapterCard: React.FC<IProps> = (props: IProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const {
|
||||
chapter,
|
||||
triggerChaptersUpdate,
|
||||
downloadChapter: dc,
|
||||
showChapterNumber,
|
||||
onSelect,
|
||||
selected,
|
||||
} = props;
|
||||
const { chapter, triggerChaptersUpdate, downloadChapter: dc, showChapterNumber, onSelect, selected } = props;
|
||||
const isSelecting = selected !== null;
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
@@ -85,9 +78,7 @@ const ChapterCard: React.FC<IProps> = (props: IProps) => {
|
||||
};
|
||||
|
||||
const deleteChapter = () => {
|
||||
client
|
||||
.delete(`/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}`)
|
||||
.then(() => triggerChaptersUpdate());
|
||||
client.delete(`/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}`).then(() => triggerChaptersUpdate());
|
||||
handleClose();
|
||||
};
|
||||
|
||||
@@ -143,9 +134,7 @@ const ChapterCard: React.FC<IProps> = (props: IProps) => {
|
||||
sx={{ mr: 0.5, position: 'relative', top: '0.15em' }}
|
||||
/>
|
||||
)}
|
||||
{showChapterNumber
|
||||
? `Chapter ${chapter.chapterNumber}`
|
||||
: chapter.name}
|
||||
{showChapterNumber ? `Chapter ${chapter.chapterNumber}` : chapter.name}
|
||||
</Typography>
|
||||
<Typography variant="caption">{chapter.scanlator}</Typography>
|
||||
<Typography variant="caption">
|
||||
@@ -165,12 +154,7 @@ const ChapterCard: React.FC<IProps> = (props: IProps) => {
|
||||
)}
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
||||
<MenuItem onClick={handleSelect}>
|
||||
<ListItemIcon>
|
||||
<CheckBoxOutlineBlank fontSize="small" />
|
||||
|
||||
@@ -140,10 +140,7 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
|
||||
setSelection(null);
|
||||
};
|
||||
|
||||
const handleFabAction: ComponentProps<typeof SelectionFAB>['onAction'] = (
|
||||
action,
|
||||
actionChapters,
|
||||
) => {
|
||||
const handleFabAction: ComponentProps<typeof SelectionFAB>['onAction'] = (action, actionChapters) => {
|
||||
if (actionChapters.length === 0) return;
|
||||
const chapterIds = actionChapters.map(({ chapter }) => chapter.id);
|
||||
|
||||
@@ -164,16 +161,9 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
|
||||
}
|
||||
|
||||
actionPromise
|
||||
.then(() =>
|
||||
makeToast(
|
||||
interpolate(chapterIds.length, actionsStrings[action].success),
|
||||
'success',
|
||||
),
|
||||
)
|
||||
.then(() => makeToast(interpolate(chapterIds.length, actionsStrings[action].success), 'success'))
|
||||
.then(() => mutate())
|
||||
.catch(() =>
|
||||
makeToast(interpolate(chapterIds.length, actionsStrings[action].error), 'error'),
|
||||
);
|
||||
.catch(() => makeToast(interpolate(chapterIds.length, actionsStrings[action].error), 'error'));
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
@@ -206,9 +196,7 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
|
||||
});
|
||||
|
||||
const selectedChapters =
|
||||
selection === null
|
||||
? null
|
||||
: chaptersWithMeta.filter(({ chapter }) => selection.includes(chapter.id));
|
||||
selection === null ? null : chaptersWithMeta.filter(({ chapter }) => selection.includes(chapter.id));
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -225,9 +213,7 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5">
|
||||
{`${visibleChapters.length} Chapter${
|
||||
visibleChapters.length === 1 ? '' : 's'
|
||||
}`}
|
||||
{`${visibleChapters.length} Chapter${visibleChapters.length === 1 ? '' : 's'}`}
|
||||
</Typography>
|
||||
|
||||
{selection === null ? (
|
||||
|
||||
@@ -134,22 +134,14 @@ const MangaDetails: React.FC<IProps> = ({ manga }) => {
|
||||
const classes = useStyles(manga.inLibrary)();
|
||||
|
||||
const addToLibrary = () => {
|
||||
mutate(
|
||||
`/api/v1/manga/${manga.id}/?onlineFetch=false`,
|
||||
{ ...manga, inLibrary: true },
|
||||
{ revalidate: false },
|
||||
);
|
||||
mutate(`/api/v1/manga/${manga.id}/?onlineFetch=false`, { ...manga, inLibrary: true }, { revalidate: false });
|
||||
client
|
||||
.get(`/api/v1/manga/${manga.id}/library/`)
|
||||
.then(() => mutate(`/api/v1/manga/${manga.id}/?onlineFetch=false`));
|
||||
};
|
||||
|
||||
const removeFromLibrary = () => {
|
||||
mutate(
|
||||
`/api/v1/manga/${manga.id}/?onlineFetch=false`,
|
||||
{ ...manga, inLibrary: false },
|
||||
{ revalidate: false },
|
||||
);
|
||||
mutate(`/api/v1/manga/${manga.id}/?onlineFetch=false`, { ...manga, inLibrary: false }, { revalidate: false });
|
||||
client
|
||||
.delete(`/api/v1/manga/${manga.id}/library/`)
|
||||
.then(() => mutate(`/api/v1/manga/${manga.id}/?onlineFetch=false`));
|
||||
@@ -160,10 +152,7 @@ const MangaDetails: React.FC<IProps> = ({ manga }) => {
|
||||
<div className={classes.top}>
|
||||
<div className={classes.leftRight}>
|
||||
<div className={classes.leftSide}>
|
||||
<img
|
||||
src={`${serverAddress}${manga.thumbnailUrl}?useCache=${useCache}`}
|
||||
alt="Manga Thumbnail"
|
||||
/>
|
||||
<img src={`${serverAddress}${manga.thumbnailUrl}?useCache=${useCache}`} alt="Manga Thumbnail" />
|
||||
</div>
|
||||
<div className={classes.rightSide}>
|
||||
<h1>{manga.title}</h1>
|
||||
@@ -181,15 +170,8 @@ const MangaDetails: React.FC<IProps> = ({ manga }) => {
|
||||
</div>
|
||||
<div className={classes.buttons}>
|
||||
<div>
|
||||
<IconButton
|
||||
onClick={manga.inLibrary ? removeFromLibrary : addToLibrary}
|
||||
size="large"
|
||||
>
|
||||
{manga.inLibrary ? (
|
||||
<FavoriteIcon sx={{ mr: 1 }} />
|
||||
) : (
|
||||
<FavoriteBorderIcon sx={{ mr: 1 }} />
|
||||
)}
|
||||
<IconButton onClick={manga.inLibrary ? removeFromLibrary : addToLibrary} size="large">
|
||||
{manga.inLibrary ? <FavoriteIcon sx={{ mr: 1 }} /> : <FavoriteBorderIcon sx={{ mr: 1 }} />}
|
||||
<Typography sx={{ fontSize: { xs: '0.75em', sm: '0.85em' } }}>
|
||||
{manga.inLibrary ? 'In Library' : 'Add To Library'}
|
||||
</Typography>
|
||||
@@ -198,9 +180,7 @@ const MangaDetails: React.FC<IProps> = ({ manga }) => {
|
||||
<a href={manga.realUrl} target="_blank" rel="noreferrer">
|
||||
<IconButton size="large">
|
||||
<PublicIcon sx={{ mr: 1 }} />
|
||||
<Typography sx={{ fontSize: { xs: '0.75em', sm: '0.85em' } }}>
|
||||
Open Site
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: { xs: '0.75em', sm: '0.85em' } }}>Open Site</Typography>
|
||||
</IconButton>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -13,13 +13,7 @@ import React, { useRef, useState } from 'react';
|
||||
import type { IChapterWithMeta } from 'components/manga/ChapterList';
|
||||
import SelectionFABActionItem from 'components/manga/SelectionFABActionItem';
|
||||
|
||||
export type SelectionAction =
|
||||
| 'download'
|
||||
| 'delete'
|
||||
| 'bookmark'
|
||||
| 'unbookmark'
|
||||
| 'mark_as_read'
|
||||
| 'mark_as_unread';
|
||||
export type SelectionAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
|
||||
|
||||
interface SelectionFABProps {
|
||||
selectedChapters: IChapterWithMeta[];
|
||||
@@ -49,12 +43,7 @@ const SelectionFAB: React.FC<SelectionFABProps> = (props) => {
|
||||
}}
|
||||
ref={anchorEl}
|
||||
>
|
||||
<Fab
|
||||
variant="extended"
|
||||
color="primary"
|
||||
id="selectionMenuButton"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<Fab variant="extended" color="primary" id="selectionMenuButton" onClick={() => setOpen(true)}>
|
||||
{`${count} ${pluralize(count, 'chapter')}`}
|
||||
<MoreHoriz sx={{ ml: 1 }} />
|
||||
</Fab>
|
||||
|
||||
@@ -17,15 +17,11 @@ const defaultChapterOptions: ChapterListOptions = {
|
||||
showChapterNumber: false,
|
||||
};
|
||||
|
||||
function chapterOptionsReducer(
|
||||
state: ChapterListOptions,
|
||||
actions: ChapterOptionsReducerAction,
|
||||
): ChapterListOptions {
|
||||
function chapterOptionsReducer(state: ChapterListOptions, actions: ChapterOptionsReducerAction): ChapterListOptions {
|
||||
switch (actions.type) {
|
||||
case 'filter':
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const active =
|
||||
state.unread !== false && state.downloaded !== false && state.bookmarked !== false;
|
||||
const active = state.unread !== false && state.downloaded !== false && state.bookmarked !== false;
|
||||
return {
|
||||
...state,
|
||||
active,
|
||||
@@ -53,10 +49,7 @@ export function unreadFilter(unread: NullAndUndefined<boolean>, { read: isChapte
|
||||
}
|
||||
}
|
||||
|
||||
function downloadFilter(
|
||||
downloaded: NullAndUndefined<boolean>,
|
||||
{ downloaded: chapterDownload }: IChapter,
|
||||
) {
|
||||
function downloadFilter(downloaded: NullAndUndefined<boolean>, { downloaded: chapterDownload }: IChapter) {
|
||||
switch (downloaded) {
|
||||
case true:
|
||||
return chapterDownload;
|
||||
@@ -67,10 +60,7 @@ function downloadFilter(
|
||||
}
|
||||
}
|
||||
|
||||
function bookmarkedFilter(
|
||||
bookmarked: NullAndUndefined<boolean>,
|
||||
{ bookmarked: chapterBookmarked }: IChapter,
|
||||
) {
|
||||
function bookmarkedFilter(bookmarked: NullAndUndefined<boolean>, { bookmarked: chapterBookmarked }: IChapter) {
|
||||
switch (bookmarked) {
|
||||
case true:
|
||||
return chapterBookmarked;
|
||||
@@ -81,10 +71,7 @@ function bookmarkedFilter(
|
||||
}
|
||||
}
|
||||
|
||||
export function filterAndSortChapters(
|
||||
chapters: IChapter[],
|
||||
options: ChapterListOptions,
|
||||
): IChapter[] {
|
||||
export function filterAndSortChapters(chapters: IChapter[], options: ChapterListOptions): IChapter[] {
|
||||
const filtered = options.active
|
||||
? chapters.filter(
|
||||
(chp) =>
|
||||
@@ -93,10 +80,7 @@ export function filterAndSortChapters(
|
||||
bookmarkedFilter(options.bookmarked, chp),
|
||||
)
|
||||
: [...chapters];
|
||||
const Sorted =
|
||||
options.sortBy === 'fetchedAt'
|
||||
? filtered.sort((a, b) => a.fetchedAt - b.fetchedAt)
|
||||
: filtered;
|
||||
const Sorted = options.sortBy === 'fetchedAt' ? filtered.sort((a, b) => a.fetchedAt - b.fetchedAt) : filtered;
|
||||
if (options.reverse) {
|
||||
Sorted.reverse();
|
||||
}
|
||||
|
||||
@@ -24,9 +24,7 @@ const DownloadStateIndicator: React.FC<DownloadStateIndicatorProps> = ({ downloa
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{download.progress !== 0 && (
|
||||
<CircularProgress variant="determinate" value={download.progress * 100} />
|
||||
)}
|
||||
{download.progress !== 0 && <CircularProgress variant="determinate" value={download.progress * 100} />}
|
||||
<Box
|
||||
sx={{
|
||||
top: 0,
|
||||
|
||||
@@ -100,9 +100,7 @@ export default function DefaultNavBar() {
|
||||
let navbar = <></>;
|
||||
if (isMobileWidth) {
|
||||
if (isMainRoute) {
|
||||
navbar = (
|
||||
<MobileBottomBar navBarItems={navbarItems.filter((it) => it.show !== 'desktop')} />
|
||||
);
|
||||
navbar = <MobileBottomBar navBarItems={navbarItems.filter((it) => it.show !== 'desktop')} />;
|
||||
}
|
||||
} else {
|
||||
navbar = <DesktopSideBar navBarItems={navbarItems.filter((it) => it.show !== 'mobile')} />;
|
||||
|
||||
@@ -126,9 +126,7 @@ export default function ReaderNavBar(props: IProps) {
|
||||
const [updateDrawerOnRender, setUpdateDrawerOnRender] = useState(true);
|
||||
const [hideOpenButton, setHideOpenButton] = useState(settings.staticNav || prevDrawerOpen);
|
||||
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
||||
const [settingsCollapseOpen, setSettingsCollapseOpen] = useState(
|
||||
prevSettingsCollapseOpen ?? true,
|
||||
);
|
||||
const [settingsCollapseOpen, setSettingsCollapseOpen] = useState(prevSettingsCollapseOpen ?? true);
|
||||
|
||||
const updateSettingValue = (key: keyof IReaderSettings, value: string | boolean) => {
|
||||
// prevent closing the navBar when updating the "staticNav" setting
|
||||
@@ -181,14 +179,7 @@ export default function ReaderNavBar(props: IProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Slide
|
||||
direction="right"
|
||||
in={drawerOpen}
|
||||
timeout={200}
|
||||
appear={false}
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
>
|
||||
<Slide direction="right" in={drawerOpen} timeout={200} appear={false} mountOnEnter unmountOnExit>
|
||||
<Root
|
||||
sx={{
|
||||
position: settings.staticNav ? 'sticky' : 'fixed',
|
||||
@@ -207,12 +198,7 @@ export default function ReaderNavBar(props: IProps) {
|
||||
<KeyboardArrowLeftIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
<Typography
|
||||
variant="h1"
|
||||
textOverflow="ellipsis"
|
||||
overflow="hidden"
|
||||
sx={{ py: 1 }}
|
||||
>
|
||||
<Typography variant="h1" textOverflow="ellipsis" overflow="hidden" sx={{ py: 1 }}>
|
||||
{chapter.name}
|
||||
</Typography>
|
||||
<IconButton
|
||||
|
||||
@@ -25,9 +25,7 @@ interface IProps {
|
||||
export default function CategorySelect(props: IProps) {
|
||||
const { open, setOpen, mangaId } = props;
|
||||
|
||||
const { data: mangaCategoriesData, mutate } = useQuery<ICategory[]>(
|
||||
`/api/v1/manga/${mangaId}/category`,
|
||||
);
|
||||
const { data: mangaCategoriesData, mutate } = useQuery<ICategory[]>(`/api/v1/manga/${mangaId}/category`);
|
||||
const { data: categoriesData } = useQuery<ICategory[]>('/api/v1/category');
|
||||
|
||||
const allCategories = useMemo(() => {
|
||||
|
||||
@@ -40,9 +40,7 @@ interface IProps {
|
||||
export default function LangSelect(props: IProps) {
|
||||
const { shownLangs, setShownLangs, allLangs, forcedLangs } = props;
|
||||
// hold a copy and only sate state on parent when OK pressed, improves performance
|
||||
const [mShownLangs, setMShownLangs] = useState(
|
||||
removeAll(cloneObject(shownLangs), forcedLangs!),
|
||||
);
|
||||
const [mShownLangs, setMShownLangs] = useState(removeAll(cloneObject(shownLangs), forcedLangs!));
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
|
||||
const handleCancel = () => {
|
||||
|
||||
@@ -35,10 +35,7 @@ export default function DesktopSideBar({ navBarItems }: IProps) {
|
||||
if (location.pathname === path)
|
||||
return <SelectedIconComponent sx={{ color: 'primary.main' }} fontSize="large" />;
|
||||
return (
|
||||
<IconComponent
|
||||
sx={{ color: theme.palette.mode === 'dark' ? 'grey.A400' : 'grey.600' }}
|
||||
fontSize="large"
|
||||
/>
|
||||
<IconComponent sx={{ color: theme.palette.mode === 'dark' ? 'grey.A400' : 'grey.600' }} fontSize="large" />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -46,23 +43,17 @@ export default function DesktopSideBar({ navBarItems }: IProps) {
|
||||
<SideNavBarContainer>
|
||||
{
|
||||
// eslint-disable-next-line react/destructuring-assignment
|
||||
navBarItems.map(
|
||||
({ path, title, IconComponent, SelectedIconComponent }: NavbarItem) => (
|
||||
<Link
|
||||
to={path}
|
||||
style={{ color: 'inherit', textDecoration: 'none' }}
|
||||
key={path}
|
||||
>
|
||||
<ListItem disableRipple button key={title}>
|
||||
<ListItemIcon sx={{ minWidth: '0' }}>
|
||||
<Tooltip placement="right" title={title}>
|
||||
{iconFor(path, IconComponent, SelectedIconComponent)}
|
||||
</Tooltip>
|
||||
</ListItemIcon>
|
||||
</ListItem>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
navBarItems.map(({ path, title, IconComponent, SelectedIconComponent }: NavbarItem) => (
|
||||
<Link to={path} style={{ color: 'inherit', textDecoration: 'none' }} key={path}>
|
||||
<ListItem disableRipple button key={title}>
|
||||
<ListItemIcon sx={{ minWidth: '0' }}>
|
||||
<Tooltip placement="right" title={title}>
|
||||
{iconFor(path, IconComponent, SelectedIconComponent)}
|
||||
</Tooltip>
|
||||
</ListItemIcon>
|
||||
</ListItem>
|
||||
</Link>
|
||||
))
|
||||
}
|
||||
</SideNavBarContainer>
|
||||
);
|
||||
|
||||
@@ -44,45 +44,35 @@ export default function MobileBottomBar({ navBarItems }: IProps) {
|
||||
if (location.pathname === path)
|
||||
return <SelectedIconComponent sx={{ color: 'primary.main' }} fontSize="medium" />;
|
||||
return (
|
||||
<IconComponent
|
||||
sx={{ color: theme.palette.mode === 'dark' ? 'grey.A400' : 'grey.600' }}
|
||||
fontSize="medium"
|
||||
/>
|
||||
<IconComponent sx={{ color: theme.palette.mode === 'dark' ? 'grey.A400' : 'grey.600' }} fontSize="medium" />
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BottomNavContainer>
|
||||
{navBarItems.map(
|
||||
({ path, title, IconComponent, SelectedIconComponent }: NavbarItem) => (
|
||||
<Link to={path} key={path}>
|
||||
<ListItem
|
||||
disableRipple
|
||||
button
|
||||
sx={{ justifyContent: 'center', padding: '8px' }}
|
||||
key={title}
|
||||
>
|
||||
<Box display="flex" flexDirection="column" alignItems="center">
|
||||
{iconFor(path, IconComponent, SelectedIconComponent)}
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: '0.65rem',
|
||||
color:
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
location.pathname === path
|
||||
? 'primary.main'
|
||||
: theme.palette.mode === 'dark'
|
||||
? 'grey.A400'
|
||||
: 'grey.600',
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Box>
|
||||
{navBarItems.map(({ path, title, IconComponent, SelectedIconComponent }: NavbarItem) => (
|
||||
<Link to={path} key={path}>
|
||||
<ListItem disableRipple button sx={{ justifyContent: 'center', padding: '8px' }} key={title}>
|
||||
<Box display="flex" flexDirection="column" alignItems="center">
|
||||
{iconFor(path, IconComponent, SelectedIconComponent)}
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: '0.65rem',
|
||||
color:
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
location.pathname === path
|
||||
? 'primary.main'
|
||||
: theme.palette.mode === 'dark'
|
||||
? 'grey.A400'
|
||||
: 'grey.600',
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Box>
|
||||
</ListItem>
|
||||
</Link>
|
||||
),
|
||||
)}
|
||||
</Box>
|
||||
</ListItem>
|
||||
</Link>
|
||||
))}
|
||||
</BottomNavContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -164,10 +164,8 @@ export default function HorizontalPager(props: IReaderProps) {
|
||||
ref={selfRef}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection:
|
||||
settings.readerType === 'ContinuesHorizontalLTR' ? 'row' : 'row-reverse',
|
||||
justifyContent:
|
||||
settings.readerType === 'ContinuesHorizontalLTR' ? 'flex-start' : 'flex-end',
|
||||
flexDirection: settings.readerType === 'ContinuesHorizontalLTR' ? 'row' : 'row-reverse',
|
||||
justifyContent: settings.readerType === 'ContinuesHorizontalLTR' ? 'flex-start' : 'flex-end',
|
||||
margin: '0 auto',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
|
||||
@@ -98,13 +98,7 @@ export default function PagedReader(props: IReaderProps) {
|
||||
height: '100vh',
|
||||
}}
|
||||
>
|
||||
<Page
|
||||
key={curPage}
|
||||
index={curPage}
|
||||
onImageLoad={() => {}}
|
||||
src={pages[curPage].src}
|
||||
settings={settings}
|
||||
/>
|
||||
<Page key={curPage} index={curPage} onImageLoad={() => {}} src={pages[curPage].src} settings={settings} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,9 +85,7 @@ export default function VerticalPager(props: IReaderProps) {
|
||||
}
|
||||
|
||||
window.scroll({
|
||||
top:
|
||||
window.scrollY +
|
||||
window.innerHeight * SCROLL_OFFSET * (direction === 'up' ? -1 : 1),
|
||||
top: window.scrollY + window.innerHeight * SCROLL_OFFSET * (direction === 'up' ? -1 : 1),
|
||||
behavior: SCROLL_BEHAVIOR,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -58,10 +58,7 @@ export default function SourceGridLayout() {
|
||||
control={
|
||||
<Radio
|
||||
name={GridLayout.Compact.toString()}
|
||||
checked={
|
||||
SourcegridLayout === GridLayout.Compact ||
|
||||
SourcegridLayout === undefined
|
||||
}
|
||||
checked={SourcegridLayout === GridLayout.Compact || SourcegridLayout === undefined}
|
||||
onChange={setGridContextOptions}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -16,16 +16,7 @@ function filterManga(mangas: IMangaCard[]): IMangaCard[] {
|
||||
}
|
||||
|
||||
export default function SourceMangaGrid(props: IMangaGridProps) {
|
||||
const {
|
||||
mangas,
|
||||
isLoading,
|
||||
hasNextPage,
|
||||
lastPageNum,
|
||||
setLastPageNum,
|
||||
message,
|
||||
messageExtra,
|
||||
gridLayout,
|
||||
} = props;
|
||||
const { mangas, isLoading, hasNextPage, lastPageNum, setLastPageNum, message, messageExtra, gridLayout } = props;
|
||||
|
||||
const filteredManga = filterManga(mangas);
|
||||
const showFilteredOutMessage = filteredManga.length === 0 && mangas.length > 0;
|
||||
|
||||
@@ -72,9 +72,7 @@ export function Options({ sourceFilter, group, updateFilterValue, update }: IFil
|
||||
/>
|
||||
);
|
||||
case 'Header':
|
||||
return (
|
||||
<HeaderFilter key={`filters ${e.filter.name}`} name={e.filter.name} />
|
||||
);
|
||||
return <HeaderFilter key={`filters ${e.filter.name}`} name={e.filter.name} />;
|
||||
case 'Select':
|
||||
return (
|
||||
<SelectFilter
|
||||
@@ -90,12 +88,7 @@ export function Options({ sourceFilter, group, updateFilterValue, update }: IFil
|
||||
/>
|
||||
);
|
||||
case 'Separator':
|
||||
return (
|
||||
<SeperatorFilter
|
||||
key={`filters ${e.filter.name}`}
|
||||
name={e.filter.name}
|
||||
/>
|
||||
);
|
||||
return <SeperatorFilter key={`filters ${e.filter.name}`} name={e.filter.name} />;
|
||||
case 'Sort':
|
||||
return (
|
||||
<SortFilter
|
||||
|
||||
@@ -24,8 +24,7 @@ const CheckBoxFilter: React.FC<Props> = (props: Props) => {
|
||||
const handleChange = (event: { target: { name: any; checked: any } }) => {
|
||||
setval(event.target.checked);
|
||||
const upd = update.filter(
|
||||
(e: { position: number; group: number | undefined }) =>
|
||||
!(position === e.position && group === e.group),
|
||||
(e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
|
||||
);
|
||||
updateFilterValue([...upd, { position, state: event.target.checked.toString(), group }]);
|
||||
};
|
||||
|
||||
@@ -44,8 +44,7 @@ function hasSelect(
|
||||
const vall = values.map((e) => e.displayname).indexOf(`${event.target.value}`);
|
||||
setval(vall);
|
||||
const upd = update.filter(
|
||||
(e: { position: number; group: number | undefined }) =>
|
||||
!(position === e.position && group === e.group),
|
||||
(e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
|
||||
);
|
||||
updateFilterValue([...upd, { position, state: vall.toString(), group }]);
|
||||
};
|
||||
@@ -58,12 +57,7 @@ function hasSelect(
|
||||
return (
|
||||
<FormControl sx={{ my: 1 }} variant="standard">
|
||||
<InputLabel>{name}</InputLabel>
|
||||
<Select
|
||||
name={name}
|
||||
value={values[val].displayname}
|
||||
label={name}
|
||||
onChange={handleChange}
|
||||
>
|
||||
<Select name={name} value={values[val].displayname} label={name} onChange={handleChange}>
|
||||
{rett}
|
||||
</Select>
|
||||
</FormControl>
|
||||
@@ -88,8 +82,7 @@ function noSelect(
|
||||
const vall = values.indexOf(`${event.target.value}`);
|
||||
setval(vall);
|
||||
const upd = update.filter(
|
||||
(e: { position: number; group: number | undefined }) =>
|
||||
!(position === e.position && group === e.group),
|
||||
(e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
|
||||
);
|
||||
updateFilterValue([...upd, { position, state: vall.toString(), group }]);
|
||||
};
|
||||
|
||||
@@ -42,8 +42,7 @@ const SortFilter: React.FC<Props> = (props: Props) => {
|
||||
tmp.index = index;
|
||||
setval(tmp);
|
||||
const upd = update.filter(
|
||||
(e: { position: number; group: number | undefined }) =>
|
||||
!(position === e.position && group === e.group),
|
||||
(e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
|
||||
);
|
||||
updateFilterValue([...upd, { position, state: JSON.stringify(tmp), group }]);
|
||||
};
|
||||
|
||||
@@ -25,8 +25,7 @@ const TextFilter: React.FC<Props> = (props) => {
|
||||
|
||||
function doneTyping(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
const upd = update.filter(
|
||||
(el: { position: number; group: number | undefined }) =>
|
||||
!(position === el.position && group === el.group),
|
||||
(el: { position: number; group: number | undefined }) => !(position === el.position && group === el.group),
|
||||
);
|
||||
updateFilterValue([...upd, { position, state: e.target.value, group }]);
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@ const TriStateFilter: React.FC<Props> = (props) => {
|
||||
const newState = checked === undefined ? 0 : checked ? 1 : 2;
|
||||
setval(newState);
|
||||
const upd = update.filter(
|
||||
(e: { position: number; group: number | undefined }) =>
|
||||
!(position === e.position && group === e.group),
|
||||
(e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
|
||||
);
|
||||
updateFilterValue([
|
||||
...upd,
|
||||
|
||||
@@ -65,12 +65,7 @@ function ListDialog(props: IListDialogProps) {
|
||||
<DialogContent dividers>
|
||||
<RadioGroup ref={radioGroupRef} value={value} onChange={handleChange}>
|
||||
{options.map((option) => (
|
||||
<FormControlLabel
|
||||
value={option}
|
||||
key={option}
|
||||
control={<Radio />}
|
||||
label={option}
|
||||
/>
|
||||
<FormControlLabel value={option} key={option} control={<Radio />} label={option} />
|
||||
))}
|
||||
</RadioGroup>
|
||||
</DialogContent>
|
||||
|
||||
@@ -64,11 +64,7 @@ function ListDialog(props: IListDialogProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
sx={{ '& .MuiDialog-paper': { width: '80%', maxHeight: 435 } }}
|
||||
maxWidth="xs"
|
||||
open={open}
|
||||
>
|
||||
<Dialog sx={{ '& .MuiDialog-paper': { width: '80%', maxHeight: 435 } }} maxWidth="xs" open={open}>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<FormGroup>
|
||||
@@ -76,9 +72,7 @@ function ListDialog(props: IListDialogProps) {
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={selectedValues.some(
|
||||
(selectedValue) => value === selectedValue,
|
||||
)}
|
||||
checked={selectedValues.some((selectedValue) => value === selectedValue)}
|
||||
onChange={(e) => handleChange(e, value)}
|
||||
color="default"
|
||||
/>
|
||||
|
||||
@@ -61,20 +61,14 @@ export default function makeToast(message: string, severity: Severity) {
|
||||
setTimeout(() => removeToast(container.id), 3500);
|
||||
}
|
||||
|
||||
export function makeToaster([toasts, setToasts]: [
|
||||
export function makeToaster([toasts, setToasts]: [React.ReactElement[], (arg0: React.ReactElement[]) => void]): [
|
||||
React.ReactElement[],
|
||||
(arg0: React.ReactElement[]) => void,
|
||||
]): [React.ReactElement[], (message: string, severity: Severity) => void] {
|
||||
(message: string, severity: Severity) => void,
|
||||
] {
|
||||
return [
|
||||
toasts,
|
||||
(message: string, severity: Severity) => {
|
||||
setToasts([
|
||||
<Toast
|
||||
key={Math.floor(Math.random() * 1000) + 1}
|
||||
message={message}
|
||||
severity={severity}
|
||||
/>,
|
||||
]);
|
||||
setToasts([<Toast key={Math.floor(Math.random() * 1000) + 1} message={message} severity={severity} />]);
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -97,9 +97,7 @@ const DownloadQueue: React.FC = () => {
|
||||
>
|
||||
<Card
|
||||
sx={{
|
||||
backgroundColor: snapshot.isDragging
|
||||
? 'custom.light'
|
||||
: undefined,
|
||||
backgroundColor: snapshot.isDragging ? 'custom.light' : undefined,
|
||||
}}
|
||||
>
|
||||
<CardActionArea
|
||||
@@ -117,18 +115,9 @@ const DownloadQueue: React.FC = () => {
|
||||
<IconButton sx={{ pointerEvents: 'none' }}>
|
||||
<DragHandle />
|
||||
</IconButton>
|
||||
<Stack
|
||||
sx={{ flex: 1, ml: 1 }}
|
||||
direction="column"
|
||||
>
|
||||
<Typography variant="h6">
|
||||
{item.manga.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
display="block"
|
||||
gutterBottom
|
||||
>
|
||||
<Stack sx={{ flex: 1, ml: 1 }} direction="column">
|
||||
<Typography variant="h6">{item.manga.title}</Typography>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
{item.chapter.name}
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
@@ -67,10 +67,7 @@ function groupExtensions(extensions: IExtension[]) {
|
||||
export default function MangaExtensions() {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const { setTitle, setAction } = useContext(NavbarContext);
|
||||
const [shownLangs, setShownLangs] = useLocalStorage<string[]>(
|
||||
'shownExtensionLangs',
|
||||
extensionDefaultLangs(),
|
||||
);
|
||||
const [shownLangs, setShownLangs] = useLocalStorage<string[]>('shownExtensionLangs', extensionDefaultLangs());
|
||||
const [showNsfw] = useLocalStorage<boolean>('showNsfw', true);
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
@@ -84,20 +81,12 @@ export default function MangaExtensions() {
|
||||
<IconButton onClick={() => inputRef.current?.click()} size="large">
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
<LangSelect
|
||||
shownLangs={shownLangs}
|
||||
setShownLangs={setShownLangs}
|
||||
allLangs={allLangs}
|
||||
/>
|
||||
<LangSelect shownLangs={shownLangs} setShownLangs={setShownLangs} allLangs={allLangs} />
|
||||
</>,
|
||||
);
|
||||
}, [shownLangs]);
|
||||
|
||||
const {
|
||||
data: allExtensions,
|
||||
mutate,
|
||||
loading,
|
||||
} = useQuery<IExtension[]>('/api/v1/extension/list');
|
||||
const { data: allExtensions, mutate, loading } = useQuery<IExtension[]>('/api/v1/extension/list');
|
||||
|
||||
const filteredExtensions = useMemo(
|
||||
() =>
|
||||
@@ -113,11 +102,7 @@ export default function MangaExtensions() {
|
||||
() =>
|
||||
groupExtensions(filteredExtensions)
|
||||
.filter((group) => group[EXTENSIONS].length > 0)
|
||||
.filter((group) =>
|
||||
['installed', 'updates pending', 'all', ...shownLangs].includes(
|
||||
group[LANGUAGE],
|
||||
),
|
||||
),
|
||||
.filter((group) => ['installed', 'updates pending', 'all', ...shownLangs].includes(group[LANGUAGE])),
|
||||
[shownLangs, filteredExtensions],
|
||||
);
|
||||
|
||||
|
||||
@@ -60,12 +60,7 @@ export default function Library() {
|
||||
};
|
||||
|
||||
if (tabsError != null) {
|
||||
return (
|
||||
<EmptyView
|
||||
message="Could not load categories"
|
||||
messageExtra={tabsError?.message ?? tabsError}
|
||||
/>
|
||||
);
|
||||
return <EmptyView message="Could not load categories" messageExtra={tabsError?.message ?? tabsError} />;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
|
||||
@@ -38,9 +38,7 @@ const Manga: React.FC = () => {
|
||||
const [refresh, { loading: refreshing }] = useRefreshManga(id);
|
||||
|
||||
useSetDefaultBackTo(
|
||||
manga?.inLibrary === false && manga.sourceId != null
|
||||
? `/sources/${manga.sourceId}/popular`
|
||||
: '/library',
|
||||
manga?.inLibrary === false && manga.sourceId != null ? `/sources/${manga.sourceId}/popular` : '/library',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -89,13 +87,7 @@ const Manga: React.FC = () => {
|
||||
<CircularProgress size={16} />
|
||||
</IconButton>
|
||||
)}
|
||||
{manga && (
|
||||
<MangaToolbarMenu
|
||||
manga={manga}
|
||||
onRefresh={refresh}
|
||||
refreshing={refreshing}
|
||||
/>
|
||||
)}
|
||||
{manga && <MangaToolbarMenu manga={manga} onRefresh={refresh} refreshing={refreshing} />}
|
||||
</Stack>
|
||||
</NavbarToolbar>
|
||||
|
||||
|
||||
@@ -75,8 +75,7 @@ export default function Reader() {
|
||||
const [curPage, setCurPage] = useState<number>(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);
|
||||
|
||||
@@ -97,9 +96,7 @@ export default function Reader() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!areDefaultSettingsLoading && !isMangaLoading) {
|
||||
checkAndHandleMissingStoredReaderSettings(manga, 'manga', defaultSettings).catch(
|
||||
() => {},
|
||||
);
|
||||
checkAndHandleMissingStoredReaderSettings(manga, 'manga', defaultSettings).catch(() => {});
|
||||
setSettings(getReaderSettingsFor(manga, defaultSettings));
|
||||
}
|
||||
}, [areDefaultSettingsLoading, isMangaLoading]);
|
||||
|
||||
@@ -16,12 +16,7 @@ import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { StringParam, useQueryParam } from 'use-query-params';
|
||||
import client from 'util/client';
|
||||
import {
|
||||
langCodeToName,
|
||||
langSortCmp,
|
||||
sourceDefualtLangs,
|
||||
sourceForcedDefaultLangs,
|
||||
} from 'util/language';
|
||||
import { langCodeToName, langSortCmp, sourceDefualtLangs, sourceForcedDefaultLangs } from 'util/language';
|
||||
import useLocalStorage from 'util/useLocalStorage';
|
||||
|
||||
function sourceToLangList(sources: ISource[]) {
|
||||
@@ -43,10 +38,7 @@ const SearchAll: React.FC = () => {
|
||||
const [triggerUpdate, setTriggerUpdate] = useState<number>(2);
|
||||
const [mangas, setMangas] = useState<any>({});
|
||||
|
||||
const [shownLangs, setShownLangs] = useLocalStorage<string[]>(
|
||||
'shownSourceLangs',
|
||||
sourceDefualtLangs(),
|
||||
);
|
||||
const [shownLangs, setShownLangs] = useLocalStorage<string[]>('shownSourceLangs', sourceDefualtLangs());
|
||||
const [showNsfw] = useLocalStorage<boolean>('showNsfw', true);
|
||||
|
||||
const [sources, setSources] = useState<ISource[]>([]);
|
||||
@@ -210,9 +202,7 @@ const SearchAll: React.FC = () => {
|
||||
sx={{ p: 3 }}
|
||||
>
|
||||
<Typography variant="h5">{displayName}</Typography>
|
||||
<Typography variant="caption">
|
||||
{langCodeToName(lang)}
|
||||
</Typography>
|
||||
<Typography variant="caption">{langCodeToName(lang)}</Typography>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
<MangaGrid
|
||||
|
||||
@@ -118,11 +118,7 @@ export default function Settings() {
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Dark Theme" />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={darkTheme}
|
||||
onChange={() => setDarkTheme(!darkTheme)}
|
||||
/>
|
||||
<Switch edge="end" checked={darkTheme} onChange={() => setDarkTheme(!darkTheme)} />
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
<ListItemButton>
|
||||
@@ -141,16 +137,9 @@ export default function Settings() {
|
||||
<ListItemIcon>
|
||||
<FavoriteIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Show NSFW"
|
||||
secondary="Hide NSFW extensions and sources"
|
||||
/>
|
||||
<ListItemText primary="Show NSFW" secondary="Hide NSFW extensions and sources" />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={showNsfw}
|
||||
onChange={() => setShowNsfw(!showNsfw)}
|
||||
/>
|
||||
<Switch edge="end" checked={showNsfw} onChange={() => setShowNsfw(!showNsfw)} />
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
@@ -163,11 +152,7 @@ export default function Settings() {
|
||||
but uses it much more internet traffic in turn"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={useCache}
|
||||
onChange={() => setUseCache(!useCache)}
|
||||
/>
|
||||
<Switch edge="end" checked={useCache} onChange={() => setUseCache(!useCache)} />
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
|
||||
@@ -9,10 +9,7 @@ import React, { useContext, useEffect } from 'react';
|
||||
import NavbarContext from 'components/context/NavbarContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import client, { useQuery } from 'util/client';
|
||||
import {
|
||||
SwitchPreferenceCompat,
|
||||
CheckBoxPreference,
|
||||
} from 'components/sourceConfiguration/TwoStatePreference';
|
||||
import { SwitchPreferenceCompat, CheckBoxPreference } from 'components/sourceConfiguration/TwoStatePreference';
|
||||
import ListPreference from 'components/sourceConfiguration/ListPreference';
|
||||
import EditTextPreference from 'components/sourceConfiguration/EditTextPreference';
|
||||
import MultiSelectListPreference from 'components/sourceConfiguration/MultiSelectListPreference';
|
||||
|
||||
@@ -221,9 +221,7 @@ export default function SourceMangas(props: { popular: boolean }) {
|
||||
messageExtra = (
|
||||
<>
|
||||
<span>Check out </span>
|
||||
<a href="https://github.com/Suwayomi/Tachidesk-Server/wiki/Local-Source">
|
||||
Local source guide
|
||||
</a>
|
||||
<a href="https://github.com/Suwayomi/Tachidesk-Server/wiki/Local-Source">Local source guide</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,12 +9,7 @@ import React, { useContext, useEffect } from 'react';
|
||||
import LangSelect from 'components/navbar/action/LangSelect';
|
||||
import SourceCard from 'components/SourceCard';
|
||||
import NavbarContext from 'components/context/NavbarContext';
|
||||
import {
|
||||
sourceDefualtLangs,
|
||||
sourceForcedDefaultLangs,
|
||||
langCodeToName,
|
||||
langSortCmp,
|
||||
} from 'util/language';
|
||||
import { sourceDefualtLangs, sourceForcedDefaultLangs, langCodeToName, langSortCmp } from 'util/language';
|
||||
import useLocalStorage from 'util/useLocalStorage';
|
||||
import LoadingPlaceholder from 'components/util/LoadingPlaceholder';
|
||||
import { IconButton } from '@mui/material';
|
||||
@@ -50,10 +45,7 @@ function groupByLang(sources: ISource[]) {
|
||||
export default function Sources() {
|
||||
const { setTitle, setAction } = useContext(NavbarContext);
|
||||
|
||||
const [shownLangs, setShownLangs] = useLocalStorage<string[]>(
|
||||
'shownSourceLangs',
|
||||
sourceDefualtLangs(),
|
||||
);
|
||||
const [shownLangs, setShownLangs] = useLocalStorage<string[]>('shownSourceLangs', sourceDefualtLangs());
|
||||
const [showNsfw] = useLocalStorage<boolean>('showNsfw', true);
|
||||
|
||||
const { data: sources, loading } = useQuery<ISource[]>('/api/v1/source/list');
|
||||
|
||||
@@ -46,9 +46,7 @@ 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 = {};
|
||||
@@ -64,10 +62,7 @@ function groupByDate(
|
||||
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: [],
|
||||
@@ -205,11 +200,7 @@ const Updates: React.FC = () => {
|
||||
<Typography variant="h5" component="h2">
|
||||
{manga.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
display="block"
|
||||
gutterBottom
|
||||
>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
{chapter.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -74,10 +74,7 @@ export default function Backup() {
|
||||
<>
|
||||
<List sx={{ padding: 0 }}>
|
||||
<ListItemLink to={`${baseURL}/api/v1/backup/export/file`} directLink>
|
||||
<ListItemText
|
||||
primary="Create Backup"
|
||||
secondary="Backup library as a Tachiyomi backup"
|
||||
/>
|
||||
<ListItemText primary="Create Backup" secondary="Backup library as a Tachiyomi backup" />
|
||||
</ListItemLink>
|
||||
<ListItem button onClick={() => document.getElementById('backup-file')?.click()}>
|
||||
<ListItemText
|
||||
|
||||
@@ -139,11 +139,7 @@ export default function Categories() {
|
||||
{(provided) => (
|
||||
<List ref={provided.innerRef}>
|
||||
{categories.map((item, index) => (
|
||||
<Draggable
|
||||
key={item.id}
|
||||
draggableId={item.id.toString()}
|
||||
index={index}
|
||||
>
|
||||
<Draggable key={item.id} draggableId={item.id.toString()} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
<ListItem
|
||||
ContainerProps={{ ref: provided.innerRef } as any}
|
||||
|
||||
@@ -51,11 +51,7 @@ export default function DefaultReaderSettings() {
|
||||
);
|
||||
}
|
||||
|
||||
checkAndHandleMissingStoredReaderSettings(
|
||||
{ meta: metadata },
|
||||
'server',
|
||||
getDefaultSettings(),
|
||||
).catch(() => {});
|
||||
checkAndHandleMissingStoredReaderSettings({ meta: metadata }, 'server', getDefaultSettings()).catch(() => {});
|
||||
|
||||
return (
|
||||
<ReaderSettingsOptions
|
||||
|
||||
@@ -83,10 +83,7 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,10 @@ const migrations: IMetadataMigration[] = [
|
||||
},
|
||||
];
|
||||
|
||||
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,
|
||||
key: string,
|
||||
appPrefix?: string,
|
||||
): boolean => Object.prototype.hasOwnProperty.call(meta ?? {}, getMetadataKey(key, appPrefix));
|
||||
const doesMetadataKeyExistIn = (meta: IMetadata | undefined, key: string, appPrefix?: string): boolean =>
|
||||
Object.prototype.hasOwnProperty.call(meta ?? {}, getMetadataKey(key, appPrefix));
|
||||
|
||||
const convertValueFromMetadata = <T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes>(
|
||||
value: string,
|
||||
@@ -44,10 +40,7 @@ const convertValueFromMetadata = <T extends AllowedMetadataValueTypes = AllowedM
|
||||
return value as T;
|
||||
};
|
||||
|
||||
const getAppMetadataFrom = (
|
||||
meta: IMetadata,
|
||||
appPrefix: string = APP_METADATA_KEY_PREFIX,
|
||||
): IMetadata => {
|
||||
const getAppMetadataFrom = (meta: IMetadata, appPrefix: string = APP_METADATA_KEY_PREFIX): IMetadata => {
|
||||
const appMetadata: IMetadata = {};
|
||||
|
||||
Object.entries(meta).forEach(([key, value]) => {
|
||||
@@ -71,9 +64,7 @@ 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) {
|
||||
@@ -134,9 +125,7 @@ const applyMetadataMigrations = (meta?: IMetadata): IMetadata | undefined => {
|
||||
return migrationToMetadata.pop()![1];
|
||||
};
|
||||
|
||||
export const getMetadataValueFrom = <
|
||||
T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes,
|
||||
>(
|
||||
export const getMetadataValueFrom = <T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes>(
|
||||
{ meta }: IMetadataHolder,
|
||||
key: AppMetadataKeys,
|
||||
defaultValue?: T,
|
||||
@@ -220,22 +209,14 @@ export const requestUpdateMetadata = async (
|
||||
): Promise<void[]> =>
|
||||
Promise.all(
|
||||
keysToValues.map(([key, value]) =>
|
||||
requestUpdateMetadataValue(
|
||||
endpoint,
|
||||
metadataHolder,
|
||||
key,
|
||||
value,
|
||||
endpointToMutate,
|
||||
wrapWithMetaKey,
|
||||
),
|
||||
requestUpdateMetadataValue(endpoint, metadataHolder, key, value, endpointToMutate, wrapWithMetaKey),
|
||||
),
|
||||
);
|
||||
|
||||
export const requestUpdateServerMetadata = async (
|
||||
serverMetadata: IMetadata,
|
||||
keysToValues: MetadataKeyValuePair[],
|
||||
): Promise<void[]> =>
|
||||
requestUpdateMetadata('', { meta: serverMetadata }, keysToValues, '/meta', false);
|
||||
): Promise<void[]> => requestUpdateMetadata('', { meta: serverMetadata }, keysToValues, '/meta', false);
|
||||
|
||||
export const requestUpdateMangaMetadata = async (
|
||||
manga: IMangaCard | IManga,
|
||||
|
||||
@@ -6,11 +6,7 @@
|
||||
* 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) =>
|
||||
@@ -75,9 +71,7 @@ export const checkAndHandleMissingStoredReaderSettings = async (
|
||||
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) => {
|
||||
|
||||
@@ -9,10 +9,7 @@ import React, { useState, Dispatch, SetStateAction, useReducer, Reducer, useCall
|
||||
import storage from 'util/localStorage';
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export default function useLocalStorage<T>(
|
||||
key: string,
|
||||
defaultValue: T | (() => T),
|
||||
): [T, Dispatch<SetStateAction<T>>] {
|
||||
export default function useLocalStorage<T>(key: string, defaultValue: T | (() => T)): [T, Dispatch<SetStateAction<T>>] {
|
||||
const initialState = defaultValue instanceof Function ? defaultValue() : defaultValue;
|
||||
const [storedValue, setStoredValue] = useState<T>(storage.getItem(key, initialState));
|
||||
|
||||
@@ -31,11 +28,7 @@ export default function useLocalStorage<T>(
|
||||
return [storedValue, setValue];
|
||||
}
|
||||
|
||||
export function useReducerLocalStorage<S, A>(
|
||||
reducer: Reducer<S, A>,
|
||||
key: string,
|
||||
defaultState: S | (() => S),
|
||||
) {
|
||||
export function useReducerLocalStorage<S, A>(reducer: Reducer<S, A>, key: string, defaultState: S | (() => S)) {
|
||||
const [storedValue, setValue] = useLocalStorage(key, defaultState);
|
||||
return useReducer((state: S, action: A): S => {
|
||||
const newState = reducer(state, action);
|
||||
|
||||
Reference in New Issue
Block a user