Add Resume Reading FAB Manga screen (#59)
* Added the Floating action button to Resume to the last read chapter - When a chapter is marked as read/unread the lastPageRead is changed to 1, Also fixed a bug which made the boolean expression appear in the ui Also removed some unused Styles Added the Resume/Start Button * changed a Resume button to a React compnent * Update src/screens/Manga.tsx Co-authored-by: Sascha Hahne <ntbm@users.noreply.github.com> * Update src/screens/Manga.tsx Co-authored-by: Sascha Hahne <ntbm@users.noreply.github.com> Co-authored-by: Sascha Hahne <ntbm@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import makeStyles from '@mui/styles/makeStyles';
|
|
||||||
import Card from '@mui/material/Card';
|
import Card from '@mui/material/Card';
|
||||||
import CardContent from '@mui/material/CardContent';
|
import CardContent from '@mui/material/CardContent';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
@@ -19,43 +18,6 @@ import MenuItem from '@mui/material/MenuItem';
|
|||||||
import BookmarkIcon from '@mui/icons-material/Bookmark';
|
import BookmarkIcon from '@mui/icons-material/Bookmark';
|
||||||
import client from 'util/client';
|
import client from 'util/client';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
|
||||||
root: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: 16,
|
|
||||||
},
|
|
||||||
bullet: {
|
|
||||||
display: 'inline-block',
|
|
||||||
margin: '0 2px',
|
|
||||||
transform: 'scale(0.8)',
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
pos: {
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
width: theme.spacing(7),
|
|
||||||
height: theme.spacing(7),
|
|
||||||
flex: '0 0 auto',
|
|
||||||
marginRight: 16,
|
|
||||||
},
|
|
||||||
card: {
|
|
||||||
margin: '10px',
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: theme.palette.action.hover,
|
|
||||||
transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
|
|
||||||
},
|
|
||||||
'&:active': {
|
|
||||||
backgroundColor: theme.palette.action.selected,
|
|
||||||
transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface IProps{
|
interface IProps{
|
||||||
chapter: IChapter
|
chapter: IChapter
|
||||||
triggerChaptersUpdate: () => void
|
triggerChaptersUpdate: () => void
|
||||||
@@ -63,7 +25,6 @@ interface IProps{
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function ChapterCard(props: IProps) {
|
export default function ChapterCard(props: IProps) {
|
||||||
const classes = useStyles();
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { chapter, triggerChaptersUpdate, downloadStatusString } = props;
|
const { chapter, triggerChaptersUpdate, downloadStatusString } = props;
|
||||||
|
|
||||||
@@ -84,6 +45,7 @@ export default function ChapterCard(props: IProps) {
|
|||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append(key, value);
|
formData.append(key, value);
|
||||||
|
if (key === 'read') { formData.append('lastPageRead', '1'); }
|
||||||
client.patch(`/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}`, formData)
|
client.patch(`/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}`, formData)
|
||||||
.then(() => triggerChaptersUpdate());
|
.then(() => triggerChaptersUpdate());
|
||||||
};
|
};
|
||||||
@@ -104,8 +66,25 @@ export default function ChapterCard(props: IProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<li>
|
<li>
|
||||||
<Card className={classes.card}>
|
<Card sx={{
|
||||||
<CardContent className={classes.root}>
|
margin: '10px',
|
||||||
|
':hover': {
|
||||||
|
backgroundColor: 'action.hover',
|
||||||
|
transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
|
||||||
|
},
|
||||||
|
':active': {
|
||||||
|
backgroundColor: 'action.selected',
|
||||||
|
transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Link
|
<Link
|
||||||
to={`/manga/${chapter.mangaId}/chapter/${chapter.index}`}
|
to={`/manga/${chapter.mangaId}/chapter/${chapter.index}`}
|
||||||
style={{
|
style={{
|
||||||
@@ -149,7 +128,7 @@ export default function ChapterCard(props: IProps) {
|
|||||||
{!chapter.bookmarked && 'Bookmark'}
|
{!chapter.bookmarked && 'Bookmark'}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={() => sendChange('read', !chapter.read)}>
|
<MenuItem onClick={() => sendChange('read', !chapter.read)}>
|
||||||
{`Mark as ${chapter.read && 'unread'} ${!chapter.read && 'read'}`}
|
{`Mark as ${chapter.read ? 'unread' : 'read'}`}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={() => sendChange('markPrevRead', true)}>
|
<MenuItem onClick={() => sendChange('markPrevRead', true)}>
|
||||||
Mark previous as Read
|
Mark previous as Read
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import React, { useEffect, useState, useContext } from 'react';
|
import React, { useEffect, useState, useContext } from 'react';
|
||||||
import { Theme } from '@mui/material/styles';
|
import { Theme } from '@mui/material/styles';
|
||||||
import makeStyles from '@mui/styles/makeStyles';
|
import makeStyles from '@mui/styles/makeStyles';
|
||||||
import { useParams } from 'react-router-dom';
|
import { Link, useParams } from 'react-router-dom';
|
||||||
import { Virtuoso } from 'react-virtuoso';
|
import { Virtuoso } from 'react-virtuoso';
|
||||||
import ChapterCard from 'components/ChapterCard';
|
import ChapterCard from 'components/ChapterCard';
|
||||||
import MangaDetails from 'components/MangaDetails';
|
import MangaDetails from 'components/MangaDetails';
|
||||||
@@ -16,6 +16,8 @@ import NavbarContext from 'components/context/NavbarContext';
|
|||||||
import client from 'util/client';
|
import client from 'util/client';
|
||||||
import LoadingPlaceholder from 'components/util/LoadingPlaceholder';
|
import LoadingPlaceholder from 'components/util/LoadingPlaceholder';
|
||||||
import makeToast from 'components/util/Toast';
|
import makeToast from 'components/util/Toast';
|
||||||
|
import { Fab } from '@mui/material';
|
||||||
|
import PlayArrow from '@mui/icons-material/PlayArrow';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
root: {
|
root: {
|
||||||
@@ -62,6 +64,7 @@ export default function Manga() {
|
|||||||
const [chapterUpdateTriggerer, setChapterUpdateTriggerer] = useState(0);
|
const [chapterUpdateTriggerer, setChapterUpdateTriggerer] = useState(0);
|
||||||
const [fetchedOnline, setFetchedOnline] = useState(false);
|
const [fetchedOnline, setFetchedOnline] = useState(false);
|
||||||
const [fetchedOffline, setFetchedOffline] = useState(false);
|
const [fetchedOffline, setFetchedOffline] = useState(false);
|
||||||
|
const [firstUnreadChapter, setFirstUnreadChapter] = useState<IChapter>();
|
||||||
|
|
||||||
const [, setWsClient] = useState<WebSocket>();
|
const [, setWsClient] = useState<WebSocket>();
|
||||||
const [{ queue }, setQueueState] = useState<IQueue>(initialQueue);
|
const [{ queue }, setQueueState] = useState<IQueue>(initialQueue);
|
||||||
@@ -129,6 +132,25 @@ export default function Manga() {
|
|||||||
});
|
});
|
||||||
}, [fetchedOnline, fetchedOffline, chapterUpdateTriggerer]);
|
}, [fetchedOnline, fetchedOffline, chapterUpdateTriggerer]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const a = [...chapters].reverse().find((chp) => !chp.read);
|
||||||
|
setFirstUnreadChapter(a);
|
||||||
|
}, [chapters]);
|
||||||
|
|
||||||
|
const ResumeFab = () => (firstUnreadChapter === undefined ? null
|
||||||
|
: (
|
||||||
|
<Fab
|
||||||
|
sx={{ position: 'fixed', bottom: '5vw', right: '5vw' }}
|
||||||
|
component={Link}
|
||||||
|
variant="extended"
|
||||||
|
color="primary"
|
||||||
|
to={`/manga/${id}/chapter/${firstUnreadChapter.index}/page/${firstUnreadChapter.lastPageRead}`}
|
||||||
|
>
|
||||||
|
<PlayArrow />
|
||||||
|
{firstUnreadChapter.index === 1 ? 'Start' : 'Resume' }
|
||||||
|
</Fab>
|
||||||
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<LoadingPlaceholder
|
<LoadingPlaceholder
|
||||||
@@ -158,7 +180,7 @@ export default function Manga() {
|
|||||||
overscan={window.innerHeight * 0.5}
|
overscan={window.innerHeight * 0.5}
|
||||||
/>
|
/>
|
||||||
</LoadingPlaceholder>
|
</LoadingPlaceholder>
|
||||||
|
<ResumeFab />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/typings.d.ts
vendored
1
src/typings.d.ts
vendored
@@ -70,6 +70,7 @@ interface IChapter {
|
|||||||
read: boolean
|
read: boolean
|
||||||
bookmarked: boolean
|
bookmarked: boolean
|
||||||
lastPageRead: number
|
lastPageRead: number
|
||||||
|
lastReadAt: number
|
||||||
index: number
|
index: number
|
||||||
fetchedAt: number
|
fetchedAt: number
|
||||||
chapterCount: number
|
chapterCount: number
|
||||||
|
|||||||
Reference in New Issue
Block a user