parent
6dc16f3ddd
commit
7035bb2944
@ -1,33 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import { sizes } from 'Helpers/Props';
|
|
||||||
import SeriesHistoryModalContentConnector from './SeriesHistoryModalContentConnector';
|
|
||||||
|
|
||||||
function SeriesHistoryModal(props) {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
onModalClose,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
size={sizes.EXTRA_EXTRA_LARGE}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<SeriesHistoryModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesHistoryModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SeriesHistoryModal;
|
|
@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import { sizes } from 'Helpers/Props';
|
||||||
|
import SeriesHistoryModalContent, {
|
||||||
|
SeriesHistoryModalContentProps,
|
||||||
|
} from './SeriesHistoryModalContent';
|
||||||
|
|
||||||
|
interface SeriesHistoryModalProps extends SeriesHistoryModalContentProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SeriesHistoryModal({
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
}: SeriesHistoryModalProps) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
size={sizes.EXTRA_EXTRA_LARGE}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
>
|
||||||
|
<SeriesHistoryModalContent {...otherProps} onModalClose={onModalClose} />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SeriesHistoryModal;
|
@ -1,154 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
|
||||||
import Table from 'Components/Table/Table';
|
|
||||||
import TableBody from 'Components/Table/TableBody';
|
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
|
||||||
import formatSeason from 'Season/formatSeason';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import SeriesHistoryRowConnector from './SeriesHistoryRowConnector';
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'eventType',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'episode',
|
|
||||||
label: () => translate('Episode'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sourceTitle',
|
|
||||||
label: () => translate('SourceTitle'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'languages',
|
|
||||||
label: () => translate('Languages'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'quality',
|
|
||||||
label: () => translate('Quality'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customFormats',
|
|
||||||
label: () => translate('CustomFormats'),
|
|
||||||
isSortable: false,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customFormatScore',
|
|
||||||
label: React.createElement(Icon, {
|
|
||||||
name: icons.SCORE,
|
|
||||||
title: () => translate('CustomFormatScore')
|
|
||||||
}),
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'date',
|
|
||||||
label: () => translate('Date'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'actions',
|
|
||||||
isVisible: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
class SeriesHistoryModalContent extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
seasonNumber,
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
items,
|
|
||||||
onMarkAsFailedPress,
|
|
||||||
onModalClose
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const fullSeries = seasonNumber == null;
|
|
||||||
const hasItems = !!items.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{seasonNumber == null ?
|
|
||||||
translate('History') :
|
|
||||||
translate('HistoryModalHeaderSeason', { season: formatSeason(seasonNumber) })
|
|
||||||
}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
{
|
|
||||||
isFetching &&
|
|
||||||
<LoadingIndicator />
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && !!error &&
|
|
||||||
<Alert kind={kinds.DANGER}>{translate('HistoryLoadError')}</Alert>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
isPopulated && !hasItems && !error &&
|
|
||||||
<div>{translate('NoHistory')}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
isPopulated && hasItems && !error &&
|
|
||||||
<Table columns={columns}>
|
|
||||||
<TableBody>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<SeriesHistoryRowConnector
|
|
||||||
key={item.id}
|
|
||||||
fullSeries={fullSeries}
|
|
||||||
{...item}
|
|
||||||
onMarkAsFailedPress={onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
}
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={onModalClose}>
|
|
||||||
{translate('Close')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesHistoryModalContent.propTypes = {
|
|
||||||
seasonNumber: PropTypes.number,
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onMarkAsFailedPress: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SeriesHistoryModalContent;
|
|
@ -0,0 +1,170 @@
|
|||||||
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
import Alert from 'Components/Alert';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import Column from 'Components/Table/Column';
|
||||||
|
import Table from 'Components/Table/Table';
|
||||||
|
import TableBody from 'Components/Table/TableBody';
|
||||||
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
|
import formatSeason from 'Season/formatSeason';
|
||||||
|
import {
|
||||||
|
clearSeriesHistory,
|
||||||
|
fetchSeriesHistory,
|
||||||
|
seriesHistoryMarkAsFailed,
|
||||||
|
} from 'Store/Actions/seriesHistoryActions';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import SeriesHistoryRow from './SeriesHistoryRow';
|
||||||
|
|
||||||
|
const columns: Column[] = [
|
||||||
|
{
|
||||||
|
name: 'eventType',
|
||||||
|
label: '',
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'episode',
|
||||||
|
label: () => translate('Episode'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sourceTitle',
|
||||||
|
label: () => translate('SourceTitle'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'languages',
|
||||||
|
label: () => translate('Languages'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quality',
|
||||||
|
label: () => translate('Quality'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customFormats',
|
||||||
|
label: () => translate('CustomFormats'),
|
||||||
|
isSortable: false,
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customFormatScore',
|
||||||
|
label: React.createElement(Icon, {
|
||||||
|
name: icons.SCORE,
|
||||||
|
title: () => translate('CustomFormatScore'),
|
||||||
|
}),
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'date',
|
||||||
|
label: () => translate('Date'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
label: '',
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export interface SeriesHistoryModalContentProps {
|
||||||
|
seriesId: number;
|
||||||
|
seasonNumber?: number;
|
||||||
|
onModalClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SeriesHistoryModalContent({
|
||||||
|
seriesId,
|
||||||
|
seasonNumber,
|
||||||
|
onModalClose,
|
||||||
|
}: SeriesHistoryModalContentProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const { isFetching, isPopulated, error, items } = useSelector(
|
||||||
|
(state: AppState) => state.seriesHistory
|
||||||
|
);
|
||||||
|
|
||||||
|
const fullSeries = seasonNumber == null;
|
||||||
|
const hasItems = !!items.length;
|
||||||
|
|
||||||
|
const handleMarkAsFailedPress = useCallback(
|
||||||
|
(historyId: number) => {
|
||||||
|
dispatch(
|
||||||
|
seriesHistoryMarkAsFailed({
|
||||||
|
historyId,
|
||||||
|
seriesId,
|
||||||
|
seasonNumber,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[seriesId, seasonNumber, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(
|
||||||
|
fetchSeriesHistory({
|
||||||
|
seriesId,
|
||||||
|
seasonNumber,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
dispatch(clearSeriesHistory());
|
||||||
|
};
|
||||||
|
}, [seriesId, seasonNumber, dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
{seasonNumber == null
|
||||||
|
? translate('History')
|
||||||
|
: translate('HistoryModalHeaderSeason', {
|
||||||
|
season: formatSeason(seasonNumber)!,
|
||||||
|
})}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
{isFetching && !isPopulated ? <LoadingIndicator /> : null}
|
||||||
|
|
||||||
|
{!isFetching && !!error ? (
|
||||||
|
<Alert kind={kinds.DANGER}>{translate('HistoryLoadError')}</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{isPopulated && !hasItems && !error ? (
|
||||||
|
<div>{translate('NoHistory')}</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{isPopulated && hasItems && !error ? (
|
||||||
|
<Table columns={columns}>
|
||||||
|
<TableBody>
|
||||||
|
{items.map((item) => {
|
||||||
|
return (
|
||||||
|
<SeriesHistoryRow
|
||||||
|
key={item.id}
|
||||||
|
fullSeries={fullSeries}
|
||||||
|
{...item}
|
||||||
|
onMarkAsFailedPress={handleMarkAsFailedPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
) : null}
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={onModalClose}>{translate('Close')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SeriesHistoryModalContent;
|
@ -1,81 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { clearSeriesHistory, fetchSeriesHistory, seriesHistoryMarkAsFailed } from 'Store/Actions/seriesHistoryActions';
|
|
||||||
import SeriesHistoryModalContent from './SeriesHistoryModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.seriesHistory,
|
|
||||||
(seriesHistory) => {
|
|
||||||
return seriesHistory;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchSeriesHistory,
|
|
||||||
clearSeriesHistory,
|
|
||||||
seriesHistoryMarkAsFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
class SeriesHistoryModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const {
|
|
||||||
seriesId,
|
|
||||||
seasonNumber
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.props.fetchSeriesHistory({
|
|
||||||
seriesId,
|
|
||||||
seasonNumber
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.props.clearSeriesHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onMarkAsFailedPress = (historyId) => {
|
|
||||||
const {
|
|
||||||
seriesId,
|
|
||||||
seasonNumber
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.props.seriesHistoryMarkAsFailed({
|
|
||||||
historyId,
|
|
||||||
seriesId,
|
|
||||||
seasonNumber
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<SeriesHistoryModalContent
|
|
||||||
{...this.props}
|
|
||||||
onMarkAsFailedPress={this.onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesHistoryModalContentConnector.propTypes = {
|
|
||||||
seriesId: PropTypes.number.isRequired,
|
|
||||||
seasonNumber: PropTypes.number,
|
|
||||||
fetchSeriesHistory: PropTypes.func.isRequired,
|
|
||||||
clearSeriesHistory: PropTypes.func.isRequired,
|
|
||||||
seriesHistoryMarkAsFailed: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(SeriesHistoryModalContentConnector);
|
|
@ -1,204 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import HistoryDetails from 'Activity/History/Details/HistoryDetails';
|
|
||||||
import HistoryEventTypeCell from 'Activity/History/HistoryEventTypeCell';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import IconButton from 'Components/Link/IconButton';
|
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|
||||||
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|
||||||
import TableRow from 'Components/Table/TableRow';
|
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
|
||||||
import EpisodeFormats from 'Episode/EpisodeFormats';
|
|
||||||
import EpisodeLanguages from 'Episode/EpisodeLanguages';
|
|
||||||
import EpisodeNumber from 'Episode/EpisodeNumber';
|
|
||||||
import EpisodeQuality from 'Episode/EpisodeQuality';
|
|
||||||
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
|
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './SeriesHistoryRow.css';
|
|
||||||
|
|
||||||
function getTitle(eventType) {
|
|
||||||
switch (eventType) {
|
|
||||||
case 'grabbed': return 'Grabbed';
|
|
||||||
case 'seriesFolderImported': return 'Series Folder Imported';
|
|
||||||
case 'downloadFolderImported': return 'Download Folder Imported';
|
|
||||||
case 'downloadFailed': return 'Download Failed';
|
|
||||||
case 'episodeFileDeleted': return 'Episode File Deleted';
|
|
||||||
case 'episodeFileRenamed': return 'Episode File Renamed';
|
|
||||||
default: return 'Unknown';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SeriesHistoryRow extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isMarkAsFailedModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onMarkAsFailedPress = () => {
|
|
||||||
this.setState({ isMarkAsFailedModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onConfirmMarkAsFailed = () => {
|
|
||||||
this.props.onMarkAsFailedPress(this.props.id);
|
|
||||||
this.setState({ isMarkAsFailedModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onMarkAsFailedModalClose = () => {
|
|
||||||
this.setState({ isMarkAsFailedModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
eventType,
|
|
||||||
sourceTitle,
|
|
||||||
languages,
|
|
||||||
quality,
|
|
||||||
qualityCutoffNotMet,
|
|
||||||
customFormats,
|
|
||||||
date,
|
|
||||||
data,
|
|
||||||
downloadId,
|
|
||||||
fullSeries,
|
|
||||||
series,
|
|
||||||
episode,
|
|
||||||
customFormatScore
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
isMarkAsFailedModalOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const EpisodeComponent = fullSeries ? SeasonEpisodeNumber : EpisodeNumber;
|
|
||||||
|
|
||||||
if (!series || !episode) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow>
|
|
||||||
<HistoryEventTypeCell
|
|
||||||
eventType={eventType}
|
|
||||||
data={data}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TableRowCell key={name}>
|
|
||||||
<EpisodeComponent
|
|
||||||
seasonNumber={episode.seasonNumber}
|
|
||||||
episodeNumber={episode.episodeNumber}
|
|
||||||
absoluteEpisodeNumber={episode.absoluteEpisodeNumber}
|
|
||||||
seriesType={series.seriesType}
|
|
||||||
alternateTitles={series.alternateTitles}
|
|
||||||
sceneSeasonNumber={episode.sceneSeasonNumber}
|
|
||||||
sceneEpisodeNumber={episode.sceneEpisodeNumber}
|
|
||||||
sceneAbsoluteEpisodeNumber={episode.sceneAbsoluteEpisodeNumber}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.sourceTitle}>
|
|
||||||
{sourceTitle}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
<EpisodeLanguages languages={languages} />
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
<EpisodeQuality
|
|
||||||
quality={quality}
|
|
||||||
isCutoffNotMet={qualityCutoffNotMet}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
<EpisodeFormats formats={customFormats} />
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
{formatCustomFormatScore(customFormatScore, customFormats.length)}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<RelativeDateCell
|
|
||||||
date={date}
|
|
||||||
includeSeconds={true}
|
|
||||||
includeTime={true}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.actions}>
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Icon
|
|
||||||
name={icons.INFO}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
title={getTitle(eventType)}
|
|
||||||
body={
|
|
||||||
<HistoryDetails
|
|
||||||
eventType={eventType}
|
|
||||||
sourceTitle={sourceTitle}
|
|
||||||
data={data}
|
|
||||||
downloadId={downloadId}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.LEFT}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
eventType === 'grabbed' &&
|
|
||||||
<IconButton
|
|
||||||
title={translate('MarkAsFailed')}
|
|
||||||
name={icons.REMOVE}
|
|
||||||
size={14}
|
|
||||||
onPress={this.onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={isMarkAsFailedModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title={translate('MarkAsFailed')}
|
|
||||||
message={translate('MarkAsFailedConfirmation', { sourceTitle })}
|
|
||||||
confirmLabel={translate('MarkAsFailed')}
|
|
||||||
onConfirm={this.onConfirmMarkAsFailed}
|
|
||||||
onCancel={this.onMarkAsFailedModalClose}
|
|
||||||
/>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesHistoryRow.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
eventType: PropTypes.string.isRequired,
|
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
|
||||||
languages: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
quality: PropTypes.object.isRequired,
|
|
||||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
|
||||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
date: PropTypes.string.isRequired,
|
|
||||||
data: PropTypes.object.isRequired,
|
|
||||||
downloadId: PropTypes.string,
|
|
||||||
fullSeries: PropTypes.bool.isRequired,
|
|
||||||
series: PropTypes.object.isRequired,
|
|
||||||
episode: PropTypes.object.isRequired,
|
|
||||||
customFormatScore: PropTypes.number.isRequired,
|
|
||||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SeriesHistoryRow;
|
|
@ -0,0 +1,183 @@
|
|||||||
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
|
import HistoryDetails from 'Activity/History/Details/HistoryDetails';
|
||||||
|
import HistoryEventTypeCell from 'Activity/History/HistoryEventTypeCell';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import IconButton from 'Components/Link/IconButton';
|
||||||
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
|
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
|
||||||
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
|
import TableRow from 'Components/Table/TableRow';
|
||||||
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
|
import EpisodeFormats from 'Episode/EpisodeFormats';
|
||||||
|
import EpisodeLanguages from 'Episode/EpisodeLanguages';
|
||||||
|
import EpisodeNumber from 'Episode/EpisodeNumber';
|
||||||
|
import EpisodeQuality from 'Episode/EpisodeQuality';
|
||||||
|
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
|
||||||
|
import useEpisode from 'Episode/useEpisode';
|
||||||
|
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
|
import Language from 'Language/Language';
|
||||||
|
import { QualityModel } from 'Quality/Quality';
|
||||||
|
import useSeries from 'Series/useSeries';
|
||||||
|
import CustomFormat from 'typings/CustomFormat';
|
||||||
|
import { HistoryData, HistoryEventType } from 'typings/History';
|
||||||
|
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './SeriesHistoryRow.css';
|
||||||
|
|
||||||
|
interface SeriesHistoryRowProps {
|
||||||
|
id: number;
|
||||||
|
seriesId: number;
|
||||||
|
episodeId: number;
|
||||||
|
eventType: HistoryEventType;
|
||||||
|
sourceTitle: string;
|
||||||
|
languages?: Language[];
|
||||||
|
quality: QualityModel;
|
||||||
|
qualityCutoffNotMet: boolean;
|
||||||
|
customFormats?: CustomFormat[];
|
||||||
|
date: string;
|
||||||
|
data: HistoryData;
|
||||||
|
downloadId?: string;
|
||||||
|
fullSeries: boolean;
|
||||||
|
customFormatScore: number;
|
||||||
|
onMarkAsFailedPress: (historyId: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SeriesHistoryRow({
|
||||||
|
id,
|
||||||
|
seriesId,
|
||||||
|
episodeId,
|
||||||
|
eventType,
|
||||||
|
sourceTitle,
|
||||||
|
languages = [],
|
||||||
|
quality,
|
||||||
|
qualityCutoffNotMet,
|
||||||
|
customFormats = [],
|
||||||
|
date,
|
||||||
|
data,
|
||||||
|
downloadId,
|
||||||
|
fullSeries,
|
||||||
|
customFormatScore,
|
||||||
|
onMarkAsFailedPress,
|
||||||
|
}: SeriesHistoryRowProps) {
|
||||||
|
const series = useSeries(seriesId);
|
||||||
|
const episode = useEpisode(episodeId, 'episodes');
|
||||||
|
|
||||||
|
const [isMarkAsFailedModalOpen, setIsMarkAsFailedModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const EpisodeComponent = fullSeries ? SeasonEpisodeNumber : EpisodeNumber;
|
||||||
|
|
||||||
|
const title = useMemo(() => {
|
||||||
|
switch (eventType) {
|
||||||
|
case 'grabbed':
|
||||||
|
return 'Grabbed';
|
||||||
|
case 'seriesFolderImported':
|
||||||
|
return 'Series Folder Imported';
|
||||||
|
case 'downloadFolderImported':
|
||||||
|
return 'Download Folder Imported';
|
||||||
|
case 'downloadFailed':
|
||||||
|
return 'Download Failed';
|
||||||
|
case 'episodeFileDeleted':
|
||||||
|
return 'Episode File Deleted';
|
||||||
|
case 'episodeFileRenamed':
|
||||||
|
return 'Episode File Renamed';
|
||||||
|
default:
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
}, [eventType]);
|
||||||
|
|
||||||
|
const handleMarkAsFailedPress = useCallback(() => {
|
||||||
|
setIsMarkAsFailedModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleConfirmMarkAsFailed = useCallback(() => {
|
||||||
|
onMarkAsFailedPress(id);
|
||||||
|
setIsMarkAsFailedModalOpen(false);
|
||||||
|
}, [id, onMarkAsFailedPress]);
|
||||||
|
|
||||||
|
const handleMarkAsFailedModalClose = useCallback(() => {
|
||||||
|
setIsMarkAsFailedModalOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!series || !episode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow>
|
||||||
|
<HistoryEventTypeCell eventType={eventType} data={data} />
|
||||||
|
|
||||||
|
<TableRowCell>
|
||||||
|
<EpisodeComponent
|
||||||
|
seasonNumber={episode.seasonNumber}
|
||||||
|
episodeNumber={episode.episodeNumber}
|
||||||
|
absoluteEpisodeNumber={episode.absoluteEpisodeNumber}
|
||||||
|
seriesType={series.seriesType}
|
||||||
|
alternateTitles={series.alternateTitles}
|
||||||
|
sceneSeasonNumber={episode.sceneSeasonNumber}
|
||||||
|
sceneEpisodeNumber={episode.sceneEpisodeNumber}
|
||||||
|
sceneAbsoluteEpisodeNumber={episode.sceneAbsoluteEpisodeNumber}
|
||||||
|
/>
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell className={styles.sourceTitle}>{sourceTitle}</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell>
|
||||||
|
<EpisodeLanguages languages={languages} />
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell>
|
||||||
|
<EpisodeQuality
|
||||||
|
quality={quality}
|
||||||
|
isCutoffNotMet={qualityCutoffNotMet}
|
||||||
|
/>
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell>
|
||||||
|
<EpisodeFormats formats={customFormats} />
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell>
|
||||||
|
{formatCustomFormatScore(customFormatScore, customFormats.length)}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<RelativeDateCell date={date} includeSeconds={true} includeTime={true} />
|
||||||
|
|
||||||
|
<TableRowCell className={styles.actions}>
|
||||||
|
<Popover
|
||||||
|
anchor={<Icon name={icons.INFO} />}
|
||||||
|
title={title}
|
||||||
|
body={
|
||||||
|
<HistoryDetails
|
||||||
|
eventType={eventType}
|
||||||
|
sourceTitle={sourceTitle}
|
||||||
|
data={data}
|
||||||
|
downloadId={downloadId}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
position={tooltipPositions.LEFT}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{eventType === 'grabbed' ? (
|
||||||
|
<IconButton
|
||||||
|
title={translate('MarkAsFailed')}
|
||||||
|
name={icons.REMOVE}
|
||||||
|
size={14}
|
||||||
|
onPress={handleMarkAsFailedPress}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
isOpen={isMarkAsFailedModalOpen}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title={translate('MarkAsFailed')}
|
||||||
|
message={translate('MarkAsFailedConfirmation', { sourceTitle })}
|
||||||
|
confirmLabel={translate('MarkAsFailed')}
|
||||||
|
onConfirm={handleConfirmMarkAsFailed}
|
||||||
|
onCancel={handleMarkAsFailedModalClose}
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SeriesHistoryRow;
|
@ -1,26 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { fetchHistory, markAsFailed } from 'Store/Actions/historyActions';
|
|
||||||
import createEpisodeSelector from 'Store/Selectors/createEpisodeSelector';
|
|
||||||
import createSeriesSelector from 'Store/Selectors/createSeriesSelector';
|
|
||||||
import SeriesHistoryRow from './SeriesHistoryRow';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createSeriesSelector(),
|
|
||||||
createEpisodeSelector(),
|
|
||||||
(series, episode) => {
|
|
||||||
return {
|
|
||||||
series,
|
|
||||||
episode
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchHistory,
|
|
||||||
markAsFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(SeriesHistoryRow);
|
|
Loading…
Reference in new issue