mui upgrade

This commit is contained in:
monoid 2022-04-20 01:07:15 +09:00
parent 0081229f86
commit 7a3fdce4dc
25 changed files with 336 additions and 455 deletions

View File

@ -6,11 +6,11 @@
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com;
font-src 'self' fonts.gstatic.com"> font-src 'self' fonts.gstatic.com">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/dist/css/style.css"> <link rel="stylesheet" href="/dist/bundle.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script src="/dist/js/bundle.js"></script> <script src="/dist/bundle.js"></script>
</body> </body>
</html> </html>

View File

@ -1,11 +0,0 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-typescript",
"@babel/preset-react"
],
"plugins": [
"@babel/proposal-class-properties",
"@babel/proposal-object-rest-spread"
]
}

View File

@ -15,6 +15,8 @@ export class FetchFailError implements Error{
} }
} }
export class ClientDocumentAccessor implements DocumentAccessor{ export class ClientDocumentAccessor implements DocumentAccessor{
search: (search_word: string) => Promise<Document[]>;
addList: (content_list: DocumentBody[]) => Promise<number[]>;
async findByPath(basepath: string, filename?: string): Promise<Document[]>{ async findByPath(basepath: string, filename?: string): Promise<Document[]>{
throw new Error("not allowed"); throw new Error("not allowed");
}; };

View File

@ -1,46 +1,50 @@
import React, { createContext, useEffect, useRef, useState } from 'react'; import React, { createContext, useEffect, useRef, useState } from 'react';
import ReactDom from 'react-dom'; import ReactDom from 'react-dom';
import {BrowserRouter, Redirect, Route, Switch as RouterSwitch} from 'react-router-dom'; import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
import { Gallery, DocumentAbout, LoginPage, NotFoundPage, ProfilePage,DifferencePage, SettingPage} from './page/mod'; import { Gallery, DocumentAbout, LoginPage, NotFoundPage, ProfilePage, DifferencePage, SettingPage } from './page/mod';
import {getInitialValue, UserContext} from './state'; import { getInitialValue, UserContext } from './state';
import { ThemeProvider, createTheme } from '@mui/material';
import './css/style.css'; import './css/style.css';
const theme = createTheme();
const App = () => { const App = () => {
const [user,setUser] = useState(""); const [user, setUser] = useState("");
const [userPermission,setUserPermission] = useState<string[]>([]); const [userPermission, setUserPermission] = useState<string[]>([]);
(async ()=>{ (async () => {
const {username,permission} = await getInitialValue(); const { username, permission } = await getInitialValue();
if(username !== user){ if (username !== user) {
setUser(username); setUser(username);
setUserPermission(permission); setUserPermission(permission);
} }
})(); })();
//useEffect(()=>{}); //useEffect(()=>{});
return ( return (
<UserContext.Provider value={{ <UserContext.Provider value={{
username:user, username: user,
setUsername:setUser, setUsername: setUser,
permission:userPermission, permission: userPermission,
setPermission:setUserPermission}}> setPermission: setUserPermission
<BrowserRouter> }}>
<RouterSwitch> <ThemeProvider theme={theme}>
<Route path="/" exact render={()=><Redirect to='/search?'/>}></Route> <BrowserRouter>
<Route path="/search" render={()=><Gallery />}></Route> <Routes>
<Route path="/doc" render={(prop)=><DocumentAbout {...prop}/>}></Route> <Route path="/" element={<Navigate replace to='/search?' />} />
<Route path="/login" render={()=><LoginPage></LoginPage>}/> <Route path="/search" element={<Gallery />} />
<Route path="/profile" component={ProfilePage}></Route> <Route path="/doc" element={<DocumentAbout />}></Route>
<Route path="/difference" component={DifferencePage}></Route> <Route path="/login" element={<LoginPage></LoginPage>} />
<Route path="/setting" component={SettingPage}></Route> <Route path="/profile" element={<ProfilePage />}></Route>
<Route> <Route path="/difference" element={<DifferencePage />}></Route>
<NotFoundPage/> <Route path="/setting" element={<SettingPage />}></Route>
</Route> <Route path="*" element={<NotFoundPage />} />
</RouterSwitch> </Routes>
</BrowserRouter> </BrowserRouter>
</UserContext.Provider>); </ThemeProvider>
</UserContext.Provider>);
}; };
ReactDom.render( ReactDom.render(
<App/>, <App />,
document.getElementById("root") document.getElementById("root")
) );

33
src/client/build.ts Normal file
View File

@ -0,0 +1,33 @@
import esbuild from 'esbuild';
async function main() {
try {
const result = await esbuild.build({
entryPoints: ['app.tsx'],
bundle: true,
outfile: '../../dist/bundle.js',
platform: 'browser',
sourcemap: true,
minify: true,
target: ['chrome100', 'firefox100'],
watch: {
onRebuild: async (err, _result) => {
if (err) {
console.error('watch build failed: ',err);
}
else{
console.log('watch build success');
}
}
}
});
console.log("watching...");
return result;
} catch (error) {
console.error(error);
process.exit(1);
}
}
main().then((res) => {
});

View File

@ -1,23 +1,16 @@
import React, { useState, useEffect } from 'react'; import React, { } from 'react';
import { Redirect, Route, Switch, useHistory, useRouteMatch, match as MatchType, Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
import { Document } from '../accessor/document'; import { Document } from '../accessor/document';
import { LoadingCircle } from '../component/loading';
import { Link, Paper, makeStyles, Theme, Box, useTheme, Typography } from '@material-ui/core'; import { Link, Paper, Theme, Box, useTheme, Typography } from '@mui/material';
import { ThumbnailContainer } from '../page/reader/reader'; import { ThumbnailContainer } from '../page/reader/reader';
import { TagChip } from '../component/tagchip'; import { TagChip } from '../component/tagchip';
import { ComicReader } from '../page/reader/comic';
export const makeContentInfoUrl = (id: number) => `/doc/${id}`; export const makeContentInfoUrl = (id: number) => `/doc/${id}`;
export const makeContentReaderUrl = (id: number) => `/doc/${id}/reader`; export const makeContentReaderUrl = (id: number) => `/doc/${id}/reader`;
const useStyles = makeStyles((theme: Theme) => ({ const useStyles = ((theme: Theme) => ({
root: {
display: "flex",
[theme.breakpoints.down("sm")]: {
flexDirection: "column",
alignItems: "center",
}
},
thumbnail_content: { thumbnail_content: {
maxHeight: '400px', maxHeight: '400px',
maxWidth: 'min(400px, 100vw)', maxWidth: 'min(400px, 100vw)',
@ -43,26 +36,26 @@ const useStyles = makeStyles((theme: Theme) => ({
overflowY: 'hidden', overflowY: 'hidden',
alignItems: 'baseline', alignItems: 'baseline',
}, },
short_subinfoContainer:{ short_subinfoContainer: {
[theme.breakpoints.down("md")]:{ [theme.breakpoints.down("md")]: {
display:'none', display: 'none',
}, },
}, },
short_root:{ short_root: {
overflowY:'hidden', overflowY: 'hidden',
display:'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
[theme.breakpoints.up("sm")]:{ [theme.breakpoints.up("sm")]: {
height:200, height: 200,
flexDirection: 'row', flexDirection: 'row',
}, },
}, },
short_thumbnail_anchor:{ short_thumbnail_anchor: {
background: '#272733', background: '#272733',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
[theme.breakpoints.up("sm")]:{ [theme.breakpoints.up("sm")]: {
width: theme.spacing(25), width: theme.spacing(25),
height: theme.spacing(25), height: theme.spacing(25),
flexShrink: 0, flexShrink: 0,
@ -84,66 +77,75 @@ export const ContentInfo = (props: {
infoContainer?: string, infoContainer?: string,
subinfoContainer?: string subinfoContainer?: string
}, },
gallery?:string, gallery?: string,
short?:boolean short?: boolean
}) => { }) => {
const classes = useStyles(); //const classes = useStyles();
const theme = useTheme(); const theme = useTheme();
const document = props.document; const document = props.document;
const propclasses = props.classes ?? {}; const propclasses = props.classes ?? {};
const rootName = props.short ? classes.short_root : classes.root; /*const rootName = props.short ? classes.short_root : classes.root;
const thumbnail_anchor = props.short ? classes.short_thumbnail_anchor : ""; const thumbnail_anchor = props.short ? classes.short_thumbnail_anchor : "";
const thumbnail_content = props.short ? classes.short_thumbnail_content : const thumbnail_content = props.short ? classes.short_thumbnail_content :
classes.thumbnail_content; classes.thumbnail_content;
const subinfoContainer = props.short ? classes.short_subinfoContainer : const subinfoContainer = props.short ? classes.short_subinfoContainer :
classes.subinfoContainer; classes.subinfoContainer;*/
return (<Paper className={propclasses.root ?? rootName} elevation={4}> const url = props.gallery === undefined ? makeContentReaderUrl(document.id) : makeContentInfoUrl(document.id);
<Link className={propclasses.thumbnail_anchor ?? thumbnail_anchor} component={RouterLink} to={{ return (<Paper sx={{
pathname:makeContentReaderUrl(document.id) display: "flex",
[theme.breakpoints.down("sm")]: {
flexDirection: "column",
alignItems: "center",
}
}} elevation={4}>
<Link /*className={propclasses.thumbnail_anchor ?? thumbnail_anchor}*/ component={RouterLink} to={{
pathname: makeContentReaderUrl(document.id)
}}> }}>
{document.deleted_at === null ? {document.deleted_at === null ?
(<ThumbnailContainer content={document} (<ThumbnailContainer content={document}
className={propclasses.thumbnail_content ?? thumbnail_content}/>) style={{
: (<Typography className={propclasses.thumbnail_content ?? thumbnail_content} variant='h4'>Deleted</Typography>)} maxHeight: '400px',
maxWidth: 'min(400px, 100vw)',
}}/>)
: (<Typography/* className={propclasses.thumbnail_content ?? thumbnail_content} */ variant='h4'>Deleted</Typography>)}
</Link> </Link>
<Box className={propclasses.infoContainer ?? classes.infoContainer}> <Box /*className={propclasses.infoContainer ?? classes.infoContainer}*/>
<Link variant='h5' color='inherit' component={RouterLink} to={{ <Link variant='h5' color='inherit' component={RouterLink} to={{pathname: url}}
pathname: props.gallery === undefined ? makeContentReaderUrl(document.id) : makeContentInfoUrl(document.id), /*className={propclasses.title ?? classes.title}*/>
}}
className={propclasses.title ?? classes.title}>
{document.title} {document.title}
</Link> </Link>
<Box className={propclasses.subinfoContainer ?? subinfoContainer}> <Box /*className={propclasses.subinfoContainer ?? subinfoContainer}*/>
{props.short ? (<Box className={propclasses.tag_list ?? classes.tag_list}>{document.tags.map(x => {props.short ? (<Box /*className={propclasses.tag_list ?? classes.tag_list}*/>{document.tags.map(x =>
(<TagChip key={x} label={x} clickable tagname={x} size="small"></TagChip>) (<TagChip key={x} label={x} clickable tagname={x} size="small"></TagChip>)
)}</Box>) : ( )}</Box>) : (
<ComicDetailTag tags={document.tags} classes={({tag_list:classes.tag_list})}></ComicDetailTag>) <ComicDetailTag tags={document.tags}/* classes={({tag_list:classes.tag_list})}*/ ></ComicDetailTag>)
} }
</Box> </Box>
</Box> </Box>
</Paper>); </Paper>);
} }
function ComicDetailTag(prop:{tags:string[],classes:{ function ComicDetailTag(prop: {
tags: string[]/*classes:{
tag_list:string tag_list:string
}}){ }*/}) {
let allTag = prop.tags; let allTag = prop.tags;
const tagKind = ["artist","group","series","type","character"]; const tagKind = ["artist", "group", "series", "type", "character"];
let tagTable:{[kind:string]:string[]} = {}; let tagTable: { [kind: string]: string[] } = {};
for(const kind of tagKind){ for (const kind of tagKind) {
const tags = allTag.filter(x => x.startsWith(kind+":")).map(x => x.slice(kind.length+1)); const tags = allTag.filter(x => x.startsWith(kind + ":")).map(x => x.slice(kind.length + 1));
tagTable[kind] = tags; tagTable[kind] = tags;
allTag = allTag.filter(x => !x.startsWith(kind+":")); allTag = allTag.filter(x => !x.startsWith(kind + ":"));
} }
return (<React.Fragment> return (<React.Fragment>
{tagKind.map(key=>( {tagKind.map(key => (
<React.Fragment key={key}> <React.Fragment key={key}>
<Typography variant='subtitle1'>{key}</Typography> <Typography variant='subtitle1'>{key}</Typography>
<Box>{tagTable[key].length !== 0 ? tagTable[key].join(", ") : "N/A"}</Box> <Box>{tagTable[key].length !== 0 ? tagTable[key].join(", ") : "N/A"}</Box>
</React.Fragment> </React.Fragment>
))} ))}
<Typography variant='subtitle1'>Tags</Typography> <Typography variant='subtitle1'>Tags</Typography>
<Box className={prop.classes.tag_list}> <Box /*lassName={prop.classes.tag_list}*/>
{allTag.map(x => (<TagChip key={x} label={x} clickable tagname={x} size="small"></TagChip>))} {allTag.map(x => (<TagChip key={x} label={x} clickable tagname={x} size="small"></TagChip>))}
</Box> </Box>
</React.Fragment>); </React.Fragment>);

View File

@ -1,123 +1,77 @@
import ReactDom from 'react-dom'; import React, { useContext, useState } from 'react';
import React, { ReactNode, useContext, useState } from 'react';
import { import {
Button, CssBaseline, Divider, IconButton, List, ListItem, Drawer, Button, CssBaseline, Divider, IconButton, List, ListItem, Drawer,
AppBar, Toolbar, Typography, InputBase, ListItemIcon, ListItemText, Menu, MenuItem, AppBar, Toolbar, Typography, InputBase, ListItemIcon, ListItemText, Menu, MenuItem,
Hidden, Tooltip, Link Hidden, Tooltip, Link, styled
} from '@material-ui/core'; } from '@mui/material';
import { makeStyles, Theme, useTheme, fade } from '@material-ui/core/styles'; import { alpha, Theme, useTheme } from '@mui/material/styles';
import { ChevronLeft, ChevronRight, Menu as MenuIcon, Search as SearchIcon, AccountCircle import {
} from '@material-ui/icons'; ChevronLeft, ChevronRight, Menu as MenuIcon, Search as SearchIcon, AccountCircle
} from '@mui/icons-material';
import { Link as RouterLink, useRouteMatch } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
import { doLogout, UserContext } from '../state'; import { doLogout, UserContext } from '../state';
const drawerWidth = 240; const drawerWidth = 270;
const useStyles = makeStyles((theme: Theme) => ({ const DrawerHeader = styled('div')(({ theme }) => ({
root: { ...theme.mixins.toolbar
display: 'flex' }));
const StyledDrawer = styled(Drawer)(({ theme }) => ({
flexShrink: 0,
whiteSpace: "nowrap",
[theme.breakpoints.up("sm")]: {
width: drawerWidth,
}, },
appBar: { }
zIndex: theme.zIndex.drawer + 1, ));
transition: theme.transitions.create(['width', 'margin'], { const StyledSearchBar = styled('div')(({ theme }) => ({
easing: theme.transitions.easing.sharp, position: 'relative',
duration: theme.transitions.duration.leavingScreen borderRadius: theme.shape.borderRadius,
}) backgroundColor: alpha(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: alpha(theme.palette.common.white, 0.25),
}, },
menuButton: { marginLeft: 0,
marginRight: 36 width: '100%',
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(1),
width: 'auto',
}, },
hide: { }));
display: "none" const StyledInputBase = styled(InputBase)(({ theme }) => ({
}, color: 'inherit',
drawer: { '& .MuiInputBase-input': {
flexShrink: 0,
whiteSpace: "nowrap",
[theme.breakpoints.up("sm")]: {
width: drawerWidth,
},
},
drawerPaper: {
width: drawerWidth
},
drawerClose: {
overflowX: 'hidden',
[theme.breakpoints.up("sm")]: {
width: theme.spacing(7) + 1,
},
},
toolbar: {
...theme.mixins.toolbar,
},
content: {
display: 'flex',
flexFlow: 'column',
flexGrow: 1,
padding: theme.spacing(3),
},
title: {
display: 'none',
[theme.breakpoints.up("sm")]: {
display: 'block'
}
},
search: {
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginRight: theme.spacing(2),
marginLeft: 0,
width: '100%',
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(3),
width: 'auto',
},
},
searchIcon: {
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
inputRoot: {
color: 'inherit'
},
inputInput: {
padding: theme.spacing(1, 1, 1, 0), padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon // vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`, paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'), transition: theme.transitions.create('width'),
width: '100%', width: '100%',
[theme.breakpoints.up('md')]: { [theme.breakpoints.up('sm')]: {
width: '20ch', width: '12ch',
"&:hover": { '&:focus': {
width: '25ch' width: '20ch',
} },
}, },
}, },
grow: {
flexGrow: 1,
},
})); }));
const closedMixin = (theme: Theme) => ({
overflowX: 'hidden',
width: `calc(${theme.spacing(7)} + 1px)`,
});
export const Headline = (prop: { export const Headline = (prop: {
children?: React.ReactNode, children?: React.ReactNode,
classes?:{ classes?: {
content?:string, content?: string,
toolbar?:string, toolbar?: string,
}, },
menu: React.ReactNode menu: React.ReactNode
}) => { }) => {
const [v, setv] = useState(false); const [v, setv] = useState(false);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const classes = useStyles();
const theme = useTheme(); const theme = useTheme();
const toggleV = () => setv(!v); const toggleV = () => setv(!v);
const handleProfileMenuOpen = (e: React.MouseEvent<HTMLElement>) => setAnchorEl(e.currentTarget); const handleProfileMenuOpen = (e: React.MouseEvent<HTMLElement>) => setAnchorEl(e.currentTarget);
@ -137,39 +91,58 @@ export const Headline = (prop: {
onClose={handleProfileMenuClose} onClose={handleProfileMenuClose}
> >
<MenuItem component={RouterLink} to='/profile'>Profile</MenuItem> <MenuItem component={RouterLink} to='/profile'>Profile</MenuItem>
<MenuItem onClick={async ()=>{handleProfileMenuClose(); await doLogout(); user_ctx.setUsername("");}}>Logout</MenuItem> <MenuItem onClick={async () => { handleProfileMenuClose(); await doLogout(); user_ctx.setUsername(""); }}>Logout</MenuItem>
</Menu>); </Menu>);
const drawer_contents = (<> const drawer_contents = (<>
<div className={classes.toolbar}> <DrawerHeader>
<IconButton onClick={toggleV}> <IconButton onClick={toggleV}>
{theme.direction === "ltr" ? <ChevronLeft /> : <ChevronRight />} {theme.direction === "ltr" ? <ChevronLeft /> : <ChevronRight />}
</IconButton> </IconButton>
</div> </DrawerHeader>
<Divider /> <Divider />
{prop.menu} {prop.menu}
</>); </>);
return (<div className={classes.root}> return (<div style={{ display: 'flex' }}>
<CssBaseline /> <CssBaseline />
<AppBar position="fixed" className={classes.appBar}> <AppBar position="fixed" sx={{
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
}}>
<Toolbar> <Toolbar>
<IconButton color="inherit" <IconButton color="inherit"
aria-label="open drawer" aria-label="open drawer"
onClick={toggleV} onClick={toggleV}
edge="start" edge="start"
className={classes.menuButton}> style={{ marginRight: 36 }}
>
<MenuIcon></MenuIcon> <MenuIcon></MenuIcon>
</IconButton> </IconButton>
<Link variant="h5" noWrap className={classes.title} color="inherit" component={RouterLink} to="/"> <Link variant="h5" noWrap sx={{
display: 'none',
[theme.breakpoints.up("sm")]: {
display: 'block'
}
}} color="inherit" component={RouterLink} to="/">
Ionian Ionian
</Link> </Link>
<div className={classes.grow}></div> <div style={{ flexGrow: 1 }}></div>
<div className={classes.search}> <StyledSearchBar >
<div className={classes.searchIcon}> <div style={{
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<SearchIcon /> <SearchIcon />
</div> </div>
<InputBase placeholder="search" <StyledInputBase placeholder="search"></StyledInputBase>
classes={{ root: classes.inputRoot, input: classes.inputInput }}></InputBase> </StyledSearchBar>
</div>
{ {
isLogin ? isLogin ?
<IconButton <IconButton
@ -188,21 +161,35 @@ export const Headline = (prop: {
{renderProfileMenu} {renderProfileMenu}
<nav> <nav>
<Hidden smUp implementation="css"> <Hidden smUp implementation="css">
<Drawer variant="temporary" anchor='left' open={v} onClose={toggleV} <StyledDrawer variant="temporary" anchor='left' open={v} onClose={toggleV}
classes={{ paper: classes.drawerPaper }}> sx={{
width: drawerWidth
}}
>
{drawer_contents} {drawer_contents}
</Drawer> </StyledDrawer>
</Hidden> </Hidden>
<Hidden xsDown implementation="css"> <Hidden xsDown implementation="css">
<Drawer variant='permanent' anchor='left' className={[classes.drawer, classes.drawerClose].join(" ").trim()} classes={{ <StyledDrawer variant='permanent' anchor='left'
paper: classes.drawerClose sx={{
}}> overflowX: 'hidden',
width: theme.spacing(7) + 1,
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme),
}}>
{drawer_contents} {drawer_contents}
</Drawer> </StyledDrawer>
</Hidden> </Hidden>
</nav> </nav>
<main className={prop.classes?.content ?? classes.content}> <main style={{
<div className={prop.classes?.toolbar ?? classes.toolbar}></div> display: 'flex',
flexFlow: 'column',
flexGrow: 1,
padding: theme.spacing(3),
marginTop: theme.spacing(8),
}}>
<div style={{
}} ></div>
{prop.children} {prop.children}
</main> </main>
</div>); </div>);

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import {Box, CircularProgress} from '@material-ui/core'; import {Box, CircularProgress} from '@mui/material';
export const LoadingCircle = ()=>{ export const LoadingCircle = ()=>{
return (<Box style={{alignSelf:'center'}}> return (<Box style={{alignSelf:'center'}}>

View File

@ -1,12 +1,11 @@
import React from 'react'; import React from 'react';
import {List, ListItem, ListItemIcon, Tooltip, ListItemText, Divider} from '@material-ui/core'; import {List, ListItem, ListItemIcon, Tooltip, ListItemText, Divider} from '@mui/material';
import {ArrowBack as ArrowBackIcon, Settings as SettingIcon, import {ArrowBack as ArrowBackIcon, Settings as SettingIcon,
Collections as CollectionIcon, VideoLibrary as VideoIcon, Home as HomeIcon, Collections as CollectionIcon, VideoLibrary as VideoIcon, Home as HomeIcon,
Folder as FolderIcon } from '@material-ui/icons'; Folder as FolderIcon } from '@mui/icons-material';
import {Link as RouterLink, useHistory} from 'react-router-dom'; import {Link as RouterLink} from 'react-router-dom';
export const NavItem = (props:{name:string,to:string, icon:React.ReactElement<any,any>})=>{ export const NavItem = (props:{name:string,to:string, icon:React.ReactElement<any,any>})=>{
const history = useHistory();
return (<ListItem button key={props.name} component={RouterLink} to={props.to}> return (<ListItem button key={props.name} component={RouterLink} to={props.to}>
<ListItemIcon> <ListItemIcon>
<Tooltip title={props.name.toLocaleLowerCase()} placement="bottom"> <Tooltip title={props.name.toLocaleLowerCase()} placement="bottom">

View File

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import {ChipTypeMap} from '@material-ui/core/Chip'; import {ChipTypeMap} from '@mui/material/Chip';
import { Chip, colors } from '@material-ui/core'; import { Chip, colors } from '@mui/material';
import {useTheme, makeStyles, Theme, emphasize, fade} from '@material-ui/core/styles'; import { Theme, emphasize} from '@mui/material/styles';
import {Link as RouterLink} from 'react-router-dom'; import {Link as RouterLink} from 'react-router-dom';
type TagChipStyleProp = { type TagChipStyleProp = {
color: string color: string
} }
const useTagStyles = makeStyles((theme:Theme)=>({ const useTagStyles = ((theme:Theme)=>({
root:(props:TagChipStyleProp)=>({ root:(props:TagChipStyleProp)=>({
color: theme.palette.getContrastText(props.color), color: theme.palette.getContrastText(props.color),
backgroundColor: props.color, backgroundColor: props.color,
@ -27,14 +27,14 @@ const useTagStyles = makeStyles((theme:Theme)=>({
color: (props:TagChipStyleProp)=>props.color, color: (props:TagChipStyleProp)=>props.color,
border: (props:TagChipStyleProp)=> `1px solid ${props.color}`, border: (props:TagChipStyleProp)=> `1px solid ${props.color}`,
'$clickable&:hover, $clickable&:focus, $deletable&:focus': { '$clickable&:hover, $clickable&:focus, $deletable&:focus': {
backgroundColor:(props:TagChipStyleProp)=>fade(props.color,theme.palette.action.hoverOpacity), //backgroundColor:(props:TagChipStyleProp)=> (props.color,theme.palette.action.hoverOpacity),
}, },
}, },
icon:{ icon:{
color:"inherit", color:"inherit",
}, },
deleteIcon:{ deleteIcon:{
color:(props:TagChipStyleProp)=>fade(theme.palette.getContrastText(props.color),0.7), //color:(props:TagChipStyleProp)=> (theme.palette.getContrastText(props.color),0.7),
"&:hover, &:active":{ "&:hover, &:active":{
color:(props:TagChipStyleProp)=>theme.palette.getContrastText(props.color), color:(props:TagChipStyleProp)=>theme.palette.getContrastText(props.color),
} }
@ -59,10 +59,8 @@ type ColorChipProp = Omit<ChipTypeMap['props'],"color"> & TagChipStyleProp & {
export const ColorChip = (props:ColorChipProp)=>{ export const ColorChip = (props:ColorChipProp)=>{
const {color,...rest} = props; const {color,...rest} = props;
const classes = useTagStyles({color : color !== "default" ? color : "#000"}); //const classes = useTagStyles({color : color !== "default" ? color : "#000"});
return <Chip color="default" classes={{ return <Chip color="default" {...rest}></Chip>;
...(color !== "default" ? classes : {})
}} {...rest}></Chip>;
} }
type TagChipProp = Omit<ChipTypeMap['props'],"color"> & { type TagChipProp = Omit<ChipTypeMap['props'],"color"> & {

View File

@ -1,52 +1,20 @@
{ {
"name": "ionian-client", "name": "ionian_client",
"version": "1.0.0", "version": "0.0.1",
"description": "", "description": "client of ionian",
"main": "index.js", "dependencies": {
"scripts": { "@emotion/react": "^11.9.0",
"build:dev": "webpack --mode development", "@emotion/styled": "^11.8.1",
"build:prod": "webpack --mode production", "@mui/icons-material": "^5.6.2",
"build:watch": "webpack --mode development -w", "@mui/material": "^5.6.2",
"type-check": "tsc --noEmit" "@types/react": "^18.0.5",
}, "@types/react-dom": "^18.0.1",
"author": "", "react": "^18.0.0",
"license": "ISC", "react-dom": "^18.0.0",
"browserslist": { "react-router-dom": "^6.3.0"
"production": [ },
"> 10%" "devDependencies": {
], "esbuild": "^0.14.36",
"development": [ "ts-node": "^10.7.0"
"last 1 chrome version", }
"last 1 firefox version"
]
},
"dependencies": {
"@material-ui/core": "^4.11.2",
"@material-ui/icons": "^4.11.2",
"@mui/material": "^5.0.3",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@babel/preset-typescript": "^7.12.7",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.1.7",
"babel-core": "^6.26.3",
"babel-loader": "^8.2.2",
"css-loader": "^5.0.1",
"eslint-plugin-node": "^11.1.0",
"mini-css-extract-plugin": "^1.3.3",
"style-loader": "^2.0.0",
"ts-json-schema-generator": "^0.82.0",
"typescript": "^4.1.3",
"webpack": "^5.15.0",
"webpack-cli": "^4.3.1"
}
} }

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import {Typography} from '@material-ui/core'; import {Typography} from '@mui/material';
import {ArrowBack as ArrowBackIcon} from '@material-ui/icons'; import {ArrowBack as ArrowBackIcon} from '@mui/icons-material';
import { Headline, BackItem, NavList, CommonMenuList } from '../component/mod'; import { Headline, BackItem, NavList, CommonMenuList } from '../component/mod';
export const NotFoundPage = ()=>{ export const NotFoundPage = ()=>{

View File

@ -1,11 +1,10 @@
import React, { useState, useEffect, useContext } from 'react'; import React, { useState, useEffect} from 'react';
import { Redirect, Route, Switch, useHistory, useRouteMatch, match as MatchType, Link as RouterLink, useParams, useLocation } from 'react-router-dom'; import { Route, Routes, useLocation, useParams } from 'react-router-dom';
import DocumentAccessor, { Document } from '../accessor/document'; import DocumentAccessor, { Document } from '../accessor/document';
import { LoadingCircle } from '../component/loading'; import { LoadingCircle } from '../component/loading';
import { Link, Paper, makeStyles, Theme, Box, useTheme, Typography } from '@material-ui/core'; import { Theme, Typography } from '@mui/material';
import {ArrowBack as ArrowBackIcon } from '@material-ui/icons';
import { getPresenter } from './reader/reader'; import { getPresenter } from './reader/reader';
import { BackItem, CommonMenuList, ContentInfo, Headline, NavItem, NavList } from '../component/mod'; import { CommonMenuList, ContentInfo, Headline } from '../component/mod';
import {NotFoundPage} from './404'; import {NotFoundPage} from './404';
export const makeContentInfoUrl = (id: number) => `/doc/${id}`; export const makeContentInfoUrl = (id: number) => `/doc/${id}`;
@ -16,7 +15,7 @@ type DocumentState = {
notfound: boolean, notfound: boolean,
} }
const useStyles = makeStyles((theme:Theme)=>({ const styles = ((theme:Theme)=>({
noPaddingContent:{ noPaddingContent:{
display:'flex', display:'flex',
flexDirection: 'column', flexDirection: 'column',
@ -28,12 +27,13 @@ const useStyles = makeStyles((theme:Theme)=>({
} }
})); }));
export const DocumentAbout = (prop: { match: MatchType }) => { export const DocumentAbout = (prop?: {}) => {
const match = useRouteMatch<{id:string}>("/doc/:id"); const location = useLocation();
const match = useParams<{id:string}>();
if (match == null) { if (match == null) {
throw new Error("unreachable"); throw new Error("unreachable");
} }
const id = Number.parseInt(match.params['id']); const id = Number.parseInt(match.id);
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>;
@ -45,7 +45,7 @@ export const DocumentAbout = (prop: { match: MatchType }) => {
} }
})(); })();
}, []); }, []);
const classes = useStyles();
if (isNaN(id)) { if (isNaN(id)) {
return ( return (
<Headline menu={menu_list()}> <Headline menu={menu_list()}>
@ -68,23 +68,20 @@ export const DocumentAbout = (prop: { match: MatchType }) => {
} }
else{ else{
const ReaderPage = getPresenter(info.doc); const ReaderPage = getPresenter(info.doc);
return (<Switch> return (<Routes>
<Route exact path={`${prop.match.path}/:id`}> <Route path={`:id`}>
<Headline menu={menu_list()}> <Headline menu={menu_list()}>
<ContentInfo document={info.doc}></ContentInfo> <ContentInfo document={info.doc}></ContentInfo>
</Headline> </Headline>
</Route> </Route>
<Route exact path={`${prop.match.path}/:id/reader`}> <Route path={`:id/reader`}>
<Headline menu={menu_list(`${prop.match.path}/${id}`)} classes={{ <Headline menu={menu_list(location.pathname)}>
content:classes.noPaddingContent,
toolbar:classes.noPaddingToolbar
}}>
<ReaderPage doc={info.doc}></ReaderPage> <ReaderPage doc={info.doc}></ReaderPage>
</Headline> </Headline>
</Route> </Route>
<Route> <Route>
<NotFoundPage></NotFoundPage> <NotFoundPage></NotFoundPage>
</Route> </Route>
</Switch>); </Routes>);
} }
} }

View File

@ -1,10 +1,10 @@
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 { Box, Grid, Paper, Typography,Button, makeStyles, Theme } from "@material-ui/core"; import { Box, Grid, Paper, Typography,Button, Theme } from "@mui/material";
import {Stack} from '@mui/material'; import {Stack} from '@mui/material';
const useStyles = makeStyles((theme:Theme)=>({ const useStyles = ((theme:Theme)=>({
paper:{ paper:{
padding: theme.spacing(2), padding: theme.spacing(2),
}, },
@ -30,12 +30,12 @@ function TypeDifference(prop:{
onCommit:(v:{type:string,path:string})=>void, onCommit:(v:{type:string,path:string})=>void,
onCommitAll:(type:string) => void onCommitAll:(type:string) => void
}){ }){
const classes = useStyles(); //const classes = useStyles();
const x = prop.content; const x = prop.content;
const [button_disable,set_disable] = useState(false); const [button_disable,set_disable] = useState(false);
return (<Paper className={classes.paper}> return (<Paper /*className={classes.paper}*/>
<Box className={classes.contentTitle}> <Box /*className={classes.contentTitle}*/>
<Typography variant='h3' >{x.type}</Typography> <Typography variant='h3' >{x.type}</Typography>
<Button variant="contained" key={x.type} onClick={()=>{ <Button variant="contained" key={x.type} onClick={()=>{
set_disable(true); set_disable(true);
@ -44,7 +44,7 @@ function TypeDifference(prop:{
}}>Commit all</Button> }}>Commit all</Button>
</Box> </Box>
{x.value.map(y=>( {x.value.map(y=>(
<Box className={classes.commitable} key={y.path}> <Box /*className={classes.commitable}*/ key={y.path}>
<Button variant="contained" onClick={()=>{ <Button variant="contained" onClick={()=>{
set_disable(true); set_disable(true);
prop.onCommit(y); prop.onCommit(y);
@ -59,7 +59,7 @@ function TypeDifference(prop:{
export function DifferencePage(){ export function DifferencePage(){
const ctx = useContext(UserContext); const ctx = useContext(UserContext);
const classes = useStyles(); //const classes = useStyles();
const [diffList,setDiffList] = useState< const [diffList,setDiffList] = useState<
FileDifference[] FileDifference[]
>([]); >([]);

View File

@ -1,22 +1,13 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { Headline, CommonMenuList,LoadingCircle, ContentInfo, NavList, NavItem, TagChip } from '../component/mod'; import { Headline, CommonMenuList,LoadingCircle, ContentInfo, NavList, NavItem, TagChip } from '../component/mod';
import { Box, Paper, Link, useMediaQuery, Portal, List, ListItem, ListItemIcon, Tooltip, ListItemText, Typography, Chip } from '@material-ui/core'; import { Box, Typography, Chip } from '@mui/material';
import {useTheme, makeStyles, Theme} from '@material-ui/core/styles';
import ContentAccessor,{QueryListOption, Document} from '../accessor/document'; import ContentAccessor,{QueryListOption, Document} from '../accessor/document';
import {Link as RouterLink} from 'react-router-dom';
import {toQueryString} from '../accessor/util'; 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';
const useStyles = makeStyles((theme:Theme)=>({
root:{
display:"grid",
gridGap: theme.spacing(4),
},
}));
export type GalleryProp = { export type GalleryProp = {
option?:QueryListOption; option?:QueryListOption;
diff:string; diff:string;
@ -28,6 +19,7 @@ type GalleryState = {
export const GalleryInfo = (props: GalleryProp)=>{ export const GalleryInfo = (props: GalleryProp)=>{
const [state,setState]= useState<GalleryState>({documents:undefined}); const [state,setState]= useState<GalleryState>({documents:undefined});
useEffect(()=>{ useEffect(()=>{
const abortController = new AbortController();
const load = (async ()=>{ const load = (async ()=>{
const c = await ContentAccessor.findList(props.option); const c = await ContentAccessor.findList(props.option);
//todo : if c is undefined, retry to fetch 3 times. and show error message. //todo : if c is undefined, retry to fetch 3 times. and show error message.
@ -35,14 +27,15 @@ export const GalleryInfo = (props: GalleryProp)=>{
}) })
load(); load();
},[props.diff]); },[props.diff]);
const classes = useStyles();
const queryString = toQueryString(props.option??{}); const queryString = toQueryString(props.option??{});
if(state.documents === undefined){ if(state.documents === undefined){
return (<LoadingCircle/>); return (<LoadingCircle/>);
} }
else{ else{
return (<Box className={classes.root}> return (<Box sx={{
display:'grid'
}}>
{props.option !== undefined && props.diff !== "" && <Box> {props.option !== undefined && props.diff !== "" && <Box>
<Typography variant="h6">search for</Typography> <Typography variant="h6">search for</Typography>
{props.option.word !== undefined && <Chip label={"search : "+props.option.word}></Chip>} {props.option.word !== undefined && <Chip label={"search : "+props.option.word}></Chip>}

View File

@ -1,9 +1,9 @@
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 { Button, Dialog, DialogActions, DialogContent, DialogContentText, import { Button, Dialog, DialogActions, DialogContent, DialogContentText,
DialogTitle, MenuList, Paper, TextField, Typography, useTheme } from '@material-ui/core'; DialogTitle, MenuList, Paper, TextField, Typography, useTheme } from '@mui/material';
import { UserContext } from '../state'; import { UserContext } from '../state';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import {doLogin as doSessionLogin} from '../state'; import {doLogin as doSessionLogin} from '../state';
export const LoginPage = ()=>{ export const LoginPage = ()=>{
@ -11,7 +11,7 @@ export const LoginPage = ()=>{
const [userLoginInfo,setUserLoginInfo]= useState({username:"",password:""}); const [userLoginInfo,setUserLoginInfo]= useState({username:"",password:""});
const [openDialog,setOpenDialog] = useState({open:false,message:""}); const [openDialog,setOpenDialog] = useState({open:false,message:""});
const {setUsername,setPermission} = useContext(UserContext); const {setUsername,setPermission} = useContext(UserContext);
const history = useHistory(); const navigate = useNavigate();
const handleDialogClose = ()=>{ const handleDialogClose = ()=>{
setOpenDialog({...openDialog,open:false}); setOpenDialog({...openDialog,open:false});
} }
@ -34,7 +34,7 @@ export const LoginPage = ()=>{
else console.error(e); else console.error(e);
return; return;
} }
history.push("/"); navigate("/");
} }
const menu = CommonMenuList(); const menu = CommonMenuList();
return <Headline menu={menu}> return <Headline menu={menu}>

View File

@ -1,10 +1,10 @@
import { CommonMenuList, Headline } from "../component/mod"; import { CommonMenuList, Headline } from "../component/mod";
import React, { useContext, useState } from 'react'; import React, { useContext, useState } from 'react';
import { UserContext } from "../state"; import { UserContext } from "../state";
import { Chip, Grid, makeStyles, Paper, Theme, Typography, Divider, Button, import { Chip, Grid, Paper, Theme, Typography, Divider, Button,
Dialog, DialogTitle, DialogContentText, DialogContent, TextField, DialogActions } from "@material-ui/core"; Dialog, DialogTitle, DialogContentText, DialogContent, TextField, DialogActions } from "@mui/material";
const useStyles = makeStyles((theme:Theme)=>({ const useStyles = ((theme:Theme)=>({
paper:{ paper:{
alignSelf:"center", alignSelf:"center",
padding:theme.spacing(2), padding:theme.spacing(2),
@ -17,7 +17,7 @@ const useStyles = makeStyles((theme:Theme)=>({
export function ProfilePage(){ export function ProfilePage(){
const userctx = useContext(UserContext); const userctx = useContext(UserContext);
const classes = useStyles(); //const classes = useStyles();
const menu = CommonMenuList(); const menu = CommonMenuList();
const [pw_open,set_pw_open] = useState(false); const [pw_open,set_pw_open] = useState(false);
const [oldpw,setOldpw] = useState(""); const [oldpw,setOldpw] = useState("");
@ -66,7 +66,7 @@ export function ProfilePage(){
handle_close(); handle_close();
} }
return (<Headline menu={menu}> return (<Headline menu={menu}>
<Paper className={classes.paper}> <Paper /*className={classes.paper}*/>
<Grid container direction="column" alignItems="center"> <Grid container direction="column" alignItems="center">
<Grid item> <Grid item>
<Typography variant='h4'>{userctx.username}</Typography> <Typography variant='h4'>{userctx.username}</Typography>
@ -87,7 +87,7 @@ export function ProfilePage(){
<DialogTitle>Password Reset</DialogTitle> <DialogTitle>Password Reset</DialogTitle>
<DialogContent> <DialogContent>
<Typography>type the old and new password</Typography> <Typography>type the old and new password</Typography>
<div className={classes.formfield}> <div /*className={classes.formfield}*/>
{(!isElectronContent) && (<TextField autoFocus margin='dense' type="password" label="old password" {(!isElectronContent) && (<TextField autoFocus margin='dense' type="password" label="old password"
value={oldpw} onChange={(e)=>setOldpw(e.target.value)}></TextField>)} value={oldpw} onChange={(e)=>setOldpw(e.target.value)}></TextField>)}
<TextField margin='dense' type="password" label="new password" <TextField margin='dense' type="password" label="new password"

View File

@ -1,5 +1,5 @@
import React, {useState, useEffect} from 'react'; import React, {useState, useEffect} from 'react';
import { Typography, useTheme } from '@material-ui/core'; import { Typography, useTheme } from '@mui/material';
import { Document } from '../../accessor/document'; import { Document } from '../../accessor/document';
type ComicType = "comic"|"artist cg"|"donjinshi"|"western" type ComicType = "comic"|"artist cg"|"donjinshi"|"western"
@ -14,7 +14,6 @@ export type PresentableTag = {
} }
export const ComicReader = (props:{doc:Document})=>{ export const ComicReader = (props:{doc:Document})=>{
const theme = useTheme();
const additional = props.doc.additional; const additional = props.doc.additional;
const [curPage,setCurPage] = useState(0); const [curPage,setCurPage] = useState(0);
if(!('page' in additional)){ if(!('page' in additional)){

View File

@ -1,4 +1,4 @@
import { Typography } from '@material-ui/core'; import { Typography } from '@mui/material';
import React from 'react'; import React from 'react';
import { Document, makeThumbnailUrl } from '../../accessor/document'; import { Document, makeThumbnailUrl } from '../../accessor/document';
import {ComicReader} from './comic'; import {ComicReader} from './comic';
@ -22,10 +22,10 @@ export const getPresenter = (content:Document):PagePresenter => {
return ()=><Typography variant='h2'>Not implemented reader</Typography>; return ()=><Typography variant='h2'>Not implemented reader</Typography>;
} }
export const ThumbnailContainer = (props:{content:Document, className?:string})=>{ export const ThumbnailContainer = (props:{content:Document, className?:string, style?:React.CSSProperties})=>{
const thumbnailurl = makeThumbnailUrl(props.content); const thumbnailurl = makeThumbnailUrl(props.content);
if(props.content.content_type === "video"){ if(props.content.content_type === "video"){
return (<video src={thumbnailurl} muted autoPlay loop className={props.className}></video>) return (<video src={thumbnailurl} muted autoPlay loop className={props.className} style={props.style}></video>)
} }
else return (<img src={thumbnailurl} className={props.className}></img>) else return (<img src={thumbnailurl} className={props.className} style={props.style}></img>)
} }

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import {Typography, Paper} from '@material-ui/core'; import {Typography, Paper} from '@mui/material';
import {ArrowBack as ArrowBackIcon} from '@material-ui/icons'; import {ArrowBack as ArrowBackIcon} from '@mui/icons-material';
import { Headline, BackItem, NavList, CommonMenuList } from '../component/mod'; import { Headline, BackItem, NavList, CommonMenuList } from '../component/mod';
export const SettingPage = ()=>{ export const SettingPage = ()=>{

View File

@ -1,70 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./build", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

View File

@ -1,45 +0,0 @@
const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = ()=>{return {
entry: './app.tsx',
output: {
path: path.resolve(__dirname, '../../dist/js'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
babelrc: true,//why babelrc not working? why is webpack not reading '.babelrc'?
presets: [
'@babel/preset-env',
'@babel/preset-typescript',
'@babel/preset-react'
],
plugins: [
'@babel/proposal-class-properties',
'@babel/proposal-object-rest-spread'
]
}
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader,'css-loader']
}
]
},
plugins : [
new MiniCssExtractPlugin({
"filename":'../css/style.css'}),
],
devtool: 'source-map',
resolve: {
extensions: ['.js','.css','.ts','.tsx']
}
};}

13
src/search/indexer.ts Normal file
View File

@ -0,0 +1,13 @@
export interface PaginationOption{
cursor:number;
limit:number;
}
export interface IIndexer{
indexDoc(word:string,doc_id:number):boolean;
indexDoc(word:string[],doc_id:number):boolean;
getDoc(word:string,option?:PaginationOption):number[];
getDoc(word:string[],option?:PaginationOption):number[];
}

10
src/search/tokenizer.ts Normal file
View File

@ -0,0 +1,10 @@
export interface ITokenizer{
tokenize(s:string):string[];
}
export class DefaultTokenizer implements ITokenizer{
tokenize(s: string): string[] {
return s.split(" ");
}
}

View File

@ -119,10 +119,12 @@ class ServerApplication{
} }
})}; })};
const setting = get_setting(); const setting = get_setting();
static_file_server('dist/css/style.css','css'); static_file_server('dist/bundle.css','css');
static_file_server('dist/js/bundle.js','js'); static_file_server('dist/bundle.js','js');
if(setting.mode === "development") if(setting.mode === "development"){
static_file_server('dist/js/bundle.js.map','text'); static_file_server('dist/bundle.js.map','text');
static_file_server('dist/bundle.css.map','text');
}
} }
start_server(){ start_server(){
let setting = get_setting(); let setting = get_setting();