New: Movie Editor in Movie Index (#3606)
* Fixed: Movie Editor in Movie Index * Fixed: CSS Style Issues * Fixed: Ensure only items shown are selected * Fixed: Cleanup and Rename from Editorpull/2/head
parent
b8f7ca0749
commit
a20222fbef
@ -1,268 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
|
||||||
import selectAll from 'Utilities/Table/selectAll';
|
|
||||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
|
||||||
import { align, sortDirections } from 'Helpers/Props';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
import PageContent from 'Components/Page/PageContent';
|
|
||||||
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
|
|
||||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
|
||||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
|
||||||
import Table from 'Components/Table/Table';
|
|
||||||
import TableBody from 'Components/Table/TableBody';
|
|
||||||
import NoMovie from 'Movie/NoMovie';
|
|
||||||
import OrganizeSeriesModal from './Organize/OrganizeSeriesModal';
|
|
||||||
import SeriesEditorRowConnector from './SeriesEditorRowConnector';
|
|
||||||
import SeriesEditorFooter from './SeriesEditorFooter';
|
|
||||||
import SeriesEditorFilterModalConnector from './SeriesEditorFilterModalConnector';
|
|
||||||
|
|
||||||
function getColumns() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: 'status',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sortTitle',
|
|
||||||
label: 'Title',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'qualityProfileId',
|
|
||||||
label: 'Quality Profile',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'path',
|
|
||||||
label: 'Path',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'tags',
|
|
||||||
label: 'Tags',
|
|
||||||
isSortable: false,
|
|
||||||
isVisible: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
class SeriesEditor extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
allSelected: false,
|
|
||||||
allUnselected: false,
|
|
||||||
lastToggled: null,
|
|
||||||
selectedState: {},
|
|
||||||
isOrganizingSeriesModalOpen: false,
|
|
||||||
columns: getColumns()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
const {
|
|
||||||
isDeleting,
|
|
||||||
deleteError
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const hasFinishedDeleting = prevProps.isDeleting &&
|
|
||||||
!isDeleting &&
|
|
||||||
!deleteError;
|
|
||||||
|
|
||||||
if (hasFinishedDeleting) {
|
|
||||||
this.onSelectAllChange({ value: false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Control
|
|
||||||
|
|
||||||
getSelectedIds = () => {
|
|
||||||
return getSelectedIds(this.state.selectedState);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSelectAllChange = ({ value }) => {
|
|
||||||
this.setState(selectAll(this.state.selectedState, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelectedChange = ({ id, value, shiftKey = false }) => {
|
|
||||||
this.setState((state) => {
|
|
||||||
return toggleSelected(state, this.props.items, id, value, shiftKey);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onSaveSelected = (changes) => {
|
|
||||||
this.props.onSaveSelected({
|
|
||||||
seriesIds: this.getSelectedIds(),
|
|
||||||
...changes
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onOrganizeSeriesPress = () => {
|
|
||||||
this.setState({ isOrganizingSeriesModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onOrganizeSeriesModalClose = (organized) => {
|
|
||||||
this.setState({ isOrganizingSeriesModalOpen: false });
|
|
||||||
|
|
||||||
if (organized === true) {
|
|
||||||
this.onSelectAllChange({ value: false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
totalItems,
|
|
||||||
items,
|
|
||||||
selectedFilterKey,
|
|
||||||
filters,
|
|
||||||
customFilters,
|
|
||||||
sortKey,
|
|
||||||
sortDirection,
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
isDeleting,
|
|
||||||
deleteError,
|
|
||||||
isOrganizingSeries,
|
|
||||||
onSortPress,
|
|
||||||
onFilterSelect
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
allSelected,
|
|
||||||
allUnselected,
|
|
||||||
selectedState,
|
|
||||||
columns
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const selectedMovieIds = this.getSelectedIds();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PageContent title="Series Editor">
|
|
||||||
<PageToolbar>
|
|
||||||
<PageToolbarSection />
|
|
||||||
<PageToolbarSection alignContent={align.RIGHT}>
|
|
||||||
<FilterMenu
|
|
||||||
alignMenu={align.RIGHT}
|
|
||||||
selectedFilterKey={selectedFilterKey}
|
|
||||||
filters={filters}
|
|
||||||
customFilters={customFilters}
|
|
||||||
filterModalConnectorComponent={SeriesEditorFilterModalConnector}
|
|
||||||
onFilterSelect={onFilterSelect}
|
|
||||||
/>
|
|
||||||
</PageToolbarSection>
|
|
||||||
</PageToolbar>
|
|
||||||
|
|
||||||
<PageContentBodyConnector>
|
|
||||||
{
|
|
||||||
isFetching && !isPopulated &&
|
|
||||||
<LoadingIndicator />
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && !!error &&
|
|
||||||
<div>Unable to load the calendar</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!error && isPopulated && !!items.length &&
|
|
||||||
<div>
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
sortKey={sortKey}
|
|
||||||
sortDirection={sortDirection}
|
|
||||||
selectAll={true}
|
|
||||||
allSelected={allSelected}
|
|
||||||
allUnselected={allUnselected}
|
|
||||||
onSortPress={onSortPress}
|
|
||||||
onSelectAllChange={this.onSelectAllChange}
|
|
||||||
>
|
|
||||||
<TableBody>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<SeriesEditorRowConnector
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
columns={columns}
|
|
||||||
isSelected={selectedState[item.id]}
|
|
||||||
onSelectedChange={this.onSelectedChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!error && isPopulated && !items.length &&
|
|
||||||
<NoMovie totalItems={totalItems} />
|
|
||||||
}
|
|
||||||
</PageContentBodyConnector>
|
|
||||||
|
|
||||||
<SeriesEditorFooter
|
|
||||||
seriesIds={selectedMovieIds}
|
|
||||||
selectedCount={selectedMovieIds.length}
|
|
||||||
isSaving={isSaving}
|
|
||||||
saveError={saveError}
|
|
||||||
isDeleting={isDeleting}
|
|
||||||
deleteError={deleteError}
|
|
||||||
isOrganizingSeries={isOrganizingSeries}
|
|
||||||
onSaveSelected={this.onSaveSelected}
|
|
||||||
onOrganizeSeriesPress={this.onOrganizeSeriesPress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<OrganizeSeriesModal
|
|
||||||
isOpen={this.state.isOrganizingSeriesModalOpen}
|
|
||||||
seriesIds={selectedMovieIds}
|
|
||||||
onModalClose={this.onOrganizeSeriesModalClose}
|
|
||||||
/>
|
|
||||||
</PageContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesEditor.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
totalItems: PropTypes.number.isRequired,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
sortKey: PropTypes.string,
|
|
||||||
sortDirection: PropTypes.oneOf(sortDirections.all),
|
|
||||||
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
|
||||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
isDeleting: PropTypes.bool.isRequired,
|
|
||||||
deleteError: PropTypes.object,
|
|
||||||
isOrganizingSeries: PropTypes.bool.isRequired,
|
|
||||||
onSortPress: PropTypes.func.isRequired,
|
|
||||||
onFilterSelect: PropTypes.func.isRequired,
|
|
||||||
onSaveSelected: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SeriesEditor;
|
|
@ -1,88 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
|
||||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
|
||||||
import { setSeriesEditorSort, setSeriesEditorFilter, saveSeriesEditor } from 'Store/Actions/movieEditorActions';
|
|
||||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
|
||||||
import * as commandNames from 'Commands/commandNames';
|
|
||||||
import SeriesEditor from './SeriesEditor';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createClientSideCollectionSelector('movies', 'movieEditor'),
|
|
||||||
createCommandExecutingSelector(commandNames.RENAME_SERIES),
|
|
||||||
(series, isOrganizingSeries) => {
|
|
||||||
return {
|
|
||||||
isOrganizingSeries,
|
|
||||||
...series
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
dispatchSetSeriesEditorSort: setSeriesEditorSort,
|
|
||||||
dispatchSetSeriesEditorFilter: setSeriesEditorFilter,
|
|
||||||
dispatchSaveMovieEditor: saveSeriesEditor,
|
|
||||||
dispatchFetchRootFolders: fetchRootFolders,
|
|
||||||
dispatchExecuteCommand: executeCommand
|
|
||||||
};
|
|
||||||
|
|
||||||
class SeriesEditorConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.dispatchFetchRootFolders();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSortPress = (sortKey) => {
|
|
||||||
this.props.dispatchSetSeriesEditorSort({ sortKey });
|
|
||||||
}
|
|
||||||
|
|
||||||
onFilterSelect = (selectedFilterKey) => {
|
|
||||||
this.props.dispatchSetSeriesEditorFilter({ selectedFilterKey });
|
|
||||||
}
|
|
||||||
|
|
||||||
onSaveSelected = (payload) => {
|
|
||||||
this.props.dispatchSaveMovieEditor(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMoveSelected = (payload) => {
|
|
||||||
this.props.dispatchExecuteCommand({
|
|
||||||
name: commandNames.MOVE_SERIES,
|
|
||||||
...payload
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<SeriesEditor
|
|
||||||
{...this.props}
|
|
||||||
onSortPress={this.onSortPress}
|
|
||||||
onFilterSelect={this.onFilterSelect}
|
|
||||||
onSaveSelected={this.onSaveSelected}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesEditorConnector.propTypes = {
|
|
||||||
dispatchSetSeriesEditorSort: PropTypes.func.isRequired,
|
|
||||||
dispatchSetSeriesEditorFilter: PropTypes.func.isRequired,
|
|
||||||
dispatchSaveMovieEditor: PropTypes.func.isRequired,
|
|
||||||
dispatchFetchRootFolders: PropTypes.func.isRequired,
|
|
||||||
dispatchExecuteCommand: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(SeriesEditorConnector);
|
|
@ -1,24 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { setSeriesEditorFilter } from 'Store/Actions/movieEditorActions';
|
|
||||||
import FilterModal from 'Components/Filter/FilterModal';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.movies.items,
|
|
||||||
(state) => state.moviesEditor.filterBuilderProps,
|
|
||||||
(sectionItems, filterBuilderProps) => {
|
|
||||||
return {
|
|
||||||
sectionItems,
|
|
||||||
filterBuilderProps,
|
|
||||||
customFilterType: 'movieEditor'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
dispatchSetFilter: setSeriesEditorFilter
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(FilterModal);
|
|
@ -1,97 +0,0 @@
|
|||||||
// import _ from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
// import titleCase from 'Utilities/String/titleCase';
|
|
||||||
import TagListConnector from 'Components/TagListConnector';
|
|
||||||
// import CheckInput from 'Components/Form/CheckInput';
|
|
||||||
import TableRow from 'Components/Table/TableRow';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|
||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
|
||||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
|
||||||
import MovieStatusCell from 'Movie/Index/Table/MovieStatusCell';
|
|
||||||
|
|
||||||
class SeriesEditorRow extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSeasonFolderChange = () => {
|
|
||||||
// Mock handler to satisfy `onChange` being required for `CheckInput`.
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
status,
|
|
||||||
titleSlug,
|
|
||||||
title,
|
|
||||||
monitored,
|
|
||||||
qualityProfile,
|
|
||||||
path,
|
|
||||||
tags,
|
|
||||||
// columns,
|
|
||||||
isSelected,
|
|
||||||
onSelectedChange
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow>
|
|
||||||
<TableSelectCell
|
|
||||||
id={id}
|
|
||||||
isSelected={isSelected}
|
|
||||||
onSelectedChange={onSelectedChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MovieStatusCell
|
|
||||||
monitored={monitored}
|
|
||||||
status={status}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
<MovieTitleLink
|
|
||||||
titleSlug={titleSlug}
|
|
||||||
title={title}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
{qualityProfile.name}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
{path}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
<TagListConnector
|
|
||||||
tags={tags}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesEditorRow.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
status: PropTypes.string.isRequired,
|
|
||||||
titleSlug: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
monitored: PropTypes.bool.isRequired,
|
|
||||||
qualityProfile: PropTypes.object.isRequired,
|
|
||||||
path: PropTypes.string.isRequired,
|
|
||||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
isSelected: PropTypes.bool,
|
|
||||||
onSelectedChange: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
SeriesEditorRow.defaultProps = {
|
|
||||||
tags: []
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SeriesEditorRow;
|
|
@ -1,31 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createQualityProfileSelector from 'Store/Selectors/createQualityProfileSelector';
|
|
||||||
import SeriesEditorRow from './SeriesEditorRow';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createQualityProfileSelector(),
|
|
||||||
(qualityProfile) => {
|
|
||||||
return {
|
|
||||||
qualityProfile
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SeriesEditorRowConnector(props) {
|
|
||||||
return (
|
|
||||||
<SeriesEditorRow
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesEditorRowConnector.propTypes = {
|
|
||||||
qualityProfileId: PropTypes.number.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(SeriesEditorRowConnector);
|
|
@ -1,5 +1,5 @@
|
|||||||
.blankpad {
|
.blankpad {
|
||||||
padding-left:2em;
|
padding-top: 10px;
|
||||||
padding-top: 10px;
|
padding-bottom: 10px;
|
||||||
padding-bottom: 10px;
|
padding-left: 2em;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.blankpad {
|
.blankpad {
|
||||||
padding-left:2em;
|
padding-top: 10px;
|
||||||
padding-top: 10px;
|
padding-bottom: 10px;
|
||||||
padding-bottom: 10px;
|
padding-left: 2em;
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
.title {
|
.title {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quality,
|
.quality,
|
||||||
.language {
|
.language {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
.language {
|
.language {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rejected,
|
.rejected,
|
||||||
.download {
|
.download {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.age,
|
.age,
|
||||||
.size {
|
.size {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -1,179 +0,0 @@
|
|||||||
import { createAction } from 'redux-actions';
|
|
||||||
import { batchActions } from 'redux-batched-actions';
|
|
||||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
|
||||||
import { filterBuilderTypes, filterBuilderValueTypes, sortDirections } from 'Helpers/Props';
|
|
||||||
import { createThunk, handleThunks } from 'Store/thunks';
|
|
||||||
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
|
||||||
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
|
|
||||||
import createHandleActions from './Creators/createHandleActions';
|
|
||||||
import { set, updateItem } from './baseActions';
|
|
||||||
import { filters, filterPredicates, sortPredicates } from './movieActions';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Variables
|
|
||||||
|
|
||||||
export const section = 'movieEditor';
|
|
||||||
|
|
||||||
//
|
|
||||||
// State
|
|
||||||
|
|
||||||
export const defaultState = {
|
|
||||||
isSaving: false,
|
|
||||||
saveError: null,
|
|
||||||
isDeleting: false,
|
|
||||||
deleteError: null,
|
|
||||||
sortKey: 'sortTitle',
|
|
||||||
sortDirection: sortDirections.ASCENDING,
|
|
||||||
secondarySortKey: 'sortTitle',
|
|
||||||
secondarySortDirection: sortDirections.ASCENDING,
|
|
||||||
selectedFilterKey: 'all',
|
|
||||||
filters,
|
|
||||||
filterPredicates,
|
|
||||||
|
|
||||||
filterBuilderProps: [
|
|
||||||
{
|
|
||||||
name: 'monitored',
|
|
||||||
label: 'Monitored',
|
|
||||||
type: filterBuilderTypes.EXACT,
|
|
||||||
valueType: filterBuilderValueTypes.BOOL
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'status',
|
|
||||||
label: 'Status',
|
|
||||||
type: filterBuilderTypes.EXACT,
|
|
||||||
valueType: filterBuilderValueTypes.SERIES_STATUS
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'qualityProfileId',
|
|
||||||
label: 'Quality Profile',
|
|
||||||
type: filterBuilderTypes.EXACT,
|
|
||||||
valueType: filterBuilderValueTypes.QUALITY_PROFILE
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'path',
|
|
||||||
label: 'Path',
|
|
||||||
type: filterBuilderTypes.STRING
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'rootFolderPath',
|
|
||||||
label: 'Root Folder Path',
|
|
||||||
type: filterBuilderTypes.EXACT
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'tags',
|
|
||||||
label: 'Tags',
|
|
||||||
type: filterBuilderTypes.ARRAY,
|
|
||||||
valueType: filterBuilderValueTypes.TAG
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
sortPredicates
|
|
||||||
};
|
|
||||||
|
|
||||||
export const persistState = [
|
|
||||||
'movieEditor.sortKey',
|
|
||||||
'movieEditor.sortDirection',
|
|
||||||
'movieEditor.selectedFilterKey',
|
|
||||||
'movieEditor.customFilters'
|
|
||||||
];
|
|
||||||
|
|
||||||
//
|
|
||||||
// Actions Types
|
|
||||||
|
|
||||||
export const SET_MOVIE_EDITOR_SORT = 'movieEditor/setMovieEditorSort';
|
|
||||||
export const SET_MOVIE_EDITOR_FILTER = 'movieEditor/setMovieEditorFilter';
|
|
||||||
export const SAVE_MOVIE_EDITOR = 'movieEditor/saveMovieEditor';
|
|
||||||
export const BULK_DELETE_MOVIE = 'movieEditor/bulkDeleteMovie';
|
|
||||||
//
|
|
||||||
// Action Creators
|
|
||||||
|
|
||||||
export const setMovieEditorSort = createAction(SET_MOVIE_EDITOR_SORT);
|
|
||||||
export const setMovieEditorFilter = createAction(SET_MOVIE_EDITOR_FILTER);
|
|
||||||
export const saveMovieEditor = createThunk(SAVE_MOVIE_EDITOR);
|
|
||||||
export const bulkDeleteMovie = createThunk(BULK_DELETE_MOVIE);
|
|
||||||
//
|
|
||||||
// Action Handlers
|
|
||||||
|
|
||||||
export const actionHandlers = handleThunks({
|
|
||||||
[SAVE_MOVIE_EDITOR]: function(getState, payload, dispatch) {
|
|
||||||
dispatch(set({
|
|
||||||
section,
|
|
||||||
isSaving: true
|
|
||||||
}));
|
|
||||||
|
|
||||||
const promise = createAjaxRequest({
|
|
||||||
url: '/movie/editor',
|
|
||||||
method: 'PUT',
|
|
||||||
data: JSON.stringify(payload),
|
|
||||||
dataType: 'json'
|
|
||||||
}).request;
|
|
||||||
|
|
||||||
promise.done((data) => {
|
|
||||||
dispatch(batchActions([
|
|
||||||
...data.map((movie) => {
|
|
||||||
return updateItem({
|
|
||||||
id: movie.id,
|
|
||||||
section: 'movies',
|
|
||||||
...movie
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
set({
|
|
||||||
section,
|
|
||||||
isSaving: false,
|
|
||||||
saveError: null
|
|
||||||
})
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.fail((xhr) => {
|
|
||||||
dispatch(set({
|
|
||||||
section,
|
|
||||||
isSaving: false,
|
|
||||||
saveError: xhr
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
[BULK_DELETE_MOVIE]: function(getState, payload, dispatch) {
|
|
||||||
dispatch(set({
|
|
||||||
section,
|
|
||||||
isDeleting: true
|
|
||||||
}));
|
|
||||||
|
|
||||||
const promise = createAjaxRequest({
|
|
||||||
url: '/movie/editor',
|
|
||||||
method: 'DELETE',
|
|
||||||
data: JSON.stringify(payload),
|
|
||||||
dataType: 'json'
|
|
||||||
}).request;
|
|
||||||
|
|
||||||
promise.done(() => {
|
|
||||||
// SignaR will take care of removing the series from the collection
|
|
||||||
|
|
||||||
dispatch(set({
|
|
||||||
section,
|
|
||||||
isDeleting: false,
|
|
||||||
deleteError: null
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.fail((xhr) => {
|
|
||||||
dispatch(set({
|
|
||||||
section,
|
|
||||||
isDeleting: false,
|
|
||||||
deleteError: xhr
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// Reducers
|
|
||||||
|
|
||||||
export const reducers = createHandleActions({
|
|
||||||
|
|
||||||
[SET_MOVIE_EDITOR_SORT]: createSetClientSideCollectionSortReducer(section),
|
|
||||||
[SET_MOVIE_EDITOR_FILTER]: createSetClientSideCollectionFilterReducer(section)
|
|
||||||
|
|
||||||
}, defaultState, section);
|
|
Loading…
Reference in new issue