import React, { ReactElement, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import AppState from 'App/State/AppState'; import * as commandNames from 'Commands/commandNames'; import Alert from 'Components/Alert'; import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import FilterMenu from 'Components/Menu/FilterMenu'; import PageContent from 'Components/Page/PageContent'; import PageContentBody from 'Components/Page/PageContentBody'; 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 Table from 'Components/Table/Table'; import TableBody from 'Components/Table/TableBody'; import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; import TablePager from 'Components/Table/TablePager'; import usePaging from 'Components/Table/usePaging'; import useCurrentPage from 'Helpers/Hooks/useCurrentPage'; import useSelectState from 'Helpers/Hooks/useSelectState'; import { align, icons, kinds } from 'Helpers/Props'; import createMoviesFetchingSelector from 'Movie/createMoviesFetchingSelector'; import { executeCommand } from 'Store/Actions/commandActions'; import { clearQueue, fetchQueue, gotoQueuePage, grabQueueItems, removeQueueItems, setQueueFilter, setQueueSort, setQueueTableOption, } from 'Store/Actions/queueActions'; import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector'; import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import { CheckInputChanged } from 'typings/inputs'; import { SelectStateInputProps } from 'typings/props'; import { TableOptionsChangePayload } from 'typings/Table'; import { registerPagePopulator, unregisterPagePopulator, } from 'Utilities/pagePopulator'; import translate from 'Utilities/String/translate'; import getSelectedIds from 'Utilities/Table/getSelectedIds'; import QueueFilterModal from './QueueFilterModal'; import QueueOptions from './QueueOptions'; import QueueRow from './QueueRow'; import RemoveQueueItemModal, { RemovePressProps } from './RemoveQueueItemModal'; import createQueueStatusSelector from './Status/createQueueStatusSelector'; function Queue() { const requestCurrentPage = useCurrentPage(); const dispatch = useDispatch(); const { isFetching, isPopulated, error, items, columns, selectedFilterKey, filters, sortKey, sortDirection, page, pageSize, totalPages, totalRecords, isGrabbing, isRemoving, } = useSelector((state: AppState) => state.queue.paged); const { count } = useSelector(createQueueStatusSelector()); const { isMoviesFetching, isMoviesPopulated, moviesError } = useSelector( createMoviesFetchingSelector() ); const customFilters = useSelector(createCustomFiltersSelector('queue')); const isRefreshMonitoredDownloadsExecuting = useSelector( createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS) ); const shouldBlockRefresh = useRef(false); const currentQueue = useRef(null); const [selectState, setSelectState] = useSelectState(); const { allSelected, allUnselected, selectedState } = selectState; const selectedIds = useMemo(() => { return getSelectedIds(selectedState); }, [selectedState]); const isPendingSelected = useMemo(() => { return items.some((item) => { return selectedIds.indexOf(item.id) > -1 && item.status === 'delay'; }); }, [items, selectedIds]); const [isConfirmRemoveModalOpen, setIsConfirmRemoveModalOpen] = useState(false); const isRefreshing = isFetching || isMoviesFetching || isRefreshMonitoredDownloadsExecuting; const isAllPopulated = isPopulated && (isMoviesPopulated || !items.length || items.every((m) => !m.movieId)); const hasError = error || moviesError; const selectedCount = selectedIds.length; const disableSelectedActions = selectedCount === 0; const handleSelectAllChange = useCallback( ({ value }: CheckInputChanged) => { setSelectState({ type: value ? 'selectAll' : 'unselectAll', items }); }, [items, setSelectState] ); const handleSelectedChange = useCallback( ({ id, value, shiftKey = false }: SelectStateInputProps) => { setSelectState({ type: 'toggleSelected', items, id, isSelected: value, shiftKey, }); }, [items, setSelectState] ); const handleRefreshPress = useCallback(() => { dispatch( executeCommand({ name: commandNames.REFRESH_MONITORED_DOWNLOADS, }) ); }, [dispatch]); const handleQueueRowModalOpenOrClose = useCallback((isOpen: boolean) => { shouldBlockRefresh.current = isOpen; }, []); const handleGrabSelectedPress = useCallback(() => { dispatch(grabQueueItems({ ids: selectedIds })); }, [selectedIds, dispatch]); const handleRemoveSelectedPress = useCallback(() => { shouldBlockRefresh.current = true; setIsConfirmRemoveModalOpen(true); }, [setIsConfirmRemoveModalOpen]); const handleRemoveSelectedConfirmed = useCallback( (payload: RemovePressProps) => { shouldBlockRefresh.current = false; dispatch(removeQueueItems({ ids: selectedIds, ...payload })); setIsConfirmRemoveModalOpen(false); }, [selectedIds, setIsConfirmRemoveModalOpen, dispatch] ); const handleConfirmRemoveModalClose = useCallback(() => { shouldBlockRefresh.current = false; setIsConfirmRemoveModalOpen(false); }, [setIsConfirmRemoveModalOpen]); const { handleFirstPagePress, handlePreviousPagePress, handleNextPagePress, handleLastPagePress, handlePageSelect, } = usePaging({ page, totalPages, gotoPage: gotoQueuePage, }); const handleFilterSelect = useCallback( (selectedFilterKey: string | number) => { dispatch(setQueueFilter({ selectedFilterKey })); }, [dispatch] ); const handleSortPress = useCallback( (sortKey: string) => { dispatch(setQueueSort({ sortKey })); }, [dispatch] ); const handleTableOptionChange = useCallback( (payload: TableOptionsChangePayload) => { dispatch(setQueueTableOption(payload)); if (payload.pageSize) { dispatch(gotoQueuePage({ page: 1 })); } }, [dispatch] ); useEffect(() => { if (requestCurrentPage) { dispatch(fetchQueue()); } else { dispatch(gotoQueuePage({ page: 1 })); } return () => { dispatch(clearQueue()); }; }, [requestCurrentPage, dispatch]); useEffect(() => { const repopulate = () => { dispatch(fetchQueue()); }; registerPagePopulator(repopulate); return () => { unregisterPagePopulator(repopulate); }; }, [dispatch]); if (!shouldBlockRefresh.current) { currentQueue.current = ( {isRefreshing && !isAllPopulated ? : null} {!isRefreshing && hasError ? ( {translate('QueueLoadError')} ) : null} {isAllPopulated && !hasError && !items.length ? ( {selectedFilterKey !== 'all' && count > 0 ? translate('QueueFilterHasNoItems') : translate('QueueIsEmpty')} ) : null} {isAllPopulated && !hasError && !!items.length ? (
{items.map((item) => { return ( ); })}
) : null}
); } return ( {currentQueue.current} { const item = items.find((i) => i.id === id); return !!(item && item.downloadClientHasPostImportCategory); }) } canIgnore={ isConfirmRemoveModalOpen && selectedIds.every((id) => { const item = items.find((i) => i.id === id); return !!(item && item.movieId); }) } isPending={ isConfirmRemoveModalOpen && selectedIds.every((id) => { const item = items.find((i) => i.id === id); if (!item) { return false; } return ( item.status === 'delay' || item.status === 'downloadClientUnavailable' ); }) } onRemovePress={handleRemoveSelectedConfirmed} onModalClose={handleConfirmRemoveModalClose} /> ); } export default Queue;