/* * 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 { Tab, Tabs } from '@mui/material'; import React, { useContext, useEffect, useState } from 'react'; import NavbarContext from 'components/context/NavbarContext'; import client from 'util/client'; import cloneObject from 'util/cloneObject'; import EmptyView from 'components/util/EmptyView'; import LoadingPlaceholder from 'components/util/LoadingPlaceholder'; import TabPanel from 'components/util/TabPanel'; import LibraryOptions from 'components/library/LibraryOptions'; import LibraryMangaGrid from 'components/library/LibraryMangaGrid'; import LibrarySearch from 'components/library/LibrarySearch'; import { useHistory, useParams } from 'react-router-dom'; interface IMangaCategory { category: ICategory mangas: IManga[] isFetched: boolean } export default function Library() { const { setTitle, setAction } = useContext(NavbarContext); useEffect(() => { setTitle('Library'); setAction( <> , ); }, []); const { tabParamNumber } = useParams<{ tabParamNumber: string }>(); const [tabs, setTabs] = useState(); const [tabNum, setTabNum] = useState(0); const history = useHistory(); // a hack so MangaGrid doesn't stop working. I won't change it in case // if I do manga pagination for library.. const [lastPageNum, setLastPageNum] = useState(1); const handleTabChange = (newTab: number) => { // eslint-disable-next-line @typescript-eslint/no-unused-expressions history.location.search === '' ? history.replace(`/library/${newTab}`) : history.replace(`/library/${newTab}/${history.location.search}`); setTabNum(newTab); }; useEffect(() => { client.get('/api/v1/category') .then((response) => response.data) .then((categories: ICategory[]) => { const categoryTabs = categories.map((category) => ({ category, mangas: [] as IManga[], isFetched: false, })); setTabs(categoryTabs); if (categoryTabs.length > 0) { setTabNum(() => { if (tabParamNumber !== undefined && !Number.isNaN(tabParamNumber) && !!categories.find((cat) => cat.order === Number(tabParamNumber))) { return Number(tabParamNumber); } history.replace('/library'); return categoryTabs[0].category.order; }); } }); }, []); // fetch the current tab useEffect(() => { if (tabs !== undefined) { tabs.forEach((tab, index) => { if (tab.category.order === tabNum && !tab.isFetched) { // eslint-disable-next-line @typescript-eslint/no-shadow client.get(`/api/v1/category/${tab.category.id}`) .then((response) => response.data) .then((data: IManga[]) => { const tabsClone = cloneObject(tabs); tabsClone[index].mangas = data; tabsClone[index].isFetched = true; setTabs(tabsClone); }); } }); } }, [tabs?.length, tabNum]); if (tabs === undefined) { return ; } if (tabs.length === 0) { return ; } let toRender; if (tabs.length > 1) { // eslint-disable-next-line max-len const tabDefines = tabs.map((tab) => ()); const tabBodies = tabs.map((tab) => ( )); // Visual Hack: 160px is min-width for viewport width of >600 const scrollableTabs = window.innerWidth < tabs.length * 160; toRender = ( <> handleTabChange(newTab)} indicatorColor="primary" textColor="primary" centered={!scrollableTabs} variant={scrollableTabs ? 'scrollable' : 'fullWidth'} scrollButtons allowScrollButtonsMobile > {tabDefines} {tabBodies} ); } else { const mangas = tabs.length === 1 ? tabs[0].mangas : []; toRender = ( ); } return toRender; }