import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import QueueDetailsProvider from 'Activity/Queue/Details/QueueDetailsProvider'; import { SelectProvider } from 'App/SelectContext'; import ClientSideCollectionAppState from 'App/State/ClientSideCollectionAppState'; import SeriesAppState, { SeriesIndexAppState } from 'App/State/SeriesAppState'; import { RSS_SYNC } from 'Commands/commandNames'; import Alert from 'Components/Alert'; import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import PageContent from 'Components/Page/PageContent'; import PageContentBody from 'Components/Page/PageContentBody'; import PageJumpBar, { PageJumpBarItems } from 'Components/Page/PageJumpBar'; import PageToolbar from 'Components/Page/Toolbar/PageToolbar'; import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection'; import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; import withScrollPosition from 'Components/withScrollPosition'; import { align, icons, kinds } from 'Helpers/Props'; import { DESCENDING } from 'Helpers/Props/sortDirections'; import ParseToolbarButton from 'Parse/ParseToolbarButton'; import NoSeries from 'Series/NoSeries'; import { executeCommand } from 'Store/Actions/commandActions'; import { fetchSeries } from 'Store/Actions/seriesActions'; import { setSeriesFilter, setSeriesSort, setSeriesTableOption, setSeriesView, } from 'Store/Actions/seriesIndexActions'; import scrollPositions from 'Store/scrollPositions'; import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createSeriesClientSideCollectionItemsSelector from 'Store/Selectors/createSeriesClientSideCollectionItemsSelector'; import translate from 'Utilities/String/translate'; import SeriesIndexFilterMenu from './Menus/SeriesIndexFilterMenu'; import SeriesIndexSortMenu from './Menus/SeriesIndexSortMenu'; import SeriesIndexViewMenu from './Menus/SeriesIndexViewMenu'; import SeriesIndexOverviewOptionsModal from './Overview/Options/SeriesIndexOverviewOptionsModal'; import SeriesIndexOverviews from './Overview/SeriesIndexOverviews'; import SeriesIndexPosterOptionsModal from './Posters/Options/SeriesIndexPosterOptionsModal'; import SeriesIndexPosters from './Posters/SeriesIndexPosters'; import SeriesIndexSelectAllButton from './Select/SeriesIndexSelectAllButton'; import SeriesIndexSelectAllMenuItem from './Select/SeriesIndexSelectAllMenuItem'; import SeriesIndexSelectFooter from './Select/SeriesIndexSelectFooter'; import SeriesIndexSelectModeButton from './Select/SeriesIndexSelectModeButton'; import SeriesIndexSelectModeMenuItem from './Select/SeriesIndexSelectModeMenuItem'; import SeriesIndexFooter from './SeriesIndexFooter'; import SeriesIndexRefreshSeriesButton from './SeriesIndexRefreshSeriesButton'; import SeriesIndexTable from './Table/SeriesIndexTable'; import SeriesIndexTableOptions from './Table/SeriesIndexTableOptions'; import styles from './SeriesIndex.css'; function getViewComponent(view: string) { if (view === 'posters') { return SeriesIndexPosters; } if (view === 'overview') { return SeriesIndexOverviews; } return SeriesIndexTable; } interface SeriesIndexProps { initialScrollTop?: number; } const SeriesIndex = withScrollPosition((props: SeriesIndexProps) => { const { isFetching, isPopulated, error, totalItems, items, columns, selectedFilterKey, filters, customFilters, sortKey, sortDirection, view, }: SeriesAppState & SeriesIndexAppState & ClientSideCollectionAppState = useSelector(createSeriesClientSideCollectionItemsSelector('seriesIndex')); const isRssSyncExecuting = useSelector( createCommandExecutingSelector(RSS_SYNC) ); const { isSmallScreen } = useSelector(createDimensionsSelector()); const dispatch = useDispatch(); const scrollerRef = useRef(null); const [isOptionsModalOpen, setIsOptionsModalOpen] = useState(false); const [jumpToCharacter, setJumpToCharacter] = useState( undefined ); const [isSelectMode, setIsSelectMode] = useState(false); useEffect(() => { dispatch(fetchSeries()); }, [dispatch]); const onRssSyncPress = useCallback(() => { dispatch( executeCommand({ name: RSS_SYNC, }) ); }, [dispatch]); const onSelectModePress = useCallback(() => { setIsSelectMode(!isSelectMode); }, [isSelectMode, setIsSelectMode]); const onTableOptionChange = useCallback( (payload: unknown) => { dispatch(setSeriesTableOption(payload)); }, [dispatch] ); const onViewSelect = useCallback( (value: string) => { dispatch(setSeriesView({ view: value })); if (scrollerRef.current) { scrollerRef.current.scrollTo(0, 0); } }, [scrollerRef, dispatch] ); const onSortSelect = useCallback( (value: string) => { dispatch(setSeriesSort({ sortKey: value })); }, [dispatch] ); const onFilterSelect = useCallback( (value: string | number) => { dispatch(setSeriesFilter({ selectedFilterKey: value })); }, [dispatch] ); const onOptionsPress = useCallback(() => { setIsOptionsModalOpen(true); }, [setIsOptionsModalOpen]); const onOptionsModalClose = useCallback(() => { setIsOptionsModalOpen(false); }, [setIsOptionsModalOpen]); const onJumpBarItemPress = useCallback( (character: string) => { setJumpToCharacter(character); }, [setJumpToCharacter] ); const onScroll = useCallback( ({ scrollTop }: { scrollTop: number }) => { setJumpToCharacter(undefined); scrollPositions.seriesIndex = scrollTop; }, [setJumpToCharacter] ); const jumpBarItems: PageJumpBarItems = useMemo(() => { // Reset if not sorting by sortTitle if (sortKey !== 'sortTitle') { return { characters: {}, order: [], }; } const characters = items.reduce((acc: Record, item) => { let char = item.sortTitle.charAt(0); if (!isNaN(Number(char))) { char = '#'; } if (char in acc) { acc[char] = acc[char] + 1; } else { acc[char] = 1; } return acc; }, {}); const order = Object.keys(characters).sort(); // Reverse if sorting descending if (sortDirection === DESCENDING) { order.reverse(); } return { characters, order, }; }, [items, sortKey, sortDirection]); const ViewComponent = useMemo(() => getViewComponent(view), [view]); const isLoaded = !!(!error && isPopulated && items.length); const hasNoSeries = !totalItems; return ( {view === 'table' ? ( ) : ( )}
{isFetching && !isPopulated ? : null} {!isFetching && !!error ? ( {translate('SeriesLoadError')} ) : null} {isLoaded ? (
) : null} {!error && isPopulated && !items.length ? ( ) : null}
{isLoaded && !!jumpBarItems.order.length ? ( ) : null}
{isSelectMode ? : null} {view === 'posters' ? ( ) : null} {view === 'overview' ? ( ) : null}
); }, 'seriesIndex'); export default SeriesIndex;