single source search done

This commit is contained in:
Aria Moradi 2021-01-22 18:07:31 +03:30
parent 6401b946b6
commit 7d1c63e181
10 changed files with 106 additions and 65 deletions

View File

@ -119,10 +119,11 @@ class Main {
}
// single source search
app.get("/api/v1/source/:sourceId/search/:searchTerm") { ctx ->
app.get("/api/v1/source/:sourceId/search/:searchTerm/:pageNum") { ctx ->
val sourceId = ctx.pathParam("sourceId").toLong()
val searchTerm = ctx.pathParam("searchTerm")
ctx.json(sourceSearch(sourceId, searchTerm))
val pageNum = ctx.pathParam("pageNum").toInt()
ctx.json(sourceSearch(sourceId, searchTerm, pageNum))
}
// source filter list

View File

@ -8,7 +8,7 @@ data class MangaDataClass(
val url: String,
val title: String,
val thumbnail_url: String? = null,
val thumbnailUrl: String? = null,
val initialized: Boolean = false,

View File

@ -1,5 +1,6 @@
package ir.armor.tachidesk.util
import eu.kanade.tachiyomi.source.model.MangasPage
import ir.armor.tachidesk.database.dataclass.MangaDataClass
import ir.armor.tachidesk.database.table.MangaStatus
import ir.armor.tachidesk.database.table.MangaTable
@ -17,6 +18,11 @@ fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): List<Manga
else
throw Exception("Source $source doesn't support latest")
}
return mangasPage.processEntries(sourceId)
}
fun MangasPage.processEntries(sourceId: Long): List<MangaDataClass> {
val mangasPage = this
return transaction {
return@transaction mangasPage.mangas.map { manga ->
var mangaEntry = MangaTable.select { MangaTable.url eq manga.url }.firstOrNull()

View File

@ -1,13 +1,16 @@
package ir.armor.tachidesk.util
import ir.armor.tachidesk.database.dataclass.MangaDataClass
fun sourceFilters(sourceId: Long) {
val source = getHttpSource(sourceId)
// source.getFilterList().toItems()
}
fun sourceSearch(sourceId: Long, searchTerm: String) {
fun sourceSearch(sourceId: Long, searchTerm: String, pageNum: Int): List<MangaDataClass> {
val source = getHttpSource(sourceId)
// source.fetchSearchManga()
val searchManga = source.fetchSearchManga(pageNum, searchTerm, source.getFilterList()).toBlocking().first()
return searchManga.processEntries(sourceId)
}
fun sourceGlobalSearch(searchTerm: String) {
@ -18,7 +21,7 @@ data class FilterWrapper(
val filter: Any
)
// private fun FilterList.toItems(): List<FilterWrapper> {
// private fun FilterList.toFilterWrapper(): List<FilterWrapper> {
// return mapNotNull { filter ->
// when (filter) {
// is Filter.Header -> FilterWrapper("Header",filter)

View File

@ -9,7 +9,7 @@ import Extensions from './screens/Extensions';
import MangaList from './screens/MangaList';
import Manga from './screens/Manga';
import Reader from './screens/Reader';
import Search from './screens/Search';
import Search from './screens/SearchSingle';
import NavBarTitle from './context/NavbarTitle';
export default function App() {
@ -22,7 +22,7 @@ export default function App() {
<NavBar />
<Switch>
<Route path="/search">
<Route path="/sources/:sourceId/search/">
<Search />
</Route>
<Route path="/extensions">

View File

@ -65,8 +65,9 @@ export default function SourceCard(props: IProps) {
</div>
</div>
<div style={{ display: 'flex' }}>
{supportsLatest && <Button variant="outlined" style={{ marginLeft: 20 }} onClick={() => { window.location.href = `sources/${id}/latest/`; }}>Latest</Button>}
<Button variant="outlined" style={{ marginLeft: 20 }} onClick={() => { window.location.href = `sources/${id}/popular/`; }}>Browse</Button>
<Button variant="outlined" style={{ marginLeft: 20 }} onClick={() => { window.location.href = `/sources/${id}/search/`; }}>Search</Button>
{supportsLatest && <Button variant="outlined" style={{ marginLeft: 20 }} onClick={() => { window.location.href = `/sources/${id}/latest/`; }}>Latest</Button>}
<Button variant="outlined" style={{ marginLeft: 20 }} onClick={() => { window.location.href = `/sources/${id}/popular/`; }}>Browse</Button>
</div>
</CardContent>
</Card>

View File

@ -48,14 +48,14 @@ export default function TemporaryDrawer({ drawerOpen, setDrawerOpen }: IProps) {
<ListItemText primary="Sources" />
</ListItem>
</Link>
<Link to="/search" style={{ color: 'inherit', textDecoration: 'none' }}>
{/* <Link to="/search" style={{ color: 'inherit', textDecoration: 'none' }}>
<ListItem button key="Search">
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Global Search" />
</ListItem>
</Link>
</Link> */}
</List>
</div>
);

View File

@ -19,8 +19,8 @@ export default function MangaList(props: { popular: boolean }) {
const sourceType = props.popular ? 'popular' : 'latest';
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}/${sourceType}/${lastPageNum}`)
.then((response) => response.json())
.then((data: { title: string, thumbnail_url: string, id:number }[]) => setMangas(
data.map((it) => ({ title: it.title, thumbnailUrl: it.thumbnail_url, id: it.id })),
.then((data: IManga[]) => setMangas(
data.map((it) => ({ title: it.title, thumbnailUrl: it.thumbnailUrl, id: it.id })),
));
}, []);

View File

@ -1,51 +0,0 @@
import React, { useContext, useState } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import MangaGrid from '../components/MangaGrid';
import NavBarTitle from '../context/NavbarTitle';
const useStyles = makeStyles((theme) => ({
root: {
TextField: {
margin: theme.spacing(1),
width: '25ch',
},
},
}));
export default function Search() {
const { setTitle } = useContext(NavBarTitle);
setTitle('Search');
const classes = useStyles();
const [error, setError] = useState<boolean>(false);
const [mangas, setMangas] = useState<IManga[]>([]);
const [message, setMessage] = useState<string>('');
const textInput = React.createRef<HTMLInputElement>();
function doSearch() {
if (textInput.current) {
const { value } = textInput.current;
if (value === '') { setError(true); } else {
setError(false);
setMangas([]);
setMessage('button pressed');
}
}
}
const mangaGrid = <MangaGrid mangas={mangas} message={message} />;
return (
<>
<form className={classes.root} noValidate autoComplete="off">
<TextField inputRef={textInput} error={error} id="standard-basic" label="Search text.." />
<Button variant="contained" color="primary" onClick={() => doSearch()}>
Primary
</Button>
</form>
{mangaGrid}
</>
);
}

View File

@ -0,0 +1,81 @@
import React, { useContext, useEffect, useState } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import { useParams } from 'react-router-dom';
import MangaGrid from '../components/MangaGrid';
import NavBarTitle from '../context/NavbarTitle';
const useStyles = makeStyles((theme) => ({
root: {
TextField: {
margin: theme.spacing(1),
width: '25ch',
},
},
}));
export default function SearchSingle() {
const { setTitle } = useContext(NavBarTitle);
const { sourceId } = useParams<{sourceId: string}>();
const classes = useStyles();
const [error, setError] = useState<boolean>(false);
const [mangas, setMangas] = useState<IManga[]>([]);
const [message, setMessage] = useState<string>('');
const [lastPageNum] = useState<number>(1);
const [searchTerm, setSearchTerm] = useState<string>('');
const textInput = React.createRef<HTMLInputElement>();
useEffect(() => {
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}`)
.then((response) => response.json())
.then((data: { name: string }) => setTitle(`Search: ${data.name}`));
}, []);
function processInput() {
if (textInput.current) {
const { value } = textInput.current;
if (value === '') {
setError(true);
setMessage('Type something to search');
} else {
setError(false);
setSearchTerm(value);
setMessage('');
}
}
}
useEffect(() => {
if (searchTerm.length > 0) {
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}/search/${searchTerm}/${lastPageNum}`)
.then((response) => response.json())
.then((data: IManga[]) => {
if (data.length > 0) {
setMangas(
data.map((it) => (
{ title: it.title, thumbnailUrl: it.thumbnailUrl, id: it.id }
)),
);
} else {
setMessage('search qeury returned nothing.');
}
});
}
}, [searchTerm]);
const mangaGrid = <MangaGrid mangas={mangas} message={message} />;
return (
<>
<form className={classes.root} noValidate autoComplete="off">
<TextField inputRef={textInput} error={error} id="standard-basic" label="Search text.." />
<Button variant="contained" color="primary" onClick={() => processInput()}>
Primary
</Button>
</form>
{mangaGrid}
</>
);
}