Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bab25f9ad9 | |||
| a62ee8f8c3 | |||
| 5f23691e20 | |||
| 3de9ccc62f | |||
| 1896f7f37b | |||
| 490643dc02 | |||
| 9808976088 |
@@ -97,7 +97,7 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// should be bumped with each stable release
|
// should be bumped with each stable release
|
||||||
val tachideskVersion = "v0.3.1"
|
val tachideskVersion = "v0.3.2"
|
||||||
|
|
||||||
// counts commit count on master
|
// counts commit count on master
|
||||||
val tachideskRevision = Runtime
|
val tachideskRevision = Runtime
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"@fontsource/roboto": "^4.3.0",
|
"@fontsource/roboto": "^4.3.0",
|
||||||
"@material-ui/core": "^4.11.4",
|
"@material-ui/core": "^4.11.4",
|
||||||
"@material-ui/icons": "^4.11.2",
|
"@material-ui/icons": "^4.11.2",
|
||||||
|
"@material-ui/lab": "^4.0.0-alpha.58",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"file-selector": "^0.2.4",
|
"file-selector": "^0.2.4",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import { Switch } from '@material-ui/core';
|
|||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||||
|
import MenuItem from '@material-ui/core/MenuItem';
|
||||||
|
import Select from '@material-ui/core/Select';
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
|
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
|
||||||
import Collapse from '@material-ui/core/Collapse';
|
import Collapse from '@material-ui/core/Collapse';
|
||||||
@@ -137,16 +139,11 @@ const useStyles = (settings: IReaderSettings) => makeStyles((theme: Theme) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export interface IReaderSettings{
|
|
||||||
staticNav: boolean
|
|
||||||
showPageNumber: boolean
|
|
||||||
continuesPageGap: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultReaderSettings = () => ({
|
export const defaultReaderSettings = () => ({
|
||||||
staticNav: false,
|
staticNav: false,
|
||||||
showPageNumber: true,
|
showPageNumber: true,
|
||||||
continuesPageGap: false,
|
continuesPageGap: false,
|
||||||
|
readerType: 'ContinuesVertical',
|
||||||
} as IReaderSettings);
|
} as IReaderSettings);
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@@ -171,7 +168,7 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
const [drawerVisible, setDrawerVisible] = useState(false || settings.staticNav);
|
const [drawerVisible, setDrawerVisible] = useState(false || settings.staticNav);
|
||||||
const [hideOpenButton, setHideOpenButton] = useState(false);
|
const [hideOpenButton, setHideOpenButton] = useState(false);
|
||||||
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
||||||
const [settingsCollapseOpen, setSettingsCollapseOpen] = useState(false);
|
const [settingsCollapseOpen, setSettingsCollapseOpen] = useState(true);
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const classes = useStyles(settings)();
|
const classes = useStyles(settings)();
|
||||||
@@ -205,7 +202,6 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ClickAwayListener onClickAway={() => (drawerVisible && setDrawerOpen(false))}>
|
|
||||||
<Slide
|
<Slide
|
||||||
direction="right"
|
direction="right"
|
||||||
in={drawerOpen}
|
in={drawerOpen}
|
||||||
@@ -282,14 +278,18 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText primary="Continues Page gap" />
|
<ListItemText primary="Reader Type" />
|
||||||
<ListItemSecondaryAction>
|
<Select
|
||||||
<Switch
|
value={settings.readerType}
|
||||||
edge="end"
|
onChange={(e) => setSettingValue('readerType', e.target.value)}
|
||||||
checked={settings.continuesPageGap}
|
>
|
||||||
onChange={(e) => setSettingValue('continuesPageGap', e.target.checked)}
|
<MenuItem value="SingleLTR">Left to right</MenuItem>
|
||||||
/>
|
<MenuItem value="SingleRTL">Right to left(WIP)</MenuItem>
|
||||||
</ListItemSecondaryAction>
|
<MenuItem value="SingleVertical">Vertical(WIP)</MenuItem>
|
||||||
|
<MenuItem value="Webtoon">Webtoon</MenuItem>
|
||||||
|
<MenuItem value="ContinuesVertical">Continues Vertical</MenuItem>
|
||||||
|
<MenuItem value="ContinuesHorizontal">Horizontal(WIP)</MenuItem>
|
||||||
|
</Select>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
@@ -341,7 +341,6 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Slide>
|
</Slide>
|
||||||
</ClickAwayListener>
|
|
||||||
<Zoom in={!drawerOpen}>
|
<Zoom in={!drawerOpen}>
|
||||||
<Fade in={!hideOpenButton}>
|
<Fade in={!hideOpenButton}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const useStyles = makeStyles({
|
|||||||
interface IProps {
|
interface IProps {
|
||||||
drawerOpen: boolean
|
drawerOpen: boolean
|
||||||
|
|
||||||
setDrawerOpen(state: boolean): void
|
setDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TemporaryDrawer({ drawerOpen, setDrawerOpen }: IProps) {
|
export default function TemporaryDrawer({ drawerOpen, setDrawerOpen }: IProps) {
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import React from 'react';
|
||||||
|
import Slide, { SlideProps } from '@material-ui/core/Slide';
|
||||||
|
import Snackbar from '@material-ui/core/Snackbar';
|
||||||
|
import MuiAlert, { AlertProps, Color as Severity } from '@material-ui/lab/Alert';
|
||||||
|
|
||||||
|
function removeToast(id: string) {
|
||||||
|
const container = document.querySelector(`#${id}`)!!;
|
||||||
|
ReactDOM.unmountComponentAtNode(container);
|
||||||
|
document.body.removeChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Transition(props: SlideProps) {
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return <Slide {...props} direction="up" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Alert(props: AlertProps) {
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return <MuiAlert elevation={6} variant="filled" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IToastProps{
|
||||||
|
message: string
|
||||||
|
severity: Severity
|
||||||
|
}
|
||||||
|
|
||||||
|
function Toast(props: IToastProps) {
|
||||||
|
const { message, severity } = props;
|
||||||
|
const [open, setOpen] = React.useState(true);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Snackbar
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
autoHideDuration={3000}
|
||||||
|
TransitionComponent={Transition}
|
||||||
|
message="I love snacks"
|
||||||
|
>
|
||||||
|
<MuiAlert elevation={6} variant="filled" onClose={handleClose} severity={severity}>
|
||||||
|
{message}
|
||||||
|
</MuiAlert>
|
||||||
|
</Snackbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function makeToast(message: string, severity: Severity) {
|
||||||
|
const id = Math.floor(Math.random() * 1000);
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.id = `alert-${id}`;
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
ReactDOM.render(<Toast message={message} severity={severity} />, container);
|
||||||
|
|
||||||
|
setTimeout(() => removeToast(container.id), 3500);
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import React from 'react';
|
||||||
|
import Page from './Page';
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
reader: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
margin: '0 auto',
|
||||||
|
width: '100%',
|
||||||
|
height: '100vh',
|
||||||
|
overflowX: 'scroll',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
pages: Array<IReaderPage>
|
||||||
|
setCurPage: React.Dispatch<React.SetStateAction<number>>
|
||||||
|
settings: IReaderSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function HorizontalReader(props: IProps) {
|
||||||
|
const { pages, settings, setCurPage } = props;
|
||||||
|
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.reader}>
|
||||||
|
{
|
||||||
|
pages.map((page) => (
|
||||||
|
<Page
|
||||||
|
key={page.index}
|
||||||
|
index={page.index}
|
||||||
|
src={page.src}
|
||||||
|
setCurPage={setCurPage}
|
||||||
|
settings={settings}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,23 +11,26 @@ import CircularProgress from '@material-ui/core/CircularProgress';
|
|||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import LazyLoad from 'react-lazyload';
|
import LazyLoad from 'react-lazyload';
|
||||||
import { IReaderSettings } from './ReaderNavBar';
|
|
||||||
|
|
||||||
const useStyles = (settings: IReaderSettings) => makeStyles({
|
const useStyles = (settings: IReaderSettings) => makeStyles({
|
||||||
loading: {
|
loading: {
|
||||||
margin: '100px auto',
|
margin: '100px auto',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
|
width: '100vw',
|
||||||
},
|
},
|
||||||
loadingImage: {
|
loadingImage: {
|
||||||
padding: settings.staticNav ? 'calc(50vh - 40px) calc(50vw - 340px)' : 'calc(50vh - 40px) calc(50vw - 40px)',
|
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
width: '200px',
|
width: '70vw',
|
||||||
|
padding: '50px calc(50% - 20px)',
|
||||||
backgroundColor: '#525252',
|
backgroundColor: '#525252',
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
image: {
|
image: {
|
||||||
display: 'block',
|
display: 'block',
|
||||||
marginBottom: settings.continuesPageGap ? '15px' : 0,
|
marginBottom: settings.readerType === 'ContinuesVertical' ? '15px' : 0,
|
||||||
|
minWidth: '50vw',
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '100%',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,7 +76,7 @@ function LazyImage(props: IProps) {
|
|||||||
|
|
||||||
if (imageSrc.length === 0) {
|
if (imageSrc.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className={classes.loadingImage}>
|
<div className={`${classes.image} ${classes.loadingImage}`}>
|
||||||
<CircularProgress thickness={5} />
|
<CircularProgress thickness={5} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -85,7 +88,6 @@ function LazyImage(props: IProps) {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
src={imageSrc}
|
src={imageSrc}
|
||||||
alt={`Page #${index}`}
|
alt={`Page #${index}`}
|
||||||
style={{ width: '100%', maxWidth: '95vw' }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -98,22 +100,12 @@ export default function Page(props: IProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '0 auto' }}>
|
<div style={{ margin: '0 auto' }}>
|
||||||
<LazyLoad
|
|
||||||
offset={window.innerHeight}
|
|
||||||
placeholder={(
|
|
||||||
<div className={classes.loading}>
|
|
||||||
<CircularProgress thickness={5} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
once
|
|
||||||
>
|
|
||||||
<LazyImage
|
<LazyImage
|
||||||
src={src}
|
src={src}
|
||||||
index={index}
|
index={index}
|
||||||
setCurPage={setCurPage}
|
setCurPage={setCurPage}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
/>
|
/>
|
||||||
</LazyLoad>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const useStyles = (settings: IReaderSettings) => makeStyles({
|
||||||
|
pageNumber: {
|
||||||
|
display: settings.showPageNumber ? 'block' : 'none',
|
||||||
|
position: 'fixed',
|
||||||
|
bottom: '50px',
|
||||||
|
right: settings.staticNav ? 'calc((100vw - 325px)/2)' : 'calc((100vw - 25px)/2)',
|
||||||
|
width: '50px',
|
||||||
|
textAlign: 'center',
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||||
|
borderRadius: '10px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
settings: IReaderSettings
|
||||||
|
curPage: number
|
||||||
|
pageCount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PageNumber(props: IProps) {
|
||||||
|
const { settings, curPage, pageCount } = props;
|
||||||
|
const classes = useStyles(settings)();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.pageNumber}>
|
||||||
|
{`${curPage + 1} / ${pageCount}`}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import Page from './Page';
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
reader: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
margin: '0 auto',
|
||||||
|
width: '100%',
|
||||||
|
height: '100vh',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function PagedReader(props: IReaderProps) {
|
||||||
|
const {
|
||||||
|
pages, settings, setCurPage, curPage,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
function nextPage() {
|
||||||
|
if (curPage < pages.length - 1) { setCurPage(curPage + 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function prevPage() {
|
||||||
|
if (curPage > 0) { setCurPage(curPage - 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyboardControl(e:KeyboardEvent) {
|
||||||
|
switch (e.key) {
|
||||||
|
case 'ArrowRight':
|
||||||
|
nextPage();
|
||||||
|
break;
|
||||||
|
case 'ArrowLeft':
|
||||||
|
prevPage();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickControl(e:MouseEvent) {
|
||||||
|
if (e.clientX > window.innerWidth / 2) {
|
||||||
|
nextPage();
|
||||||
|
} else {
|
||||||
|
prevPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener('keyup', keyboardControl, false);
|
||||||
|
document.addEventListener('click', clickControl);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keyup', keyboardControl);
|
||||||
|
document.removeEventListener('click', clickControl);
|
||||||
|
};
|
||||||
|
}, [curPage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.reader}>
|
||||||
|
<Page
|
||||||
|
key={curPage}
|
||||||
|
index={curPage}
|
||||||
|
src={pages[curPage].src}
|
||||||
|
setCurPage={setCurPage}
|
||||||
|
settings={settings}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import React from 'react';
|
||||||
|
import Page from './Page';
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
reader: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
margin: '0 auto',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function VerticalReader(props: IReaderProps) {
|
||||||
|
const { pages, settings, setCurPage } = props;
|
||||||
|
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.reader}>
|
||||||
|
{
|
||||||
|
pages.map((page) => (
|
||||||
|
<Page
|
||||||
|
key={page.index}
|
||||||
|
index={page.index}
|
||||||
|
src={page.src}
|
||||||
|
setCurPage={setCurPage}
|
||||||
|
settings={settings}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import MangaDetails from '../components/MangaDetails';
|
|||||||
import NavbarContext from '../context/NavbarContext';
|
import NavbarContext from '../context/NavbarContext';
|
||||||
import client from '../util/client';
|
import client from '../util/client';
|
||||||
import LoadingPlaceholder from '../components/LoadingPlaceholder';
|
import LoadingPlaceholder from '../components/LoadingPlaceholder';
|
||||||
|
import makeToast from '../components/Toast';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
root: {
|
root: {
|
||||||
@@ -58,6 +59,7 @@ export default function Manga() {
|
|||||||
|
|
||||||
const [manga, setManga] = useState<IManga>();
|
const [manga, setManga] = useState<IManga>();
|
||||||
const [chapters, setChapters] = useState<IChapter[]>([]);
|
const [chapters, setChapters] = useState<IChapter[]>([]);
|
||||||
|
const [fetchedChapters, setFetchedChapters] = useState(false);
|
||||||
const [chapterUpdateTriggerer, setChapterUpdateTriggerer] = useState(0);
|
const [chapterUpdateTriggerer, setChapterUpdateTriggerer] = useState(0);
|
||||||
|
|
||||||
function triggerChaptersUpdate() {
|
function triggerChaptersUpdate() {
|
||||||
@@ -76,11 +78,17 @@ export default function Manga() {
|
|||||||
}, [manga]);
|
}, [manga]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldFetchOnline = chapters.length > 0 && chapterUpdateTriggerer === 0;
|
const shouldFetchOnline = fetchedChapters && chapterUpdateTriggerer === 0;
|
||||||
client.get(`/api/v1/manga/${id}/chapters?onlineFetch=${shouldFetchOnline}`)
|
client.get(`/api/v1/manga/${id}/chapters?onlineFetch=${shouldFetchOnline}`)
|
||||||
.then((response) => response.data)
|
.then((response) => response.data)
|
||||||
.then((data) => setChapters(data));
|
.then((data) => {
|
||||||
}, [chapters.length, chapterUpdateTriggerer]);
|
if (data.length === 0 && fetchedChapters) {
|
||||||
|
makeToast('No chapters found', 'warning');
|
||||||
|
}
|
||||||
|
setChapters(data);
|
||||||
|
})
|
||||||
|
.then(() => setFetchedChapters(true));
|
||||||
|
}, [chapters.length, fetchedChapters, chapterUpdateTriggerer]);
|
||||||
|
|
||||||
// const itemContent = (index:any) => <InnerItem chapters={chapters} index={index} />;
|
// const itemContent = (index:any) => <InnerItem chapters={chapters} index={index} />;
|
||||||
const itemContent = (index:any) => (
|
const itemContent = (index:any) => (
|
||||||
@@ -99,7 +107,7 @@ export default function Manga() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<LoadingPlaceholder
|
<LoadingPlaceholder
|
||||||
shouldRender={chapters.length > 0}
|
shouldRender={chapters.length > 0 || fetchedChapters}
|
||||||
>
|
>
|
||||||
<Virtuoso
|
<Virtuoso
|
||||||
style={{ // override Virtuoso default values and set them with class
|
style={{ // override Virtuoso default values and set them with class
|
||||||
|
|||||||
@@ -10,36 +10,51 @@ import CircularProgress from '@material-ui/core/CircularProgress';
|
|||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import Page from '../components/Page';
|
import HorizontalReader from '../components/reader/HorizontalReader';
|
||||||
import ReaderNavBar, { defaultReaderSettings, IReaderSettings } from '../components/ReaderNavBar';
|
import Page from '../components/reader/Page';
|
||||||
|
import PageNumber from '../components/reader/PageNumber';
|
||||||
|
import PagedReader from '../components/reader/PagedReader';
|
||||||
|
import VerticalReader from '../components/reader/VerticalReader';
|
||||||
|
import ReaderNavBar, { defaultReaderSettings } from '../components/ReaderNavBar';
|
||||||
import NavbarContext from '../context/NavbarContext';
|
import NavbarContext from '../context/NavbarContext';
|
||||||
import client from '../util/client';
|
import client from '../util/client';
|
||||||
import useLocalStorage from '../util/useLocalStorage';
|
import useLocalStorage from '../util/useLocalStorage';
|
||||||
|
|
||||||
const useStyles = (settings: IReaderSettings) => makeStyles({
|
const useStyles = (settings: IReaderSettings) => makeStyles({
|
||||||
reader: {
|
root: {
|
||||||
display: 'flex',
|
width: settings.staticNav ? 'calc(100vw - 300px)' : '100vw',
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
margin: '0 auto',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
loading: {
|
loading: {
|
||||||
margin: '50px auto',
|
margin: '50px auto',
|
||||||
},
|
},
|
||||||
|
|
||||||
pageNumber: {
|
|
||||||
display: settings.showPageNumber ? 'block' : 'none',
|
|
||||||
position: 'fixed',
|
|
||||||
bottom: '50px',
|
|
||||||
right: settings.staticNav ? 'calc((100vw - 325px)/2)' : 'calc((100vw - 25px)/2)',
|
|
||||||
width: '50px',
|
|
||||||
textAlign: 'center',
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
|
||||||
borderRadius: '10px',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getReaderComponent = (readerType: ReaderType) => {
|
||||||
|
switch (readerType) {
|
||||||
|
case 'ContinuesVertical':
|
||||||
|
return VerticalReader;
|
||||||
|
break;
|
||||||
|
case 'Webtoon':
|
||||||
|
return VerticalReader;
|
||||||
|
break;
|
||||||
|
case 'SingleVertical':
|
||||||
|
return PagedReader;
|
||||||
|
break;
|
||||||
|
case 'SingleRTL':
|
||||||
|
return PagedReader;
|
||||||
|
break;
|
||||||
|
case 'SingleLTR':
|
||||||
|
return PagedReader;
|
||||||
|
break;
|
||||||
|
case 'ContinuesHorizontal':
|
||||||
|
return HorizontalReader;
|
||||||
|
default:
|
||||||
|
return VerticalReader;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
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, chapterCount: 0 });
|
const initialChapter = () => ({ pageCount: -1, index: -1, chapterCount: 0 });
|
||||||
|
|
||||||
@@ -102,20 +117,28 @@ export default function Reader() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pages = range(chapter.pageCount).map((index) => ({
|
||||||
|
index,
|
||||||
|
src: `${serverAddress}/api/v1/manga/${mangaId}/chapter/${chapterIndex}/page/${index}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ReaderComponent = getReaderComponent(settings.readerType);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.reader}>
|
<div className={classes.root}>
|
||||||
<div className={classes.pageNumber}>
|
<PageNumber
|
||||||
{`${curPage + 1} / ${chapter.pageCount}`}
|
settings={settings}
|
||||||
</div>
|
curPage={curPage}
|
||||||
{range(chapter.pageCount).map((index) => (
|
pageCount={chapter.pageCount}
|
||||||
<Page
|
/>
|
||||||
key={index}
|
<ReaderComponent
|
||||||
index={index}
|
pages={pages}
|
||||||
src={`${serverAddress}/api/v1/manga/${mangaId}/chapter/${chapterIndex}/page/${index}`}
|
pageCount={chapter.pageCount}
|
||||||
setCurPage={setCurPage}
|
setCurPage={setCurPage}
|
||||||
|
curPage={curPage}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
/>
|
/>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+27
@@ -87,3 +87,30 @@ interface INavbarOverride {
|
|||||||
status: boolean
|
status: boolean
|
||||||
value: any
|
value: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReaderType =
|
||||||
|
'ContinuesVertical'|
|
||||||
|
'Webtoon' |
|
||||||
|
'SingleVertical' |
|
||||||
|
'SingleRTL' |
|
||||||
|
'SingleLTR' |
|
||||||
|
'ContinuesHorizontal';
|
||||||
|
|
||||||
|
interface IReaderSettings{
|
||||||
|
staticNav: boolean
|
||||||
|
showPageNumber: boolean
|
||||||
|
readerType: ReaderType
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IReaderPage {
|
||||||
|
index: number
|
||||||
|
src: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IReaderProps {
|
||||||
|
pages: Array<IReaderPage>
|
||||||
|
pageCount: number
|
||||||
|
setCurPage: React.Dispatch<React.SetStateAction<number>>
|
||||||
|
curPage: number
|
||||||
|
settings: IReaderSettings
|
||||||
|
}
|
||||||
|
|||||||
@@ -1487,6 +1487,17 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.4.4"
|
"@babel/runtime" "^7.4.4"
|
||||||
|
|
||||||
|
"@material-ui/lab@^4.0.0-alpha.58":
|
||||||
|
version "4.0.0-alpha.58"
|
||||||
|
resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.58.tgz#c7ebb66f49863c5acbb20817163737caa299fafc"
|
||||||
|
integrity sha512-GKHlJqLxUeHH3L3dGQ48ZavYrqGOTXkFkiEiuYMAnAvXAZP4rhMIqeHOPXSUQan4Bd8QnafDcpovOSLnadDmKw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.4.4"
|
||||||
|
"@material-ui/utils" "^4.11.2"
|
||||||
|
clsx "^1.0.4"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-is "^16.8.0 || ^17.0.0"
|
||||||
|
|
||||||
"@material-ui/styles@^4.11.4":
|
"@material-ui/styles@^4.11.4":
|
||||||
version "4.11.4"
|
version "4.11.4"
|
||||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d"
|
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d"
|
||||||
|
|||||||
Reference in New Issue
Block a user