simple search
This commit is contained in:
parent
7a3fdce4dc
commit
1eba3e43e7
@ -1,7 +1,7 @@
|
|||||||
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, Navigate, Route, Routes } 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, ReaderPage } from './page/mod';
|
||||||
import { getInitialValue, UserContext } from './state';
|
import { getInitialValue, UserContext } from './state';
|
||||||
import { ThemeProvider, createTheme } from '@mui/material';
|
import { ThemeProvider, createTheme } from '@mui/material';
|
||||||
|
|
||||||
@ -32,7 +32,8 @@ const App = () => {
|
|||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Navigate replace to='/search?' />} />
|
<Route path="/" element={<Navigate replace to='/search?' />} />
|
||||||
<Route path="/search" element={<Gallery />} />
|
<Route path="/search" element={<Gallery />} />
|
||||||
<Route path="/doc" element={<DocumentAbout />}></Route>
|
<Route path="/doc/:id" element={<DocumentAbout />}></Route>
|
||||||
|
<Route path="/doc/:id/reader" element={<ReaderPage />}></Route>
|
||||||
<Route path="/login" element={<LoginPage></LoginPage>} />
|
<Route path="/login" element={<LoginPage></LoginPage>} />
|
||||||
<Route path="/profile" element={<ProfilePage />}></Route>
|
<Route path="/profile" element={<ProfilePage />}></Route>
|
||||||
<Route path="/difference" element={<DifferencePage />}></Route>
|
<Route path="/difference" element={<DifferencePage />}></Route>
|
||||||
|
@ -83,7 +83,6 @@ export const ContentInfo = (props: {
|
|||||||
//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 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 :
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
ChevronLeft, ChevronRight, Menu as MenuIcon, Search as SearchIcon, AccountCircle
|
ChevronLeft, ChevronRight, Menu as MenuIcon, Search as SearchIcon, AccountCircle
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink, useNavigate } from 'react-router-dom';
|
||||||
import { doLogout, UserContext } from '../state';
|
import { doLogout, UserContext } from '../state';
|
||||||
|
|
||||||
const drawerWidth = 270;
|
const drawerWidth = 270;
|
||||||
@ -80,6 +80,8 @@ export const Headline = (prop: {
|
|||||||
const menuId = 'primary-search-account-menu';
|
const menuId = 'primary-search-account-menu';
|
||||||
const user_ctx = useContext(UserContext);
|
const user_ctx = useContext(UserContext);
|
||||||
const isLogin = user_ctx.username !== "";
|
const isLogin = user_ctx.username !== "";
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
const renderProfileMenu = (<Menu
|
const renderProfileMenu = (<Menu
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
@ -139,9 +141,21 @@ export const Headline = (prop: {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center'
|
||||||
}}>
|
}}>
|
||||||
<SearchIcon />
|
<SearchIcon onClick={() => navigate(`/search?word=${encodeURIComponent(search)}`)} />
|
||||||
</div>
|
</div>
|
||||||
<StyledInputBase placeholder="search"></StyledInputBase>
|
<StyledInputBase placeholder="search"
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyUp={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
let words = search.includes("&") ? search.split("&") : [search];
|
||||||
|
words = words.map(w => w.trim())
|
||||||
|
.map(w => w.includes(":") ?
|
||||||
|
`allow_tag=${w}`
|
||||||
|
: `word=${encodeURIComponent(w)}`);
|
||||||
|
navigate(`/search?${words.join("&")}`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={search}></StyledInputBase>
|
||||||
</StyledSearchBar>
|
</StyledSearchBar>
|
||||||
{
|
{
|
||||||
isLogin ?
|
isLogin ?
|
||||||
@ -159,7 +173,7 @@ export const Headline = (prop: {
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
{renderProfileMenu}
|
{renderProfileMenu}
|
||||||
<nav>
|
<nav style={{ width: theme.spacing(7) }}>
|
||||||
<Hidden smUp implementation="css">
|
<Hidden smUp implementation="css">
|
||||||
<StyledDrawer variant="temporary" anchor='left' open={v} onClose={toggleV}
|
<StyledDrawer variant="temporary" anchor='left' open={v} onClose={toggleV}
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import React, { useState, useEffect} from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Route, Routes, useLocation, useParams } 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 { Theme, Typography } from '@mui/material';
|
import { Theme, Typography } from '@mui/material';
|
||||||
import { getPresenter } from './reader/reader';
|
import { getPresenter } from './reader/reader';
|
||||||
import { CommonMenuList, ContentInfo, Headline } 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}`;
|
||||||
export const makeComicReaderUrl = (id: number) => `/doc/${id}/reader`;
|
export const makeComicReaderUrl = (id: number) => `/doc/${id}/reader`;
|
||||||
@ -15,28 +15,28 @@ type DocumentState = {
|
|||||||
notfound: boolean,
|
notfound: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = ((theme:Theme)=>({
|
const styles = ((theme: Theme) => ({
|
||||||
noPaddingContent:{
|
noPaddingContent: {
|
||||||
display:'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
},
|
},
|
||||||
noPaddingToolbar:{
|
noPaddingToolbar: {
|
||||||
flex: '0 1 auto',
|
flex: '0 1 auto',
|
||||||
...theme.mixins.toolbar,
|
...theme.mixins.toolbar,
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const DocumentAbout = (prop?: {}) => {
|
export function ReaderPage(props?: {}) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const match = useParams<{id:string}>();
|
const match = useParams<{ id: string }>();
|
||||||
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);
|
||||||
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>;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (!isNaN(id)) {
|
if (!isNaN(id)) {
|
||||||
@ -53,7 +53,7 @@ export const DocumentAbout = (prop?: {}) => {
|
|||||||
</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>
|
||||||
@ -66,22 +66,57 @@ export const DocumentAbout = (prop?: {}) => {
|
|||||||
</Headline>
|
</Headline>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
const ReaderPage = getPresenter(info.doc);
|
const ReaderPage = getPresenter(info.doc);
|
||||||
return (<Routes>
|
return <Headline menu={menu_list(location.pathname)}>
|
||||||
<Route path={`:id`}>
|
<ReaderPage doc={info.doc}></ReaderPage>
|
||||||
|
</Headline>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DocumentAbout = (prop?: {}) => {
|
||||||
|
const match = useParams<{ id: string }>();
|
||||||
|
if (match == null) {
|
||||||
|
throw new Error("unreachable");
|
||||||
|
}
|
||||||
|
const id = Number.parseInt(match.id);
|
||||||
|
const [info, setInfo] = useState<DocumentState>({ doc: undefined, notfound: false });
|
||||||
|
const menu_list = (link?: string) => <CommonMenuList url={link}></CommonMenuList>;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (!isNaN(id)) {
|
||||||
|
const c = await DocumentAccessor.findById(id);
|
||||||
|
setInfo({ doc: c, notfound: c === undefined });
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isNaN(id)) {
|
||||||
|
return (
|
||||||
|
<Headline menu={menu_list()}>
|
||||||
|
<Typography variant='h2'>Oops. Invalid ID</Typography>
|
||||||
|
</Headline>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (info.notfound) {
|
||||||
|
return (
|
||||||
|
<Headline menu={menu_list()}>
|
||||||
|
<Typography variant='h2'>Content has been removed.</Typography>
|
||||||
|
</Headline>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (info.doc === undefined) {
|
||||||
|
return (<Headline menu={menu_list()}>
|
||||||
|
<LoadingCircle />
|
||||||
|
</Headline>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (
|
||||||
<Headline menu={menu_list()}>
|
<Headline menu={menu_list()}>
|
||||||
<ContentInfo document={info.doc}></ContentInfo>
|
<ContentInfo document={info.doc}></ContentInfo>
|
||||||
</Headline>
|
</Headline>
|
||||||
</Route>
|
);
|
||||||
<Route path={`:id/reader`}>
|
|
||||||
<Headline menu={menu_list(location.pathname)}>
|
|
||||||
<ReaderPage doc={info.doc}></ReaderPage>
|
|
||||||
</Headline>
|
|
||||||
</Route>
|
|
||||||
<Route>
|
|
||||||
<NotFoundPage></NotFoundPage>
|
|
||||||
</Route>
|
|
||||||
</Routes>);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
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, Typography, Chip } from '@mui/material';
|
import { Box, Typography, Chip, Pagination } from '@mui/material';
|
||||||
import ContentAccessor,{QueryListOption, Document} from '../accessor/document';
|
import ContentAccessor,{QueryListOption, Document} from '../accessor/document';
|
||||||
import {toQueryString} from '../accessor/util';
|
import {toQueryString} from '../accessor/util';
|
||||||
|
|
||||||
@ -62,5 +62,6 @@ export const Gallery = ()=>{
|
|||||||
option.limit = typeof query['limit'] === "string" ? parseInt(query['limit']) : undefined;
|
option.limit = typeof query['limit'] === "string" ? parseInt(query['limit']) : undefined;
|
||||||
return (<Headline menu={menu_list}>
|
return (<Headline menu={menu_list}>
|
||||||
<GalleryInfo diff={location.search} option={query}></GalleryInfo>
|
<GalleryInfo diff={location.search} option={query}></GalleryInfo>
|
||||||
|
|
||||||
</Headline>)
|
</Headline>)
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react';
|
|||||||
import { Typography, useTheme } from '@mui/material';
|
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";
|
||||||
|
|
||||||
export type PresentableTag = {
|
export type PresentableTag = {
|
||||||
artist:string[],
|
artist:string[],
|
||||||
|
@ -16,7 +16,11 @@ class KnexDocumentAccessor implements DocumentAccessor{
|
|||||||
this.tagController = createKnexTagController(knex);
|
this.tagController = createKnexTagController(knex);
|
||||||
}
|
}
|
||||||
async search(search_word: string): Promise<Document[]>{
|
async search(search_word: string): Promise<Document[]>{
|
||||||
throw new Error("Not implemented");
|
throw new Error("Method not implemented.");
|
||||||
|
const sw = `%${search_word}%`;
|
||||||
|
const docs = await this.knex.select("*").from("document")
|
||||||
|
.where("title","like",sw);
|
||||||
|
return docs;
|
||||||
}
|
}
|
||||||
async addList(content_list: DocumentBody[]):Promise<number[]>{
|
async addList(content_list: DocumentBody[]):Promise<number[]>{
|
||||||
return await this.knex.transaction(async (trx)=>{
|
return await this.knex.transaction(async (trx)=>{
|
||||||
|
Loading…
Reference in New Issue
Block a user