add: comic reader page up/down btn

This commit is contained in:
monoid 2023-06-01 23:07:58 +09:00
parent d961e8166d
commit e83a6bbe2b
11 changed files with 163 additions and 116 deletions

View File

@ -198,8 +198,7 @@ export const Headline = (prop: {
} }
}} }}
value={search} value={search}
> />
</StyledInputBase>
</StyledSearchBar> </StyledSearchBar>
{isLogin {isLogin
? ( ? (
@ -250,12 +249,10 @@ export const Headline = (prop: {
display: "flex", display: "flex",
flexFlow: "column", flexFlow: "column",
flexGrow: 1, flexGrow: 1,
padding: theme.spacing(3), padding: "0px",
marginTop: theme.spacing(6), marginTop: "64px",
}} }}
> >{prop.children}
<div style={{}}></div>
{prop.children}
</main> </main>
</div> </div>
); );

View File

@ -0,0 +1,5 @@
import { styled } from "@mui/material";
export const PagePad = styled("div")(({theme})=>({
padding: theme.spacing(3)
}))

View File

@ -2,12 +2,15 @@ import { ArrowBack as ArrowBackIcon } from "@mui/icons-material";
import { Typography } from "@mui/material"; import { Typography } from "@mui/material";
import React from "react"; import React from "react";
import { BackItem, CommonMenuList, Headline, NavList } from "../component/mod"; import { BackItem, CommonMenuList, Headline, NavList } from "../component/mod";
import { PagePad } from "../component/pagepad";
export const NotFoundPage = () => { export const NotFoundPage = () => {
const menu = CommonMenuList(); const menu = CommonMenuList();
return ( return (
<Headline menu={menu}> <Headline menu={menu}>
<Typography variant="h2">404 Not Found</Typography> <PagePad>
<Typography variant="h2">404 Not Found</Typography>
</PagePad>
</Headline> </Headline>
); );
}; };

View File

@ -6,6 +6,7 @@ import { LoadingCircle } from "../component/loading";
import { CommonMenuList, ContentInfo, Headline } from "../component/mod"; import { CommonMenuList, ContentInfo, Headline } from "../component/mod";
import { NotFoundPage } from "./404"; import { NotFoundPage } from "./404";
import { getPresenter } from "./reader/reader"; import { getPresenter } from "./reader/reader";
import { PagePad } from "../component/pagepad";
export const makeContentInfoUrl = (id: number) => `/doc/${id}`; export const makeContentInfoUrl = (id: number) => `/doc/${id}`;
export const makeComicReaderUrl = (id: number) => `/doc/${id}/reader`; export const makeComicReaderUrl = (id: number) => `/doc/${id}/reader`;
@ -79,7 +80,7 @@ export const DocumentAbout = (prop?: {}) => {
if (match == null) { if (match == null) {
throw new Error("unreachable"); throw new Error("unreachable");
} }
const id = Number.parseInt(match.id); const id = Number.parseInt(match.id ?? "NaN");
const [info, setInfo] = useState<DocumentState>({ doc: undefined, notfound: false }); const [info, setInfo] = useState<DocumentState>({ doc: undefined, notfound: false });
const menu_list = (link?: string) => <CommonMenuList url={link}></CommonMenuList>; const menu_list = (link?: string) => <CommonMenuList url={link}></CommonMenuList>;
@ -95,25 +96,33 @@ export const DocumentAbout = (prop?: {}) => {
if (isNaN(id)) { if (isNaN(id)) {
return ( return (
<Headline menu={menu_list()}> <Headline menu={menu_list()}>
<PagePad>
<Typography variant="h2">Oops. Invalid ID</Typography> <Typography variant="h2">Oops. Invalid ID</Typography>
</PagePad>
</Headline> </Headline>
); );
} else if (info.notfound) { } else if (info.notfound) {
return ( return (
<Headline menu={menu_list()}> <Headline menu={menu_list()}>
<PagePad>
<Typography variant="h2">Content has been removed.</Typography> <Typography variant="h2">Content has been removed.</Typography>
</PagePad>
</Headline> </Headline>
); );
} else if (info.doc === undefined) { } else if (info.doc === undefined) {
return ( return (
<Headline menu={menu_list()}> <Headline menu={menu_list()}>
<PagePad>
<LoadingCircle /> <LoadingCircle />
</PagePad>
</Headline> </Headline>
); );
} else { } else {
return ( return (
<Headline menu={menu_list()}> <Headline menu={menu_list()}>
<PagePad>
<ContentInfo document={info.doc}></ContentInfo> <ContentInfo document={info.doc}></ContentInfo>
</PagePad>
</Headline> </Headline>
); );
} }

View File

@ -3,6 +3,7 @@ import { Stack } from "@mui/material";
import React, { useContext, useEffect, useState } from "react"; import React, { useContext, useEffect, useState } from "react";
import { CommonMenuList, Headline } from "../component/mod"; import { CommonMenuList, Headline } from "../component/mod";
import { UserContext } from "../state"; import { UserContext } from "../state";
import { PagePad } from "../component/pagepad";
const useStyles = (theme: Theme) => ({ const useStyles = (theme: Theme) => ({
paper: { paper: {
@ -127,6 +128,7 @@ export function DifferencePage() {
const menu = CommonMenuList(); const menu = CommonMenuList();
return ( return (
<Headline menu={menu}> <Headline menu={menu}>
<PagePad>
{(ctx.username == "admin") {(ctx.username == "admin")
? ( ? (
<div> <div>
@ -136,6 +138,7 @@ export function DifferencePage() {
</div> </div>
) )
: <Typography variant="h2">Not Allowed : please login as an admin</Typography>} : <Typography variant="h2">Not Allowed : please login as an admin</Typography>}
</PagePad>
</Headline> </Headline>
); );
} }

View File

@ -8,6 +8,7 @@ import { toQueryString } from "../accessor/util";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import { QueryStringToMap } from "../accessor/util"; import { QueryStringToMap } from "../accessor/util";
import { useIsElementInViewport } from "./reader/reader"; import { useIsElementInViewport } from "./reader/reader";
import { PagePad } from "../component/pagepad";
export type GalleryProp = { export type GalleryProp = {
option?: QueryListOption; option?: QueryListOption;
@ -70,8 +71,7 @@ export const GalleryInfo = (props: GalleryProp) => {
)} )}
{props.option.allow_tag !== undefined {props.option.allow_tag !== undefined
&& props.option.allow_tag.map(x => ( && props.option.allow_tag.map(x => (
<TagChip key={x} tagname={decodeURIComponent(x)} label={decodeURIComponent(x)}> <TagChip key={x} tagname={decodeURIComponent(x)} label={decodeURIComponent(x)}/>
</TagChip>
))} ))}
</Box> </Box>
)} )}
@ -126,7 +126,9 @@ export const Gallery = () => {
option.limit = typeof query["limit"] === "string" ? parseInt(query["limit"]) : undefined; option.limit = typeof query["limit"] === "string" ? parseInt(query["limit"]) : undefined;
return ( return (
<Headline menu={menu_list}> <Headline menu={menu_list}>
<PagePad>
<GalleryInfo diff={location.search} option={query}></GalleryInfo> <GalleryInfo diff={location.search} option={query}></GalleryInfo>
</PagePad>
</Headline> </Headline>
); );
}; };

View File

@ -16,6 +16,7 @@ import { useNavigate } from "react-router-dom";
import { CommonMenuList, Headline } from "../component/mod"; import { CommonMenuList, Headline } from "../component/mod";
import { UserContext } from "../state"; import { UserContext } from "../state";
import { doLogin as doSessionLogin } from "../state"; import { doLogin as doSessionLogin } from "../state";
import { PagePad } from "../component/pagepad";
export const LoginPage = () => { export const LoginPage = () => {
const theme = useTheme(); const theme = useTheme();
@ -48,39 +49,41 @@ export const LoginPage = () => {
const menu = CommonMenuList(); const menu = CommonMenuList();
return ( return (
<Headline menu={menu}> <Headline menu={menu}>
<Paper style={{ width: theme.spacing(40), padding: theme.spacing(4), alignSelf: "center" }}> <PagePad>
<Typography variant="h4">Login</Typography> <Paper style={{ width: theme.spacing(40), padding: theme.spacing(4), alignSelf: "center" }}>
<div style={{ minHeight: theme.spacing(2) }}></div> <Typography variant="h4">Login</Typography>
<form style={{ display: "flex", flexFlow: "column", alignItems: "stretch" }}>
<TextField
label="username"
onChange={(e) => setUserLoginInfo({ ...userLoginInfo, username: e.target.value ?? "" })}
>
</TextField>
<TextField
label="password"
type="password"
onKeyDown={(e) => {
if (e.key === "Enter") doLogin();
}}
onChange={(e) => setUserLoginInfo({ ...userLoginInfo, password: e.target.value ?? "" })}
/>
<div style={{ minHeight: theme.spacing(2) }}></div> <div style={{ minHeight: theme.spacing(2) }}></div>
<div style={{ display: "flex" }}> <form style={{ display: "flex", flexFlow: "column", alignItems: "stretch" }}>
<Button onClick={doLogin}>login</Button> <TextField
<Button>signin</Button> label="username"
</div> onChange={(e) => setUserLoginInfo({ ...userLoginInfo, username: e.target.value ?? "" })}
</form> >
</Paper> </TextField>
<Dialog open={openDialog.open} onClose={handleDialogClose}> <TextField
<DialogTitle>Login Failed</DialogTitle> label="password"
<DialogContent> type="password"
<DialogContentText>detail : {openDialog.message}</DialogContentText> onKeyDown={(e) => {
</DialogContent> if (e.key === "Enter") doLogin();
<DialogActions> }}
<Button onClick={handleDialogClose} color="primary" autoFocus>Close</Button> onChange={(e) => setUserLoginInfo({ ...userLoginInfo, password: e.target.value ?? "" })}
</DialogActions> />
</Dialog> <div style={{ minHeight: theme.spacing(2) }}></div>
<div style={{ display: "flex" }}>
<Button onClick={doLogin}>login</Button>
<Button>signin</Button>
</div>
</form>
</Paper>
<Dialog open={openDialog.open} onClose={handleDialogClose}>
<DialogTitle>Login Failed</DialogTitle>
<DialogContent>
<DialogContentText>detail : {openDialog.message}</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleDialogClose} color="primary" autoFocus>Close</Button>
</DialogActions>
</Dialog>
</PagePad>
</Headline> </Headline>
); );
}; };

View File

@ -16,6 +16,7 @@ import {
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { CommonMenuList, Headline } from "../component/mod"; import { CommonMenuList, Headline } from "../component/mod";
import { UserContext } from "../state"; import { UserContext } from "../state";
import { PagePad } from "../component/pagepad";
const useStyles = (theme: Theme) => ({ const useStyles = (theme: Theme) => ({
paper: { paper: {
@ -77,71 +78,74 @@ export function ProfilePage() {
}; };
return ( return (
<Headline menu={menu}> <Headline menu={menu}>
<Paper /*className={classes.paper}*/> <PagePad>
<Grid container direction="column" alignItems="center">
<Grid item> <Paper /*className={classes.paper}*/>
<Typography variant="h4">{userctx.username}</Typography> <Grid container direction="column" alignItems="center">
<Grid item>
<Typography variant="h4">{userctx.username}</Typography>
</Grid>
<Divider></Divider>
<Grid item>
Permission
</Grid>
<Grid item>
{permission_list.length == 0 ? "-" : permission_list}
</Grid>
<Grid item>
<Button onClick={handle_open}>Password Reset</Button>
</Grid>
</Grid> </Grid>
<Divider></Divider> </Paper>
<Grid item> <Dialog open={pw_open} onClose={handle_close}>
Permission <DialogTitle>Password Reset</DialogTitle>
</Grid> <DialogContent>
<Grid item> <Typography>type the old and new password</Typography>
{permission_list.length == 0 ? "-" : permission_list} <div /*className={classes.formfield}*/>
</Grid> {(!isElectronContent) && (
<Grid item> <TextField
<Button onClick={handle_open}>Password Reset</Button> autoFocus
</Grid> margin="dense"
</Grid> type="password"
</Paper> label="old password"
<Dialog open={pw_open} onClose={handle_close}> value={oldpw}
<DialogTitle>Password Reset</DialogTitle> onChange={(e) => setOldpw(e.target.value)}
<DialogContent> >
<Typography>type the old and new password</Typography> </TextField>
<div /*className={classes.formfield}*/> )}
{(!isElectronContent) && (
<TextField <TextField
autoFocus
margin="dense" margin="dense"
type="password" type="password"
label="old password" label="new password"
value={oldpw} value={newpw}
onChange={(e) => setOldpw(e.target.value)} onChange={e => setNewpw(e.target.value)}
> >
</TextField> </TextField>
)} <TextField
<TextField margin="dense"
margin="dense" type="password"
type="password" label="new password check"
label="new password" value={newpwch}
value={newpw} onChange={e => setNewpwch(e.target.value)}
onChange={e => setNewpw(e.target.value)} >
> </TextField>
</TextField> </div>
<TextField </DialogContent>
margin="dense" <DialogActions>
type="password" <Button onClick={handle_close} color="primary">Cancel</Button>
label="new password check" <Button onClick={handle_ok} color="primary">Ok</Button>
value={newpwch} </DialogActions>
onChange={e => setNewpwch(e.target.value)} </Dialog>
> <Dialog open={msg_dialog.opened} onClose={() => set_msg_dialog({ opened: false, msg: "" })}>
</TextField> <DialogTitle>Alert!</DialogTitle>
</div> <DialogContent>
</DialogContent> <DialogContentText>{msg_dialog.msg}</DialogContentText>
<DialogActions> </DialogContent>
<Button onClick={handle_close} color="primary">Cancel</Button> <DialogActions>
<Button onClick={handle_ok} color="primary">Ok</Button> <Button onClick={() => set_msg_dialog({ opened: false, msg: "" })} color="primary">Close</Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
<Dialog open={msg_dialog.opened} onClose={() => set_msg_dialog({ opened: false, msg: "" })}> </PagePad>
<DialogTitle>Alert!</DialogTitle>
<DialogContent>
<DialogContentText>{msg_dialog.msg}</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => set_msg_dialog({ opened: false, msg: "" })} color="primary">Close</Button>
</DialogActions>
</Dialog>
</Headline> </Headline>
); );
} }

View File

@ -1,4 +1,4 @@
import { Typography, useTheme } from "@mui/material"; import { Typography, styled } from "@mui/material";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { Document } from "../../accessor/document"; import { Document } from "../../accessor/document";
@ -13,6 +13,21 @@ export type PresentableTag = {
tags: string[]; tags: string[];
}; };
const ViewMain = styled("div")(({ theme }) => ({
overflow: "hidden",
width: "100%",
height: "calc(100vh - 64px)",
position: "relative",
}));
const CurrentView = styled("img")(({theme})=>({
maxWidth: "100%",
maxHeight: "100%",
top:"50%",
left:"50%",
transform: "translate(-50%,-50%)",
position: "absolute"
}));
export const ComicReader = (props: { doc: Document }) => { export const ComicReader = (props: { doc: Document }) => {
const additional = props.doc.additional; const additional = props.doc.additional;
const [curPage, setCurPage] = useState(0); const [curPage, setCurPage] = useState(0);
@ -21,13 +36,13 @@ export const ComicReader = (props: { doc: Document }) => {
return <Typography>Error. DB error. page restriction</Typography>; return <Typography>Error. DB error. page restriction</Typography>;
} }
const PageDown = () => setCurPage(Math.max(curPage - 1, 0)); const PageDown = () => setCurPage(Math.max(curPage - 1, 0));
const PageUP = () => setCurPage(Math.min(curPage + 1, page - 1)); const PageUp = () => setCurPage(Math.min(curPage + 1, page - 1));
const page: number = additional["page"] as number; const page: number = additional["page"] as number;
const onKeyUp = (e: KeyboardEvent) => { const onKeyUp = (e: KeyboardEvent) => {
if (e.code === "ArrowLeft") { if (e.code === "ArrowLeft") {
PageDown(); PageDown();
} else if (e.code === "ArrowRight") { } else if (e.code === "ArrowRight") {
PageUP(); PageUp();
} }
}; };
useEffect(() => { useEffect(() => {
@ -38,14 +53,14 @@ export const ComicReader = (props: { doc: Document }) => {
}); });
// theme.mixins.toolbar.minHeight; // theme.mixins.toolbar.minHeight;
return ( return (
<div style={{ overflow: "hidden", alignSelf: "center" }}> <ViewMain>
<img <div onClick={PageDown} style={{left:"0", width: "50%", position:"absolute", height: "100%", zIndex:100}}></div>
onClick={PageUP} <CurrentView
onClick={PageUp}
src={`/api/doc/${props.doc.id}/comic/${curPage}`} src={`/api/doc/${props.doc.id}/comic/${curPage}`}
style={{ maxWidth: "100%", maxHeight: "calc(100vh - 64px)" }} ></CurrentView>
> <div onClick={PageUp} style={{right:"0", width: "50%", position:"absolute", height: "100%", zIndex:100}}></div>
</img> </ViewMain>
</div>
); );
}; };

View File

@ -2,14 +2,17 @@ import { ArrowBack as ArrowBackIcon } from "@mui/icons-material";
import { Paper, Typography } from "@mui/material"; import { Paper, Typography } from "@mui/material";
import React from "react"; import React from "react";
import { BackItem, CommonMenuList, Headline, NavList } from "../component/mod"; import { BackItem, CommonMenuList, Headline, NavList } from "../component/mod";
import { PagePad } from "../component/pagepad";
export const SettingPage = () => { export const SettingPage = () => {
const menu = CommonMenuList(); const menu = CommonMenuList();
return ( return (
<Headline menu={menu}> <Headline menu={menu}>
<Paper> <PagePad>
<Typography variant="h2">Setting</Typography> <Paper>
</Paper> <Typography variant="h2">Setting</Typography>
</Paper>
</PagePad>
</Headline> </Headline>
); );
}; };

View File

@ -3,6 +3,7 @@ import { DataGrid, GridColDef } from "@mui/x-data-grid";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { LoadingCircle } from "../component/loading"; import { LoadingCircle } from "../component/loading";
import { CommonMenuList, Headline } from "../component/mod"; import { CommonMenuList, Headline } from "../component/mod";
import { PagePad } from "../component/pagepad";
type TagCount = { type TagCount = {
tag_name: string; tag_name: string;
@ -67,7 +68,9 @@ export const TagsPage = () => {
const menu = CommonMenuList(); const menu = CommonMenuList();
return ( return (
<Headline menu={menu}> <Headline menu={menu}>
<TagTable></TagTable> <PagePad>
<TagTable></TagTable>
</PagePad>
</Headline> </Headline>
); );
}; };