From 63d9a64542924e43ad367ffdfd73e603dd18290a Mon Sep 17 00:00:00 2001 From: Aria Moradi Date: Wed, 10 Nov 2021 23:04:04 +0330 Subject: [PATCH] Updates pagination update (#68) * make it work * refactor & update styling to mui v5 * ditch styled, this looks bad --- src/screens/Updates.tsx | 142 +++++++++++++++++++++++----------------- src/typings.d.ts | 5 ++ 2 files changed, 87 insertions(+), 60 deletions(-) diff --git a/src/screens/Updates.tsx b/src/screens/Updates.tsx index e6f14061..b82f2af9 100644 --- a/src/screens/Updates.tsx +++ b/src/screens/Updates.tsx @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-shadow */ -/* eslint-disable react/destructuring-assignment */ -/* eslint-disable react/jsx-props-no-spreading */ /* * Copyright (C) Contributors to the Suwayomi project * @@ -8,9 +5,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import React, { useContext, useEffect, useState } from 'react'; +import React, { + useContext, useEffect, useState, useRef, +} from 'react'; import { useHistory } from 'react-router-dom'; -import makeStyles from '@mui/styles/makeStyles'; import Card from '@mui/material/Card'; import CardContent from '@mui/material/CardContent'; import IconButton from '@mui/material/IconButton'; @@ -20,44 +18,8 @@ import Typography from '@mui/material/Typography'; import NavbarContext from 'components/context/NavbarContext'; import client from 'util/client'; import useLocalStorage from 'util/useLocalStorage'; - -const useStyles = makeStyles((theme) => ({ - root: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - padding: 16, - }, - bullet: { - display: 'inline-block', - margin: '0 2px', - transform: 'scale(0.8)', - }, - title: { - fontSize: 14, - }, - pos: { - marginBottom: 12, - }, - icon: { - width: theme.spacing(7), - height: theme.spacing(7), - flex: '0 0 auto', - marginRight: 16, - imageRendering: 'pixelated', - }, - card: { - margin: '10px', - '&:hover': { - backgroundColor: theme.palette.action.hover, - transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', - }, - '&:active': { - backgroundColor: theme.palette.action.selected, - transition: 'background-color 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', - }, - }, -})); +import EmptyView from 'components/util/EmptyView'; +import LoadingPlaceholder from 'components/util/LoadingPlaceholder'; function epochToDate(epoch: number) { const date = new Date(0); // The 0 there is the key, which sets the date to the epoch @@ -81,16 +43,17 @@ function getDateString(date: Date) { return date.toLocaleDateString(); } -function groupByDate(updates: IMangaChapter[]): [string, IMangaChapter[]][] { +function groupByDate(updates: IMangaChapter[]): +[string, { item: IMangaChapter, globalIdx: number }[] ][] { if (updates.length === 0) return []; const groups = {}; - updates.forEach((item) => { + updates.forEach((item, globalIdx) => { const key = getDateString(epochToDate(item.chapter.fetchedAt)); // @ts-ignore if (groups[key] === undefined) groups[key] = []; // @ts-ignore - groups[key].push(item); + groups[key].push({ item, globalIdx }); }); // @ts-ignore @@ -104,11 +67,13 @@ const initialQueue = { } as IQueue; export default function Updates() { - const classes = useStyles(); const history = useHistory(); const { setTitle, setAction } = useContext(NavbarContext); const [updateEntries, setUpdateEntries] = useState([]); + const [hasNextPage, setHasNextPage] = useState(true); + const [fetched, setFetched] = useState(false); + const [lastPageNum, setLastPageNum] = useState(0); const [serverAddress] = useLocalStorage('serverBaseURL', ''); const [useCache] = useLocalStorage('useCache', true); @@ -135,12 +100,39 @@ export default function Updates() { }, []); useEffect(() => { - client.get('/api/v1/update/recentChapters') - .then((response) => response.data) - .then((updates: IMangaChapter[]) => { - setUpdateEntries(updates); - }); - }, []); + if (hasNextPage) { + client.get(`/api/v1/update/recentChapters/${lastPageNum}`) + .then((response) => response.data) + .then(({ hasNextPage: fetchedHasNextPage, page }: PaginatedList) => { + setUpdateEntries([ + ...updateEntries, + ...page, + ]); + setHasNextPage(fetchedHasNextPage); + setFetched(true); + }); + } + }, [lastPageNum]); + + const lastEntry = useRef(null); + + const scrollHandler = () => { + if (lastEntry.current) { + const rect = lastEntry.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, updateEntries]); + + if (!fetched) { return ; } + if (fetched && updateEntries.length === 0) { return ; } const downloadStatusStringFor = (chapter: IChapter) => { let rtn = ''; @@ -163,20 +155,50 @@ export default function Updates() { <> {groupByDate(updateEntries).map((dateGroup) => (
-

+ {dateGroup[0]} -

- {dateGroup[1].map(({ chapter, manga }) => ( + + {dateGroup[1].map(({ item: { chapter, manga }, globalIdx }) => ( history.push(`/manga/${chapter.mangaId}/chapter/${chapter.index}`)} > - +
diff --git a/src/typings.d.ts b/src/typings.d.ts index 1bbf9a55..b7b1d270 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -203,3 +203,8 @@ interface NavbarItem { IconComponent: OverridableComponent>, show: 'mobile' | 'desktop' | 'both' } + +interface PaginatedList { + page: T[], + hasNextPage: boolean +}