/* * Copyright (C) Contributors to the Suwayomi project * * This Source Code Form is subject to the terms of the Mozilla Public * 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 DownloadIcon from '@mui/icons-material/Download'; import { CardActionArea } from '@mui/material'; import Avatar from '@mui/material/Avatar'; import Card from '@mui/material/Card'; import CardContent from '@mui/material/CardContent'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; import { Box } from '@mui/system'; import NavbarContext from 'components/context/NavbarContext'; import DownloadStateIndicator from 'components/molecules/DownloadStateIndicator'; import EmptyView from 'components/util/EmptyView'; import LoadingPlaceholder from 'components/util/LoadingPlaceholder'; import React, { useContext, useEffect, useRef, useState } from 'react'; import { Link, useHistory } from 'react-router-dom'; import client from 'util/client'; import useLocalStorage from 'util/useLocalStorage'; function epochToDate(epoch: number) { const date = new Date(0); // The 0 there is the key, which sets the date to the epoch date.setUTCSeconds(epoch); return date; } function isTheSameDay(first: Date, second: Date) { return ( first.getDate() === second.getDate() && first.getMonth() === second.getMonth() && first.getFullYear() === second.getFullYear() ); } function getDateString(date: Date) { const today = new Date(); if (isTheSameDay(today, date)) return 'TODAY'; // calculate yesterday const yesterday = new Date(); yesterday.setDate(today.getDate() - 1); if (isTheSameDay(yesterday, date)) return 'YESTERDAY'; return date.toLocaleDateString(); } function groupByDate(updates: IMangaChapter[]): [string, { item: IMangaChapter; globalIdx: number }[]][] { if (updates.length === 0) return []; const groups = {}; 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, globalIdx }); }); // @ts-ignore return Object.keys(groups).map((key) => [key, groups[key]]); } const baseWebsocketUrl = JSON.parse(window.localStorage.getItem('serverBaseURL')!).replace('http', 'ws'); const initialQueue = { status: 'Stopped', queue: [], } as IQueue; const Updates: React.FC = () => { 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); const [, setWsClient] = useState(); const [{ queue }, setQueueState] = useState(initialQueue); useEffect(() => { const wsc = new WebSocket(`${baseWebsocketUrl}/api/v1/downloads`); wsc.onmessage = (e) => { const data = JSON.parse(e.data) as IQueue; setQueueState(data); }; setWsClient(wsc); return () => wsc.close(); }, []); useEffect(() => { setTitle('Updates'); setAction(<>); }, []); useEffect(() => { 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 downloadForChapter = (chapter: IChapter) => { const { index, mangaId } = chapter; return queue.find((q) => index === q.chapterIndex && mangaId === q.mangaId); }; const downloadChapter = (chapter: IChapter) => { client.get(`/api/v1/download/${chapter.mangaId}/chapter/${chapter.index}`); }; return ( <> {groupByDate(updateEntries).map((dateGroup) => (
{dateGroup[0]} {dateGroup[1].map(({ item: { chapter, manga }, globalIdx }) => { const download = downloadForChapter(chapter); return ( {manga.title} {chapter.name} {download && } {download == null && !chapter.downloaded && ( { e.stopPropagation(); e.preventDefault(); downloadChapter(chapter); }} size="large" > )} ); })}
))} ); }; export default Updates;