back link feature

This commit is contained in:
monoid 2021-01-08 17:26:38 +09:00
parent 5002bc22f9
commit 714ba9aae7
7 changed files with 77 additions and 26 deletions

View File

@ -14,4 +14,22 @@ export const toQueryString = (obj:ToQueryStringA)=> {
? e[1].map(f=>`${e[0]}[]=${encodeURIComponent(f)}`).join('&') ? e[1].map(f=>`${e[0]}[]=${encodeURIComponent(f)}`).join('&')
: `${e[0]}=${encodeURIComponent(e[1])}`) : `${e[0]}=${encodeURIComponent(e[1])}`)
.join('&'); .join('&');
}
export const QueryStringToMap = (query:string) =>{
const keyValue = query.slice(query.indexOf("?")+1).split("&");
const param:{[k:string]:string|string[]} = {};
keyValue.forEach((p)=>{
const [k,v] = p.split("=");
if(k.endsWith("[]")){
let arr = param[k];
if(arr instanceof Array){
arr.push(v);
}
else{
param[k] = [v];
}
}
else param[k] = v;
});
return param;
} }

View File

@ -1,23 +1,28 @@
import React, { useRef, useState } from 'react'; import React, { createContext, useRef, useState } from 'react';
import ReactDom from 'react-dom'; import ReactDom from 'react-dom';
import {BrowserRouter, Route, Switch as RouterSwitch} from 'react-router-dom'; import {BrowserRouter, Route, Switch as RouterSwitch} from 'react-router-dom';
import { Gallery, ContentAbout} from './page/mod'; import { Gallery, ContentAbout} from './page/mod';
import {BackLinkContext} from './state';
import './css/style.css'; import './css/style.css';
const FooProfile = ()=><div>test profile</div>; const FooProfile = ()=><div>test profile</div>;
const App = () => { const App = () => {
return (<BrowserRouter> const [path,setPath] = useState("/");
<RouterSwitch> return (
<Route path="/" exact render={()=><Gallery />}></Route> <BackLinkContext.Provider value={{path:path,setPath:setPath}}>
<Route path="/search" render={()=><Gallery />}></Route> <BrowserRouter>
<Route path="/doc" render={(prop)=><ContentAbout {...prop}/>}></Route> <RouterSwitch>
<Route path="/profile" component={FooProfile}></Route> <Route path="/" exact render={()=><Gallery />}></Route>
<Route> <Route path="/search" render={()=><Gallery />}></Route>
<div>404 Not Found</div> <Route path="/doc" render={(prop)=><ContentAbout {...prop}/>}></Route>
</Route> <Route path="/profile" component={FooProfile}></Route>
</RouterSwitch> <Route>
</BrowserRouter>); <div>404 Not Found</div>
</Route>
</RouterSwitch>
</BrowserRouter>
</BackLinkContext.Provider>);
}; };
ReactDom.render( ReactDom.render(

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useContext } from 'react';
import { Redirect, Route, Switch, useHistory, useRouteMatch, match as MatchType, Link as RouterLink, useParams, useLocation } from 'react-router-dom'; import { Redirect, Route, Switch, useHistory, useRouteMatch, match as MatchType, Link as RouterLink, useParams, useLocation } from 'react-router-dom';
import ContentAccessor, { Content } from '../accessor/contents'; import ContentAccessor, { Content } from '../accessor/contents';
import { LoadingCircle } from '../component/loading'; import { LoadingCircle } from '../component/loading';
@ -6,6 +6,7 @@ import { Link, Paper, makeStyles, Theme, Box, useTheme, Typography } from '@mate
import {ArrowBack as ArrowBackIcon } from '@material-ui/icons'; import {ArrowBack as ArrowBackIcon } from '@material-ui/icons';
import { MangaReader } from './reader/manga'; import { MangaReader } from './reader/manga';
import { ContentInfo, Headline, NavItem, NavList } from '../component/mod'; import { ContentInfo, Headline, NavItem, NavList } from '../component/mod';
import {BackLinkContext} from '../state';
export const makeContentInfoUrl = (id: number) => `/doc/${id}`; export const makeContentInfoUrl = (id: number) => `/doc/${id}`;
export const makeMangaReaderUrl = (id: number) => `/doc/${id}/reader`; export const makeMangaReaderUrl = (id: number) => `/doc/${id}/reader`;
@ -24,9 +25,15 @@ export const ContentAbout = (prop: { match: MatchType }) => {
const [info, setInfo] = useState<ContentState>({ content: undefined, notfound:false }); const [info, setInfo] = useState<ContentState>({ content: undefined, notfound:false });
const location = useLocation(); const location = useLocation();
console.log("state : "+location.state); console.log("state : "+location.state);
const menu_list = ( const menu_list = (link?:string)=>(
<NavList> <NavList>
<NavItem name="Back" to="/" icon={<ArrowBackIcon/>}></NavItem> <BackLinkContext.Consumer>
{
(ctx) => link === undefined ?
<NavItem name="Back" to={ctx.path} icon={<ArrowBackIcon/>}/>
: <NavItem name="Back" to={link} icon={<ArrowBackIcon/>}/>
}
</BackLinkContext.Consumer>
</NavList> </NavList>
); );
@ -42,39 +49,43 @@ export const ContentAbout = (prop: { match: MatchType }) => {
}, []); }, []);
if (isNaN(id)) { if (isNaN(id)) {
return ( return (
<Headline menu={menu_list}> <Headline menu={menu_list()}>
<Typography variant='h2'>Oops. Invalid ID</Typography> <Typography variant='h2'>Oops. Invalid ID</Typography>
</Headline> </Headline>
); );
} }
else if(info.notfound){ else if(info.notfound){
return ( return (
<Headline menu={menu_list}> <Headline menu={menu_list()}>
<Typography variant='h2'>Content has been removed.</Typography> <Typography variant='h2'>Content has been removed.</Typography>
</Headline> </Headline>
) )
} }
else if (info.content === undefined) { else if (info.content === undefined) {
return (<Headline menu={menu_list}> return (<Headline menu={menu_list()}>
<LoadingCircle /> <LoadingCircle />
</Headline> </Headline>
); );
} }
else return (<Switch> else{
return (<Switch>
<Route exact path={`${prop.match.path}/:id`}> <Route exact path={`${prop.match.path}/:id`}>
<Headline menu={menu_list}>
<Headline menu={menu_list()}>
<ContentInfo content={info.content}></ContentInfo> <ContentInfo content={info.content}></ContentInfo>
</Headline> </Headline>
</Route> </Route>
<Route exact path={`${prop.match.path}/:id/reader`}> <Route exact path={`${prop.match.path}/:id/reader`}>
<Headline menu={menu_list}> <Headline menu={menu_list(`${prop.match.path}/${id}`)}>
<MangaReader content={info.content}></MangaReader> <MangaReader content={info.content}></MangaReader>
</Headline> </Headline>
</Route> </Route>
<Route> <Route>
<Headline menu={menu_list}> <Headline menu={menu_list()}>
<div>404 Not Found invalid url : {prop.match.path}</div> <div>404 Not Found invalid url : {prop.match.path}</div>
</Headline> </Headline>
</Route> </Route>
</Switch>); </Switch>);
}
} }

View File

@ -1,14 +1,22 @@
import React from 'react'; import React, { useContext } from 'react';
import { NavList, NavItem, Headline } from '../component/mod'; import { NavList, NavItem, Headline } from '../component/mod';
import {ArrowBack as ArrowBackIcon} from '@material-ui/icons'; import {ArrowBack as ArrowBackIcon} from '@material-ui/icons';
import {GalleryInfo} from '../component/mod'; import {GalleryInfo} from '../component/mod';
import {BackLinkContext} from '../state';
import {useLocation} from 'react-router-dom';
import { QueryStringToMap } from '../accessor/util';
export const Gallery = ()=>{ export const Gallery = ()=>{
const location = useLocation();
const backctx = useContext(BackLinkContext);
backctx.setPath("/");
const query = QueryStringToMap(location.search);
const menu_list = (<NavList> const menu_list = (<NavList>
<NavItem name="Back" to="" icon={<ArrowBackIcon></ArrowBackIcon>}></NavItem> {Object.keys(query).length !== 0 && <NavItem name="Back" to="/" icon={<ArrowBackIcon></ArrowBackIcon>}></NavItem>}
</NavList>); </NavList>);
return (<Headline menu={menu_list}> return (<Headline menu={menu_list}>
<GalleryInfo></GalleryInfo> <GalleryInfo option={query}></GalleryInfo>
</Headline>) </Headline>)
} }

View File

@ -22,7 +22,7 @@ export const MangaReader = (props:{content:Content})=>{
return <Typography>Error. DB error. page restriction</Typography> return <Typography>Error. DB error. page restriction</Typography>
} }
const page:number = additional['page'] as number; const page:number = additional['page'] as number;
return (<div>{[...Array(page).keys()].map(x=>(<img src={`/content/${props.content.id}/manga/${x}`} style={{maxHeight:'100%',maxWidth:'100%'}}></img>))}</div>); return (<div>{[...Array(page).keys()].map(x=>(<img src={`/content/${props.content.id}/manga/${x}`} key={x} style={{maxHeight:'100%',maxWidth:'100%'}}></img>))}</div>);
} }
export default MangaReader; export default MangaReader;

3
src/client/state.tsx Normal file
View File

@ -0,0 +1,3 @@
import React, { createContext, useRef, useState } from 'react';
export const BackLinkContext = createContext({path:"",setPath:(s:string)=>{}});

View File

@ -47,6 +47,12 @@ async function main(){
ctx.body = index_html; ctx.body = index_html;
} }
); );
router.get('/search'
,async (ctx,next)=>{
ctx.type = "html";
ctx.body = index_html;
}
);
let content_router = getContentRouter(createKnexContentsAccessor(db)); let content_router = getContentRouter(createKnexContentsAccessor(db));
router.use('/content',content_router.routes()); router.use('/content',content_router.routes());
router.use('/content',content_router.allowedMethods()); router.use('/content',content_router.allowedMethods());