mirror of
https://github.com/Sonarr/Sonarr
synced 2025-10-05 23:52:45 +02:00
Use react-query for History UI
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import Button from 'Components/Link/Button';
|
import Button from 'Components/Link/Button';
|
||||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||||
import Modal from 'Components/Modal/Modal';
|
import Modal from 'Components/Modal/Modal';
|
||||||
@@ -9,6 +9,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
|
|||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import { HistoryData, HistoryEventType } from 'typings/History';
|
import { HistoryData, HistoryEventType } from 'typings/History';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
import { useMarkAsFailed } from '../useHistory';
|
||||||
import HistoryDetails from './HistoryDetails';
|
import HistoryDetails from './HistoryDetails';
|
||||||
import styles from './HistoryDetailsModal.css';
|
import styles from './HistoryDetailsModal.css';
|
||||||
|
|
||||||
@@ -33,26 +34,32 @@ function getHeaderTitle(eventType: HistoryEventType) {
|
|||||||
|
|
||||||
interface HistoryDetailsModalProps {
|
interface HistoryDetailsModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
id: number;
|
||||||
eventType: HistoryEventType;
|
eventType: HistoryEventType;
|
||||||
sourceTitle: string;
|
sourceTitle: string;
|
||||||
data: HistoryData;
|
data: HistoryData;
|
||||||
downloadId?: string;
|
downloadId?: string;
|
||||||
isMarkingAsFailed: boolean;
|
|
||||||
onMarkAsFailedPress: () => void;
|
|
||||||
onModalClose: () => void;
|
onModalClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function HistoryDetailsModal(props: HistoryDetailsModalProps) {
|
function HistoryDetailsModal(props: HistoryDetailsModalProps) {
|
||||||
const {
|
const { isOpen, id, eventType, sourceTitle, data, downloadId, onModalClose } =
|
||||||
isOpen,
|
props;
|
||||||
eventType,
|
|
||||||
sourceTitle,
|
const { markAsFailed, isMarkingAsFailed, markAsFailedError } =
|
||||||
data,
|
useMarkAsFailed(id);
|
||||||
downloadId,
|
|
||||||
isMarkingAsFailed = false,
|
const wasMarkingAsFailed = useRef(isMarkingAsFailed);
|
||||||
onMarkAsFailedPress,
|
|
||||||
onModalClose,
|
const handleMarkAsFailedPress = useCallback(() => {
|
||||||
} = props;
|
markAsFailed();
|
||||||
|
}, [markAsFailed]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (wasMarkingAsFailed && !isMarkingAsFailed && !markAsFailedError) {
|
||||||
|
onModalClose();
|
||||||
|
}
|
||||||
|
}, [wasMarkingAsFailed, isMarkingAsFailed, markAsFailedError, onModalClose]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onModalClose={onModalClose}>
|
<Modal isOpen={isOpen} onModalClose={onModalClose}>
|
||||||
@@ -74,7 +81,7 @@ function HistoryDetailsModal(props: HistoryDetailsModalProps) {
|
|||||||
className={styles.markAsFailedButton}
|
className={styles.markAsFailedButton}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
isSpinning={isMarkingAsFailed}
|
isSpinning={isMarkingAsFailed}
|
||||||
onPress={onMarkAsFailedPress}
|
onPress={handleMarkAsFailedPress}
|
||||||
>
|
>
|
||||||
{translate('MarkAsFailed')}
|
{translate('MarkAsFailed')}
|
||||||
</SpinnerButton>
|
</SpinnerButton>
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import AppState from 'App/State/AppState';
|
import {
|
||||||
|
setQueueOption,
|
||||||
|
setQueueOptions,
|
||||||
|
} from 'Activity/Queue/queueOptionsStore';
|
||||||
import Alert from 'Components/Alert';
|
import Alert from 'Components/Alert';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||||
@@ -13,20 +16,11 @@ import Table from 'Components/Table/Table';
|
|||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import TablePager from 'Components/Table/TablePager';
|
import TablePager from 'Components/Table/TablePager';
|
||||||
import usePaging from 'Components/Table/usePaging';
|
|
||||||
import createEpisodesFetchingSelector from 'Episode/createEpisodesFetchingSelector';
|
import createEpisodesFetchingSelector from 'Episode/createEpisodesFetchingSelector';
|
||||||
import useCurrentPage from 'Helpers/Hooks/useCurrentPage';
|
import useCurrentPage from 'Helpers/Hooks/useCurrentPage';
|
||||||
import { align, icons, kinds } from 'Helpers/Props';
|
import { align, icons, kinds } from 'Helpers/Props';
|
||||||
import { clearEpisodes, fetchEpisodes } from 'Store/Actions/episodeActions';
|
import { clearEpisodes, fetchEpisodes } from 'Store/Actions/episodeActions';
|
||||||
import { clearEpisodeFiles } from 'Store/Actions/episodeFileActions';
|
import { clearEpisodeFiles } from 'Store/Actions/episodeFileActions';
|
||||||
import {
|
|
||||||
clearHistory,
|
|
||||||
fetchHistory,
|
|
||||||
gotoHistoryPage,
|
|
||||||
setHistoryFilter,
|
|
||||||
setHistorySort,
|
|
||||||
setHistoryTableOption,
|
|
||||||
} from 'Store/Actions/historyActions';
|
|
||||||
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
|
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import HistoryItem from 'typings/History';
|
import HistoryItem from 'typings/History';
|
||||||
import { TableOptionsChangePayload } from 'typings/Table';
|
import { TableOptionsChangePayload } from 'typings/Table';
|
||||||
@@ -37,100 +31,90 @@ import {
|
|||||||
} from 'Utilities/pagePopulator';
|
} from 'Utilities/pagePopulator';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import HistoryFilterModal from './HistoryFilterModal';
|
import HistoryFilterModal from './HistoryFilterModal';
|
||||||
|
import { useHistoryOptions } from './historyOptionsStore';
|
||||||
import HistoryRow from './HistoryRow';
|
import HistoryRow from './HistoryRow';
|
||||||
|
import useHistory, { useFilters } from './useHistory';
|
||||||
|
|
||||||
function History() {
|
function History() {
|
||||||
const requestCurrentPage = useCurrentPage();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isFetching,
|
records,
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
items,
|
|
||||||
columns,
|
|
||||||
selectedFilterKey,
|
|
||||||
filters,
|
|
||||||
sortKey,
|
|
||||||
sortDirection,
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
totalPages,
|
totalPages,
|
||||||
totalRecords,
|
totalRecords,
|
||||||
} = useSelector((state: AppState) => state.history);
|
error,
|
||||||
|
isFetching,
|
||||||
|
isFetched,
|
||||||
|
isLoading,
|
||||||
|
page,
|
||||||
|
goToPage,
|
||||||
|
refetch,
|
||||||
|
} = useHistory();
|
||||||
|
|
||||||
|
const { columns, pageSize, sortKey, sortDirection, selectedFilterKey } =
|
||||||
|
useHistoryOptions();
|
||||||
|
|
||||||
|
const filters = useFilters();
|
||||||
|
|
||||||
|
const requestCurrentPage = useCurrentPage();
|
||||||
|
|
||||||
const { isEpisodesFetching, isEpisodesPopulated, episodesError } =
|
const { isEpisodesFetching, isEpisodesPopulated, episodesError } =
|
||||||
useSelector(createEpisodesFetchingSelector());
|
useSelector(createEpisodesFetchingSelector());
|
||||||
const customFilters = useSelector(createCustomFiltersSelector('history'));
|
const customFilters = useSelector(createCustomFiltersSelector('history'));
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const isFetchingAny = isFetching || isEpisodesFetching;
|
const isFetchingAny = isLoading || isEpisodesFetching;
|
||||||
const isAllPopulated = isPopulated && (isEpisodesPopulated || !items.length);
|
const isAllPopulated = isFetched && (isEpisodesPopulated || !records.length);
|
||||||
const hasError = error || episodesError;
|
const hasError = error || episodesError;
|
||||||
|
|
||||||
const {
|
|
||||||
handleFirstPagePress,
|
|
||||||
handlePreviousPagePress,
|
|
||||||
handleNextPagePress,
|
|
||||||
handleLastPagePress,
|
|
||||||
handlePageSelect,
|
|
||||||
} = usePaging({
|
|
||||||
page,
|
|
||||||
totalPages,
|
|
||||||
gotoPage: gotoHistoryPage,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleFilterSelect = useCallback(
|
const handleFilterSelect = useCallback(
|
||||||
(selectedFilterKey: string | number) => {
|
(selectedFilterKey: string | number) => {
|
||||||
dispatch(setHistoryFilter({ selectedFilterKey }));
|
setQueueOption('selectedFilterKey', selectedFilterKey);
|
||||||
},
|
},
|
||||||
[dispatch]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSortPress = useCallback(
|
const handleSortPress = useCallback((sortKey: string) => {
|
||||||
(sortKey: string) => {
|
setQueueOption('sortKey', sortKey);
|
||||||
dispatch(setHistorySort({ sortKey }));
|
}, []);
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleTableOptionChange = useCallback(
|
const handleTableOptionChange = useCallback(
|
||||||
(payload: TableOptionsChangePayload) => {
|
(payload: TableOptionsChangePayload) => {
|
||||||
dispatch(setHistoryTableOption(payload));
|
setQueueOptions(payload);
|
||||||
|
|
||||||
if (payload.pageSize) {
|
if (payload.pageSize) {
|
||||||
dispatch(gotoHistoryPage({ page: 1 }));
|
goToPage(1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[dispatch]
|
[goToPage]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const handleRefreshPress = useCallback(() => {
|
||||||
if (requestCurrentPage) {
|
goToPage(1);
|
||||||
dispatch(fetchHistory());
|
refetch();
|
||||||
} else {
|
}, [goToPage, refetch]);
|
||||||
dispatch(gotoHistoryPage({ page: 1 }));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
dispatch(clearHistory());
|
|
||||||
dispatch(clearEpisodes());
|
dispatch(clearEpisodes());
|
||||||
dispatch(clearEpisodeFiles());
|
dispatch(clearEpisodeFiles());
|
||||||
};
|
};
|
||||||
}, [requestCurrentPage, dispatch]);
|
}, [requestCurrentPage, dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const episodeIds = selectUniqueIds<HistoryItem, number>(items, 'episodeId');
|
const episodeIds = selectUniqueIds<HistoryItem, number>(
|
||||||
|
records,
|
||||||
|
'episodeId'
|
||||||
|
);
|
||||||
|
|
||||||
if (episodeIds.length) {
|
if (episodeIds.length) {
|
||||||
dispatch(fetchEpisodes({ episodeIds }));
|
dispatch(fetchEpisodes({ episodeIds }));
|
||||||
} else {
|
} else {
|
||||||
dispatch(clearEpisodes());
|
dispatch(clearEpisodes());
|
||||||
}
|
}
|
||||||
}, [items, dispatch]);
|
}, [records, dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const repopulate = () => {
|
const repopulate = () => {
|
||||||
dispatch(fetchHistory());
|
refetch();
|
||||||
};
|
};
|
||||||
|
|
||||||
registerPagePopulator(repopulate);
|
registerPagePopulator(repopulate);
|
||||||
@@ -138,7 +122,7 @@ function History() {
|
|||||||
return () => {
|
return () => {
|
||||||
unregisterPagePopulator(repopulate);
|
unregisterPagePopulator(repopulate);
|
||||||
};
|
};
|
||||||
}, [dispatch]);
|
}, [refetch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('History')}>
|
<PageContent title={translate('History')}>
|
||||||
@@ -148,7 +132,7 @@ function History() {
|
|||||||
label={translate('Refresh')}
|
label={translate('Refresh')}
|
||||||
iconName={icons.REFRESH}
|
iconName={icons.REFRESH}
|
||||||
isSpinning={isFetching}
|
isSpinning={isFetching}
|
||||||
onPress={handleFirstPagePress}
|
onPress={handleRefreshPress}
|
||||||
/>
|
/>
|
||||||
</PageToolbarSection>
|
</PageToolbarSection>
|
||||||
|
|
||||||
@@ -186,12 +170,12 @@ function History() {
|
|||||||
// If history isPopulated and it's empty show no history found and don't
|
// If history isPopulated and it's empty show no history found and don't
|
||||||
// wait for the episodes to populate because they are never coming.
|
// wait for the episodes to populate because they are never coming.
|
||||||
|
|
||||||
isPopulated && !hasError && !items.length ? (
|
isFetched && !hasError && !records.length ? (
|
||||||
<Alert kind={kinds.INFO}>{translate('NoHistoryFound')}</Alert>
|
<Alert kind={kinds.INFO}>{translate('NoHistoryFound')}</Alert>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
{isAllPopulated && !hasError && items.length ? (
|
{isAllPopulated && !hasError && records.length ? (
|
||||||
<div>
|
<div>
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -202,7 +186,7 @@ function History() {
|
|||||||
onSortPress={handleSortPress}
|
onSortPress={handleSortPress}
|
||||||
>
|
>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{items.map((item) => {
|
{records.map((item) => {
|
||||||
return (
|
return (
|
||||||
<HistoryRow key={item.id} columns={columns} {...item} />
|
<HistoryRow key={item.id} columns={columns} {...item} />
|
||||||
);
|
);
|
||||||
@@ -215,11 +199,7 @@ function History() {
|
|||||||
totalPages={totalPages}
|
totalPages={totalPages}
|
||||||
totalRecords={totalRecords}
|
totalRecords={totalRecords}
|
||||||
isFetching={isFetching}
|
isFetching={isFetching}
|
||||||
onFirstPagePress={handleFirstPagePress}
|
onPageSelect={goToPage}
|
||||||
onPreviousPagePress={handlePreviousPagePress}
|
|
||||||
onNextPagePress={handleNextPagePress}
|
|
||||||
onLastPagePress={handleLastPagePress}
|
|
||||||
onPageSelect={handlePageSelect}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@@ -1,48 +1,25 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import AppState from 'App/State/AppState';
|
|
||||||
import FilterModal, { FilterModalProps } from 'Components/Filter/FilterModal';
|
import FilterModal, { FilterModalProps } from 'Components/Filter/FilterModal';
|
||||||
import { setHistoryFilter } from 'Store/Actions/historyActions';
|
import { setHistoryOption } from './historyOptionsStore';
|
||||||
|
import useHistory, { FILTER_BUILDER } from './useHistory';
|
||||||
function createHistorySelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.history.items,
|
|
||||||
(queueItems) => {
|
|
||||||
return queueItems;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilterBuilderPropsSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.history.filterBuilderProps,
|
|
||||||
(filterBuilderProps) => {
|
|
||||||
return filterBuilderProps;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
type HistoryFilterModalProps = FilterModalProps<History>;
|
type HistoryFilterModalProps = FilterModalProps<History>;
|
||||||
|
|
||||||
export default function HistoryFilterModal(props: HistoryFilterModalProps) {
|
export default function HistoryFilterModal(props: HistoryFilterModalProps) {
|
||||||
const sectionItems = useSelector(createHistorySelector());
|
const { records } = useHistory();
|
||||||
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const dispatchSetFilter = useCallback(
|
const dispatchSetFilter = useCallback(
|
||||||
(payload: { selectedFilterKey: string | number }) => {
|
({ selectedFilterKey }: { selectedFilterKey: string | number }) => {
|
||||||
dispatch(setHistoryFilter(payload));
|
setHistoryOption('selectedFilterKey', selectedFilterKey);
|
||||||
},
|
},
|
||||||
[dispatch]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FilterModal
|
<FilterModal
|
||||||
{...props}
|
{...props}
|
||||||
sectionItems={sectionItems}
|
sectionItems={records}
|
||||||
filterBuilderProps={filterBuilderProps}
|
filterBuilderProps={FILTER_BUILDER}
|
||||||
customFilterType="history"
|
customFilterType="history"
|
||||||
dispatchSetFilter={dispatchSetFilter}
|
dispatchSetFilter={dispatchSetFilter}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
|
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
@@ -13,13 +12,11 @@ import EpisodeQuality from 'Episode/EpisodeQuality';
|
|||||||
import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
|
import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
|
||||||
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
|
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
|
||||||
import useEpisode from 'Episode/useEpisode';
|
import useEpisode from 'Episode/useEpisode';
|
||||||
import usePrevious from 'Helpers/Hooks/usePrevious';
|
|
||||||
import { icons, tooltipPositions } from 'Helpers/Props';
|
import { icons, tooltipPositions } from 'Helpers/Props';
|
||||||
import Language from 'Language/Language';
|
import Language from 'Language/Language';
|
||||||
import { QualityModel } from 'Quality/Quality';
|
import { QualityModel } from 'Quality/Quality';
|
||||||
import SeriesTitleLink from 'Series/SeriesTitleLink';
|
import SeriesTitleLink from 'Series/SeriesTitleLink';
|
||||||
import useSeries from 'Series/useSeries';
|
import useSeries from 'Series/useSeries';
|
||||||
import { fetchHistory, markAsFailed } from 'Store/Actions/historyActions';
|
|
||||||
import CustomFormat from 'typings/CustomFormat';
|
import CustomFormat from 'typings/CustomFormat';
|
||||||
import { HistoryData, HistoryEventType } from 'typings/History';
|
import { HistoryData, HistoryEventType } from 'typings/History';
|
||||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
||||||
@@ -61,13 +58,9 @@ function HistoryRow(props: HistoryRowProps) {
|
|||||||
date,
|
date,
|
||||||
data,
|
data,
|
||||||
downloadId,
|
downloadId,
|
||||||
isMarkingAsFailed = false,
|
|
||||||
markAsFailedError,
|
|
||||||
columns,
|
columns,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const wasMarkingAsFailed = usePrevious(isMarkingAsFailed);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const series = useSeries(seriesId);
|
const series = useSeries(seriesId);
|
||||||
const episode = useEpisode(episodeId, 'episodes');
|
const episode = useEpisode(episodeId, 'episodes');
|
||||||
|
|
||||||
@@ -81,23 +74,6 @@ function HistoryRow(props: HistoryRowProps) {
|
|||||||
setIsDetailsModalOpen(false);
|
setIsDetailsModalOpen(false);
|
||||||
}, [setIsDetailsModalOpen]);
|
}, [setIsDetailsModalOpen]);
|
||||||
|
|
||||||
const handleMarkAsFailedPress = useCallback(() => {
|
|
||||||
dispatch(markAsFailed({ id }));
|
|
||||||
}, [id, dispatch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (wasMarkingAsFailed && !isMarkingAsFailed && !markAsFailedError) {
|
|
||||||
setIsDetailsModalOpen(false);
|
|
||||||
dispatch(fetchHistory());
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
wasMarkingAsFailed,
|
|
||||||
isMarkingAsFailed,
|
|
||||||
markAsFailedError,
|
|
||||||
setIsDetailsModalOpen,
|
|
||||||
dispatch,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!series || !episode) {
|
if (!series || !episode) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -254,13 +230,12 @@ function HistoryRow(props: HistoryRowProps) {
|
|||||||
})}
|
})}
|
||||||
|
|
||||||
<HistoryDetailsModal
|
<HistoryDetailsModal
|
||||||
|
id={id}
|
||||||
isOpen={isDetailsModalOpen}
|
isOpen={isDetailsModalOpen}
|
||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
sourceTitle={sourceTitle}
|
sourceTitle={sourceTitle}
|
||||||
data={data}
|
data={data}
|
||||||
downloadId={downloadId}
|
downloadId={downloadId}
|
||||||
isMarkingAsFailed={isMarkingAsFailed}
|
|
||||||
onMarkAsFailedPress={handleMarkAsFailedPress}
|
|
||||||
onModalClose={handleDetailsModalClose}
|
onModalClose={handleDetailsModalClose}
|
||||||
/>
|
/>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
109
frontend/src/Activity/History/historyOptionsStore.ts
Normal file
109
frontend/src/Activity/History/historyOptionsStore.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import {
|
||||||
|
createOptionsStore,
|
||||||
|
PageableOptions,
|
||||||
|
} from 'Helpers/Hooks/useOptionsStore';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
|
export type HistoryOptions = PageableOptions;
|
||||||
|
|
||||||
|
const { useOptions, useOption, setOptions, setOption } =
|
||||||
|
createOptionsStore<HistoryOptions>('history_options', () => {
|
||||||
|
return {
|
||||||
|
includeUnknownSeriesItems: true,
|
||||||
|
pageSize: 20,
|
||||||
|
selectedFilterKey: 'all',
|
||||||
|
sortKey: 'time',
|
||||||
|
sortDirection: 'descending',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'eventType',
|
||||||
|
label: '',
|
||||||
|
columnLabel: () => translate('EventType'),
|
||||||
|
isVisible: true,
|
||||||
|
isModifiable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'series.sortTitle',
|
||||||
|
label: () => translate('Series'),
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'episode',
|
||||||
|
label: () => translate('Episode'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'episodes.title',
|
||||||
|
label: () => translate('EpisodeTitle'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'languages',
|
||||||
|
label: () => translate('Languages'),
|
||||||
|
isVisible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quality',
|
||||||
|
label: () => translate('Quality'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customFormats',
|
||||||
|
label: () => translate('Formats'),
|
||||||
|
isSortable: false,
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'date',
|
||||||
|
label: () => translate('Date'),
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'downloadClient',
|
||||||
|
label: () => translate('DownloadClient'),
|
||||||
|
isVisible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'indexer',
|
||||||
|
label: () => translate('Indexer'),
|
||||||
|
isVisible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'releaseGroup',
|
||||||
|
label: () => translate('ReleaseGroup'),
|
||||||
|
isVisible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sourceTitle',
|
||||||
|
label: () => translate('SourceTitle'),
|
||||||
|
isVisible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customFormatScore',
|
||||||
|
columnLabel: () => translate('CustomFormatScore'),
|
||||||
|
label: React.createElement(Icon, {
|
||||||
|
name: icons.SCORE,
|
||||||
|
title: () => translate('CustomFormatScore'),
|
||||||
|
}),
|
||||||
|
isVisible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'details',
|
||||||
|
label: '',
|
||||||
|
columnLabel: () => translate('Details'),
|
||||||
|
isVisible: true,
|
||||||
|
isModifiable: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useHistoryOptions = useOptions;
|
||||||
|
export const setHistoryOptions = setOptions;
|
||||||
|
export const useHistoryOption = useOption;
|
||||||
|
export const setHistoryOption = setOption;
|
186
frontend/src/Activity/History/useHistory.ts
Normal file
186
frontend/src/Activity/History/useHistory.ts
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
import { keepPreviousData, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { CustomFilter, Filter, FilterBuilderProp } from 'App/State/AppState';
|
||||||
|
import useApiMutation from 'Helpers/Hooks/useApiMutation';
|
||||||
|
import usePage from 'Helpers/Hooks/usePage';
|
||||||
|
import usePagedApiQuery from 'Helpers/Hooks/usePagedApiQuery';
|
||||||
|
import { filterBuilderValueTypes } from 'Helpers/Props';
|
||||||
|
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
|
import History from 'typings/History';
|
||||||
|
import findSelectedFilters from 'Utilities/Filter/findSelectedFilters';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import { useHistoryOptions } from './historyOptionsStore';
|
||||||
|
|
||||||
|
export const FILTERS: Filter[] = [
|
||||||
|
{
|
||||||
|
key: 'all',
|
||||||
|
label: () => translate('All'),
|
||||||
|
filters: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'grabbed',
|
||||||
|
label: () => translate('Grabbed'),
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: 'eventType',
|
||||||
|
value: '1',
|
||||||
|
type: 'equal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'imported',
|
||||||
|
label: () => translate('Imported'),
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: 'eventType',
|
||||||
|
value: '3',
|
||||||
|
type: 'equal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'failed',
|
||||||
|
label: () => translate('Failed'),
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: 'eventType',
|
||||||
|
value: '4',
|
||||||
|
type: 'equal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'deleted',
|
||||||
|
label: () => translate('Deleted'),
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: 'eventType',
|
||||||
|
value: '5',
|
||||||
|
type: 'equal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'renamed',
|
||||||
|
label: () => translate('Renamed'),
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: 'eventType',
|
||||||
|
value: '6',
|
||||||
|
type: 'equal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ignored',
|
||||||
|
label: () => translate('Ignored'),
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
key: 'eventType',
|
||||||
|
value: '7',
|
||||||
|
type: 'equal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const FILTER_BUILDER: FilterBuilderProp<History>[] = [
|
||||||
|
{
|
||||||
|
name: 'eventType',
|
||||||
|
label: () => translate('EventType'),
|
||||||
|
type: 'equal',
|
||||||
|
valueType: filterBuilderValueTypes.HISTORY_EVENT_TYPE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'seriesIds',
|
||||||
|
label: () => translate('Series'),
|
||||||
|
type: 'equal',
|
||||||
|
valueType: filterBuilderValueTypes.SERIES,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quality',
|
||||||
|
label: () => translate('Quality'),
|
||||||
|
type: 'equal',
|
||||||
|
valueType: filterBuilderValueTypes.QUALITY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'languages',
|
||||||
|
label: () => translate('Languages'),
|
||||||
|
type: 'contains',
|
||||||
|
valueType: filterBuilderValueTypes.LANGUAGE,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const useHistory = () => {
|
||||||
|
const { page, goToPage } = usePage('history');
|
||||||
|
const { pageSize, selectedFilterKey, sortKey, sortDirection } =
|
||||||
|
useHistoryOptions();
|
||||||
|
const customFilters = useSelector(
|
||||||
|
createCustomFiltersSelector('history')
|
||||||
|
) as CustomFilter[];
|
||||||
|
|
||||||
|
const filters = useMemo(() => {
|
||||||
|
return findSelectedFilters(selectedFilterKey, FILTERS, customFilters);
|
||||||
|
}, [selectedFilterKey, customFilters]);
|
||||||
|
|
||||||
|
const { refetch, ...query } = usePagedApiQuery<History>({
|
||||||
|
path: '/history',
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
filters,
|
||||||
|
sortKey,
|
||||||
|
sortDirection,
|
||||||
|
queryOptions: {
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleGoToPage = useCallback(
|
||||||
|
(page: number) => {
|
||||||
|
goToPage(page);
|
||||||
|
},
|
||||||
|
[goToPage]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...query,
|
||||||
|
goToPage: handleGoToPage,
|
||||||
|
page,
|
||||||
|
refetch,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useHistory;
|
||||||
|
|
||||||
|
export const useFilters = () => {
|
||||||
|
return FILTERS;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMarkAsFailed = (id: number) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const { mutate, isPending } = useApiMutation<unknown, void>({
|
||||||
|
path: `/history/failed/${id}`,
|
||||||
|
method: 'POST',
|
||||||
|
mutationOptions: {
|
||||||
|
onMutate: () => {
|
||||||
|
setError(null);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['/history'] });
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
setError('Error marking history item as failed');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
markAsFailed: mutate,
|
||||||
|
isMarkingAsFailed: isPending,
|
||||||
|
markAsFailedError: error,
|
||||||
|
};
|
||||||
|
};
|
@@ -90,7 +90,6 @@ interface AppState {
|
|||||||
episodeHistory: HistoryAppState;
|
episodeHistory: HistoryAppState;
|
||||||
episodes: EpisodesAppState;
|
episodes: EpisodesAppState;
|
||||||
episodesSelection: EpisodesAppState;
|
episodesSelection: EpisodesAppState;
|
||||||
history: HistoryAppState;
|
|
||||||
importSeries: ImportSeriesAppState;
|
importSeries: ImportSeriesAppState;
|
||||||
interactiveImport: InteractiveImportAppState;
|
interactiveImport: InteractiveImportAppState;
|
||||||
oAuth: OAuthAppState;
|
oAuth: OAuthAppState;
|
||||||
|
@@ -5,12 +5,14 @@ import { create } from 'zustand';
|
|||||||
interface PageStore {
|
interface PageStore {
|
||||||
blocklist: number;
|
blocklist: number;
|
||||||
events: number;
|
events: number;
|
||||||
|
history: number;
|
||||||
queue: number;
|
queue: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageStore = create<PageStore>(() => ({
|
const pageStore = create<PageStore>(() => ({
|
||||||
blocklist: 1,
|
blocklist: 1,
|
||||||
events: 1,
|
events: 1,
|
||||||
|
history: 1,
|
||||||
queue: 1,
|
queue: 1,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@@ -1,327 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { createAction } from 'redux-actions';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import { filterBuilderTypes, filterBuilderValueTypes, filterTypes, icons, sortDirections } from 'Helpers/Props';
|
|
||||||
import { createThunk, handleThunks } from 'Store/thunks';
|
|
||||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
|
||||||
import serverSideCollectionHandlers from 'Utilities/State/serverSideCollectionHandlers';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import { updateItem } from './baseActions';
|
|
||||||
import createHandleActions from './Creators/createHandleActions';
|
|
||||||
import createServerSideCollectionHandlers from './Creators/createServerSideCollectionHandlers';
|
|
||||||
import createClearReducer from './Creators/Reducers/createClearReducer';
|
|
||||||
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Variables
|
|
||||||
|
|
||||||
export const section = 'history';
|
|
||||||
|
|
||||||
//
|
|
||||||
// State
|
|
||||||
|
|
||||||
export const defaultState = {
|
|
||||||
isFetching: false,
|
|
||||||
isPopulated: false,
|
|
||||||
error: null,
|
|
||||||
pageSize: 20,
|
|
||||||
sortKey: 'date',
|
|
||||||
sortDirection: sortDirections.DESCENDING,
|
|
||||||
items: [],
|
|
||||||
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
name: 'eventType',
|
|
||||||
columnLabel: () => translate('EventType'),
|
|
||||||
isVisible: true,
|
|
||||||
isModifiable: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'series.sortTitle',
|
|
||||||
label: () => translate('Series'),
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'episode',
|
|
||||||
label: () => translate('Episode'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'episodes.title',
|
|
||||||
label: () => translate('EpisodeTitle'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'languages',
|
|
||||||
label: () => translate('Languages'),
|
|
||||||
isVisible: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'quality',
|
|
||||||
label: () => translate('Quality'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customFormats',
|
|
||||||
label: () => translate('Formats'),
|
|
||||||
isSortable: false,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'date',
|
|
||||||
label: () => translate('Date'),
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'downloadClient',
|
|
||||||
label: () => translate('DownloadClient'),
|
|
||||||
isVisible: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'indexer',
|
|
||||||
label: () => translate('Indexer'),
|
|
||||||
isVisible: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'releaseGroup',
|
|
||||||
label: () => translate('ReleaseGroup'),
|
|
||||||
isVisible: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sourceTitle',
|
|
||||||
label: () => translate('SourceTitle'),
|
|
||||||
isVisible: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customFormatScore',
|
|
||||||
columnLabel: () => translate('CustomFormatScore'),
|
|
||||||
label: React.createElement(Icon, {
|
|
||||||
name: icons.SCORE,
|
|
||||||
title: () => translate('CustomFormatScore')
|
|
||||||
}),
|
|
||||||
isVisible: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'details',
|
|
||||||
columnLabel: () => translate('Details'),
|
|
||||||
isVisible: true,
|
|
||||||
isModifiable: false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
selectedFilterKey: 'all',
|
|
||||||
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
key: 'all',
|
|
||||||
label: () => translate('All'),
|
|
||||||
filters: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'grabbed',
|
|
||||||
label: () => translate('Grabbed'),
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
key: 'eventType',
|
|
||||||
value: '1',
|
|
||||||
type: filterTypes.EQUAL
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'imported',
|
|
||||||
label: () => translate('Imported'),
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
key: 'eventType',
|
|
||||||
value: '3',
|
|
||||||
type: filterTypes.EQUAL
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'failed',
|
|
||||||
label: () => translate('Failed'),
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
key: 'eventType',
|
|
||||||
value: '4',
|
|
||||||
type: filterTypes.EQUAL
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'deleted',
|
|
||||||
label: () => translate('Deleted'),
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
key: 'eventType',
|
|
||||||
value: '5',
|
|
||||||
type: filterTypes.EQUAL
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'renamed',
|
|
||||||
label: () => translate('Renamed'),
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
key: 'eventType',
|
|
||||||
value: '6',
|
|
||||||
type: filterTypes.EQUAL
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'ignored',
|
|
||||||
label: () => translate('Ignored'),
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
key: 'eventType',
|
|
||||||
value: '7',
|
|
||||||
type: filterTypes.EQUAL
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
filterBuilderProps: [
|
|
||||||
{
|
|
||||||
name: 'eventType',
|
|
||||||
label: () => translate('EventType'),
|
|
||||||
type: filterBuilderTypes.EQUAL,
|
|
||||||
valueType: filterBuilderValueTypes.HISTORY_EVENT_TYPE
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'seriesIds',
|
|
||||||
label: () => translate('Series'),
|
|
||||||
type: filterBuilderTypes.EQUAL,
|
|
||||||
valueType: filterBuilderValueTypes.SERIES
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'quality',
|
|
||||||
label: () => translate('Quality'),
|
|
||||||
type: filterBuilderTypes.EQUAL,
|
|
||||||
valueType: filterBuilderValueTypes.QUALITY
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'languages',
|
|
||||||
label: () => translate('Languages'),
|
|
||||||
type: filterBuilderTypes.CONTAINS,
|
|
||||||
valueType: filterBuilderValueTypes.LANGUAGE
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export const persistState = [
|
|
||||||
'history.pageSize',
|
|
||||||
'history.sortKey',
|
|
||||||
'history.sortDirection',
|
|
||||||
'history.selectedFilterKey',
|
|
||||||
'history.columns'
|
|
||||||
];
|
|
||||||
|
|
||||||
//
|
|
||||||
// Actions Types
|
|
||||||
|
|
||||||
export const FETCH_HISTORY = 'history/fetchHistory';
|
|
||||||
export const GOTO_FIRST_HISTORY_PAGE = 'history/gotoHistoryFirstPage';
|
|
||||||
export const GOTO_PREVIOUS_HISTORY_PAGE = 'history/gotoHistoryPreviousPage';
|
|
||||||
export const GOTO_NEXT_HISTORY_PAGE = 'history/gotoHistoryNextPage';
|
|
||||||
export const GOTO_LAST_HISTORY_PAGE = 'history/gotoHistoryLastPage';
|
|
||||||
export const GOTO_HISTORY_PAGE = 'history/gotoHistoryPage';
|
|
||||||
export const SET_HISTORY_SORT = 'history/setHistorySort';
|
|
||||||
export const SET_HISTORY_FILTER = 'history/setHistoryFilter';
|
|
||||||
export const SET_HISTORY_TABLE_OPTION = 'history/setHistoryTableOption';
|
|
||||||
export const CLEAR_HISTORY = 'history/clearHistory';
|
|
||||||
export const MARK_AS_FAILED = 'history/markAsFailed';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Action Creators
|
|
||||||
|
|
||||||
export const fetchHistory = createThunk(FETCH_HISTORY);
|
|
||||||
export const gotoHistoryFirstPage = createThunk(GOTO_FIRST_HISTORY_PAGE);
|
|
||||||
export const gotoHistoryPreviousPage = createThunk(GOTO_PREVIOUS_HISTORY_PAGE);
|
|
||||||
export const gotoHistoryNextPage = createThunk(GOTO_NEXT_HISTORY_PAGE);
|
|
||||||
export const gotoHistoryLastPage = createThunk(GOTO_LAST_HISTORY_PAGE);
|
|
||||||
export const gotoHistoryPage = createThunk(GOTO_HISTORY_PAGE);
|
|
||||||
export const setHistorySort = createThunk(SET_HISTORY_SORT);
|
|
||||||
export const setHistoryFilter = createThunk(SET_HISTORY_FILTER);
|
|
||||||
export const setHistoryTableOption = createAction(SET_HISTORY_TABLE_OPTION);
|
|
||||||
export const clearHistory = createAction(CLEAR_HISTORY);
|
|
||||||
export const markAsFailed = createThunk(MARK_AS_FAILED);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Action Handlers
|
|
||||||
|
|
||||||
export const actionHandlers = handleThunks({
|
|
||||||
...createServerSideCollectionHandlers(
|
|
||||||
section,
|
|
||||||
'/history',
|
|
||||||
fetchHistory,
|
|
||||||
{
|
|
||||||
[serverSideCollectionHandlers.FETCH]: FETCH_HISTORY,
|
|
||||||
[serverSideCollectionHandlers.FIRST_PAGE]: GOTO_FIRST_HISTORY_PAGE,
|
|
||||||
[serverSideCollectionHandlers.PREVIOUS_PAGE]: GOTO_PREVIOUS_HISTORY_PAGE,
|
|
||||||
[serverSideCollectionHandlers.NEXT_PAGE]: GOTO_NEXT_HISTORY_PAGE,
|
|
||||||
[serverSideCollectionHandlers.LAST_PAGE]: GOTO_LAST_HISTORY_PAGE,
|
|
||||||
[serverSideCollectionHandlers.EXACT_PAGE]: GOTO_HISTORY_PAGE,
|
|
||||||
[serverSideCollectionHandlers.SORT]: SET_HISTORY_SORT,
|
|
||||||
[serverSideCollectionHandlers.FILTER]: SET_HISTORY_FILTER
|
|
||||||
}),
|
|
||||||
|
|
||||||
[MARK_AS_FAILED]: function(getState, payload, dispatch) {
|
|
||||||
const id = payload.id;
|
|
||||||
|
|
||||||
dispatch(updateItem({
|
|
||||||
section,
|
|
||||||
id,
|
|
||||||
isMarkingAsFailed: true
|
|
||||||
}));
|
|
||||||
|
|
||||||
const promise = createAjaxRequest({
|
|
||||||
url: `/history/failed/${id}`,
|
|
||||||
method: 'POST',
|
|
||||||
dataType: 'json'
|
|
||||||
}).request;
|
|
||||||
|
|
||||||
promise.done(() => {
|
|
||||||
dispatch(updateItem({
|
|
||||||
section,
|
|
||||||
id,
|
|
||||||
isMarkingAsFailed: false,
|
|
||||||
markAsFailedError: null
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.fail((xhr) => {
|
|
||||||
dispatch(updateItem({
|
|
||||||
section,
|
|
||||||
id,
|
|
||||||
isMarkingAsFailed: false,
|
|
||||||
markAsFailedError: xhr
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// Reducers
|
|
||||||
|
|
||||||
export const reducers = createHandleActions({
|
|
||||||
|
|
||||||
[SET_HISTORY_TABLE_OPTION]: createSetTableOptionReducer(section),
|
|
||||||
|
|
||||||
[CLEAR_HISTORY]: createClearReducer(section, {
|
|
||||||
isFetching: false,
|
|
||||||
isPopulated: false,
|
|
||||||
error: null,
|
|
||||||
items: [],
|
|
||||||
totalPages: 0,
|
|
||||||
totalRecords: 0
|
|
||||||
})
|
|
||||||
|
|
||||||
}, defaultState, section);
|
|
@@ -7,7 +7,6 @@ import * as episodes from './episodeActions';
|
|||||||
import * as episodeFiles from './episodeFileActions';
|
import * as episodeFiles from './episodeFileActions';
|
||||||
import * as episodeHistory from './episodeHistoryActions';
|
import * as episodeHistory from './episodeHistoryActions';
|
||||||
import * as episodeSelection from './episodeSelectionActions';
|
import * as episodeSelection from './episodeSelectionActions';
|
||||||
import * as history from './historyActions';
|
|
||||||
import * as importSeries from './importSeriesActions';
|
import * as importSeries from './importSeriesActions';
|
||||||
import * as interactiveImportActions from './interactiveImportActions';
|
import * as interactiveImportActions from './interactiveImportActions';
|
||||||
import * as oAuth from './oAuthActions';
|
import * as oAuth from './oAuthActions';
|
||||||
@@ -35,7 +34,6 @@ export default [
|
|||||||
episodeFiles,
|
episodeFiles,
|
||||||
episodeHistory,
|
episodeHistory,
|
||||||
episodeSelection,
|
episodeSelection,
|
||||||
history,
|
|
||||||
importSeries,
|
importSeries,
|
||||||
interactiveImportActions,
|
interactiveImportActions,
|
||||||
oAuth,
|
oAuth,
|
||||||
|
Reference in New Issue
Block a user