mirror of
https://github.com/tachiyomiorg/tachiyomi-extensions-inspector.git
synced 2025-01-12 08:49:08 +01:00
implemented infinite scroll
This commit is contained in:
parent
2c76ad9b74
commit
0757ea5d0d
@ -18,3 +18,8 @@ data class MangaDataClass(
|
|||||||
val genre: String? = null,
|
val genre: String? = null,
|
||||||
val status: String = MangaStatus.UNKNOWN.name
|
val status: String = MangaStatus.UNKNOWN.name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class PagedMangaListDataClass(
|
||||||
|
val mangaList: List<MangaDataClass>,
|
||||||
|
val hasNextPage: Boolean
|
||||||
|
)
|
||||||
|
@ -2,13 +2,14 @@ package ir.armor.tachidesk.util
|
|||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||||
|
import ir.armor.tachidesk.database.dataclass.PagedMangaListDataClass
|
||||||
import ir.armor.tachidesk.database.table.MangaStatus
|
import ir.armor.tachidesk.database.table.MangaStatus
|
||||||
import ir.armor.tachidesk.database.table.MangaTable
|
import ir.armor.tachidesk.database.table.MangaTable
|
||||||
import org.jetbrains.exposed.sql.insertAndGetId
|
import org.jetbrains.exposed.sql.insertAndGetId
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): List<MangaDataClass> {
|
fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): PagedMangaListDataClass {
|
||||||
val source = getHttpSource(sourceId.toLong())
|
val source = getHttpSource(sourceId.toLong())
|
||||||
val mangasPage = if (popular) {
|
val mangasPage = if (popular) {
|
||||||
source.fetchPopularManga(pageNum).toBlocking().first()
|
source.fetchPopularManga(pageNum).toBlocking().first()
|
||||||
@ -21,9 +22,9 @@ fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): List<Manga
|
|||||||
return mangasPage.processEntries(sourceId)
|
return mangasPage.processEntries(sourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MangasPage.processEntries(sourceId: Long): List<MangaDataClass> {
|
fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
||||||
val mangasPage = this
|
val mangasPage = this
|
||||||
return transaction {
|
val mangaList = transaction {
|
||||||
return@transaction mangasPage.mangas.map { manga ->
|
return@transaction mangasPage.mangas.map { manga ->
|
||||||
var mangaEntry = MangaTable.select { MangaTable.url eq manga.url }.firstOrNull()
|
var mangaEntry = MangaTable.select { MangaTable.url eq manga.url }.firstOrNull()
|
||||||
var mangaEntityId = if (mangaEntry == null) { // create manga entry
|
var mangaEntityId = if (mangaEntry == null) { // create manga entry
|
||||||
@ -62,4 +63,8 @@ fun MangasPage.processEntries(sourceId: Long): List<MangaDataClass> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return PagedMangaListDataClass(
|
||||||
|
mangaList,
|
||||||
|
mangasPage.hasNextPage
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package ir.armor.tachidesk.util
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
import ir.armor.tachidesk.database.dataclass.PagedMangaListDataClass
|
||||||
|
|
||||||
fun sourceFilters(sourceId: Long) {
|
fun sourceFilters(sourceId: Long) {
|
||||||
val source = getHttpSource(sourceId)
|
val source = getHttpSource(sourceId)
|
||||||
// source.getFilterList().toItems()
|
// source.getFilterList().toItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sourceSearch(sourceId: Long, searchTerm: String, pageNum: Int): List<MangaDataClass> {
|
fun sourceSearch(sourceId: Long, searchTerm: String, pageNum: Int): PagedMangaListDataClass {
|
||||||
val source = getHttpSource(sourceId)
|
val source = getHttpSource(sourceId)
|
||||||
val searchManga = source.fetchSearchManga(pageNum, searchTerm, source.getFilterList()).toBlocking().first()
|
val searchManga = source.fetchSearchManga(pageNum, searchTerm, source.getFilterList()).toBlocking().first()
|
||||||
return searchManga.processEntries(sourceId)
|
return searchManga.processEntries(sourceId)
|
||||||
|
@ -38,8 +38,10 @@ const useStyles = makeStyles({
|
|||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
manga: IManga
|
manga: IManga
|
||||||
|
// eslint-disable-next-line react/no-unused-prop-types, react/require-default-props
|
||||||
|
// ref?: false | React.MutableRefObject<HTMLInputElement | undefined>
|
||||||
}
|
}
|
||||||
export default function MangaCard(props: IProps) {
|
const MangaCard = React.forwardRef((props: IProps, ref) => {
|
||||||
const {
|
const {
|
||||||
manga: {
|
manga: {
|
||||||
id, title, thumbnailUrl,
|
id, title, thumbnailUrl,
|
||||||
@ -49,7 +51,7 @@ export default function MangaCard(props: IProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/manga/${id}/`}>
|
<Link to={`/manga/${id}/`}>
|
||||||
<Card className={classes.root}>
|
<Card className={classes.root} ref={ref}>
|
||||||
<CardActionArea>
|
<CardActionArea>
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<CardMedia
|
<CardMedia
|
||||||
@ -66,4 +68,6 @@ export default function MangaCard(props: IProps) {
|
|||||||
</Card>
|
</Card>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
export default MangaCard;
|
||||||
|
@ -1,26 +1,54 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import MangaCard from './MangaCard';
|
import MangaCard from './MangaCard';
|
||||||
|
|
||||||
interface IProps{
|
interface IProps{
|
||||||
mangas: IManga[]
|
mangas: IManga[]
|
||||||
message?: string
|
message?: string
|
||||||
|
hasNextPage: boolean
|
||||||
|
lastPageNum: number
|
||||||
|
setLastPageNum: (lastPageNum: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MangaGrid(props: IProps) {
|
export default function MangaGrid(props: IProps) {
|
||||||
const { mangas, message } = props;
|
const {
|
||||||
|
mangas, message, hasNextPage, lastPageNum, setLastPageNum,
|
||||||
|
} = props;
|
||||||
let mapped;
|
let mapped;
|
||||||
|
const lastManga = useRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
const scrollHandler = () => {
|
||||||
|
if (lastManga.current) {
|
||||||
|
const rect = lastManga.current.getBoundingClientRect();
|
||||||
|
if (((rect.y + rect.height) / window.innerHeight < 2) && hasNextPage) {
|
||||||
|
setLastPageNum(lastPageNum + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('scroll', scrollHandler, true);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', scrollHandler, true);
|
||||||
|
};
|
||||||
|
}, [hasNextPage, mangas]);
|
||||||
|
|
||||||
if (mangas.length === 0) {
|
if (mangas.length === 0) {
|
||||||
mapped = <h3>{message !== undefined ? message : 'loading...'}</h3>;
|
mapped = <h3>{message}</h3>;
|
||||||
} else {
|
} else {
|
||||||
mapped = (
|
mapped = mangas.map((it, idx) => {
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, auto)', gridGap: '1em' }}>
|
if (idx === mangas.length - 1) {
|
||||||
{mangas.map((it) => (
|
return <MangaCard manga={it} ref={lastManga} />;
|
||||||
<MangaCard manga={it} />
|
}
|
||||||
))}
|
return <MangaCard manga={it} />;
|
||||||
</div>
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapped;
|
return (
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, auto)', gridGap: '1em' }}>
|
||||||
|
{mapped}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MangaGrid.defaultProps = {
|
||||||
|
message: 'loading...',
|
||||||
|
};
|
||||||
|
@ -7,7 +7,8 @@ export default function MangaList(props: { popular: boolean }) {
|
|||||||
const { sourceId } = useParams<{sourceId: string}>();
|
const { sourceId } = useParams<{sourceId: string}>();
|
||||||
const { setTitle } = useContext(NavBarTitle);
|
const { setTitle } = useContext(NavBarTitle);
|
||||||
const [mangas, setMangas] = useState<IManga[]>([]);
|
const [mangas, setMangas] = useState<IManga[]>([]);
|
||||||
const [lastPageNum] = useState<number>(1);
|
const [hasNextPage, setHasNextPage] = useState<boolean>(false);
|
||||||
|
const [lastPageNum, setLastPageNum] = useState<number>(1);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}`)
|
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}`)
|
||||||
@ -19,10 +20,22 @@ export default function MangaList(props: { popular: boolean }) {
|
|||||||
const sourceType = props.popular ? 'popular' : 'latest';
|
const sourceType = props.popular ? 'popular' : 'latest';
|
||||||
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}/${sourceType}/${lastPageNum}`)
|
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}/${sourceType}/${lastPageNum}`)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data: IManga[]) => setMangas(
|
.then((data: { mangaList: IManga[], hasNextPage: boolean }) => {
|
||||||
data.map((it) => ({ title: it.title, thumbnailUrl: it.thumbnailUrl, id: it.id })),
|
setMangas([
|
||||||
));
|
...mangas,
|
||||||
}, []);
|
...data.mangaList.map((it) => ({
|
||||||
|
title: it.title, thumbnailUrl: it.thumbnailUrl, id: it.id,
|
||||||
|
}))]);
|
||||||
|
setHasNextPage(data.hasNextPage);
|
||||||
|
});
|
||||||
|
}, [lastPageNum]);
|
||||||
|
|
||||||
return <MangaGrid mangas={mangas} />;
|
return (
|
||||||
|
<MangaGrid
|
||||||
|
mangas={mangas}
|
||||||
|
hasNextPage={hasNextPage}
|
||||||
|
lastPageNum={lastPageNum}
|
||||||
|
setLastPageNum={setLastPageNum}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,9 @@ export default function SearchSingle() {
|
|||||||
const [error, setError] = useState<boolean>(false);
|
const [error, setError] = useState<boolean>(false);
|
||||||
const [mangas, setMangas] = useState<IManga[]>([]);
|
const [mangas, setMangas] = useState<IManga[]>([]);
|
||||||
const [message, setMessage] = useState<string>('');
|
const [message, setMessage] = useState<string>('');
|
||||||
const [lastPageNum] = useState<number>(1);
|
|
||||||
const [searchTerm, setSearchTerm] = useState<string>('');
|
const [searchTerm, setSearchTerm] = useState<string>('');
|
||||||
|
const [hasNextPage, setHasNextPage] = useState<boolean>(false);
|
||||||
|
const [lastPageNum, setLastPageNum] = useState<number>(1);
|
||||||
|
|
||||||
const textInput = React.createRef<HTMLInputElement>();
|
const textInput = React.createRef<HTMLInputElement>();
|
||||||
|
|
||||||
@ -51,13 +52,14 @@ export default function SearchSingle() {
|
|||||||
if (searchTerm.length > 0) {
|
if (searchTerm.length > 0) {
|
||||||
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}/search/${searchTerm}/${lastPageNum}`)
|
fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}/search/${searchTerm}/${lastPageNum}`)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data: IManga[]) => {
|
.then((data: { mangaList: IManga[], hasNextPage: boolean }) => {
|
||||||
if (data.length > 0) {
|
if (data.mangaList.length > 0) {
|
||||||
setMangas(
|
setMangas([
|
||||||
data.map((it) => (
|
...mangas,
|
||||||
{ title: it.title, thumbnailUrl: it.thumbnailUrl, id: it.id }
|
...data.mangaList.map((it) => ({
|
||||||
)),
|
title: it.title, thumbnailUrl: it.thumbnailUrl, id: it.id,
|
||||||
);
|
}))]);
|
||||||
|
setHasNextPage(data.hasNextPage);
|
||||||
} else {
|
} else {
|
||||||
setMessage('search qeury returned nothing.');
|
setMessage('search qeury returned nothing.');
|
||||||
}
|
}
|
||||||
@ -65,7 +67,15 @@ export default function SearchSingle() {
|
|||||||
}
|
}
|
||||||
}, [searchTerm]);
|
}, [searchTerm]);
|
||||||
|
|
||||||
const mangaGrid = <MangaGrid mangas={mangas} message={message} />;
|
const mangaGrid = (
|
||||||
|
<MangaGrid
|
||||||
|
mangas={mangas}
|
||||||
|
message={message}
|
||||||
|
hasNextPage={hasNextPage}
|
||||||
|
lastPageNum={lastPageNum}
|
||||||
|
setLastPageNum={setLastPageNum}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user