1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-17 16:14:46 -04:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Qstick
74382d7250 New: Rework List Exclusion UI 2022-07-05 23:10:47 -05:00
Qstick
6659bc034c Remove general yarn restore key to avoid cross OS conflict 2022-07-05 17:15:52 -05:00
7 changed files with 167 additions and 53 deletions

View File

@@ -173,7 +173,6 @@ stages:
key: 'yarn | "$(osName)" | yarn.lock' key: 'yarn | "$(osName)" | yarn.lock'
restoreKeys: | restoreKeys: |
yarn | "$(osName)" yarn | "$(osName)"
yarn
path: $(yarnCacheFolder) path: $(yarnCacheFolder)
displayName: Cache Yarn packages displayName: Cache Yarn packages
- bash: ./build.sh --frontend - bash: ./build.sh --frontend
@@ -976,7 +975,6 @@ stages:
key: 'yarn | "$(osName)" | yarn.lock' key: 'yarn | "$(osName)" | yarn.lock'
restoreKeys: | restoreKeys: |
yarn | "$(osName)" yarn | "$(osName)"
yarn
path: $(yarnCacheFolder) path: $(yarnCacheFolder)
displayName: Cache Yarn packages displayName: Cache Yarn packages
- bash: ./build.sh --lint - bash: ./build.sh --lint

View File

@@ -181,12 +181,13 @@ class Blocklist extends Component {
> >
<TableBody> <TableBody>
{ {
items.map((item) => { items.map((item, index) => {
return ( return (
<BlocklistRowConnector <BlocklistRowConnector
key={item.id} key={item.id}
isSelected={selectedState[item.id] || false} isSelected={selectedState[item.id] || false}
columns={columns} columns={columns}
index={index}
{...item} {...item}
onSelectedChange={this.onSelectedChange} onSelectedChange={this.onSelectedChange}
/> />

View File

@@ -15,12 +15,13 @@
.tmdbId, .tmdbId,
.movieYear { .movieYear {
flex: 0 0 70px; composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 80px;
} }
.actions { .actions {
display: flex; composes: cell from '~Components/Table/Cells/TableRowCell.css';
justify-content: flex-end;
flex: 1 0 auto; width: 70px;
padding-right: 10px;
} }

View File

@@ -1,13 +1,16 @@
import classNames from 'classnames';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import Link from 'Components/Link/Link'; import Link from 'Components/Link/Link';
import ConfirmModal from 'Components/Modal/ConfirmModal'; import ConfirmModal from 'Components/Modal/ConfirmModal';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import { icons, kinds } from 'Helpers/Props'; import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector'; import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
import styles from './ImportListExclusion.css'; import styles from './ImportListExclusion.css';
import IconButton from 'Components/Link/IconButton';
class ImportListExclusion extends Component { class ImportListExclusion extends Component {
@@ -55,28 +58,82 @@ class ImportListExclusion extends Component {
render() { render() {
const { const {
id, id,
isSelected,
onSelectedChange,
columns,
movieTitle, movieTitle,
tmdbId, tmdbId,
movieYear movieYear
} = this.props; } = this.props;
return ( return (
<div <TableRow>
className={classNames( <TableSelectCell
styles.importExclusion id={id}
)} isSelected={isSelected}
> onSelectedChange={onSelectedChange}
<div className={styles.tmdbId}>{tmdbId}</div> />
<div className={styles.movieTitle}>{movieTitle}</div>
<div className={styles.movieYear}>{movieYear}</div>
<div className={styles.actions}> {
<Link columns.map((column) => {
onPress={this.onEditImportExclusionPress} const {
> name,
<Icon name={icons.EDIT} /> isVisible
</Link> } = column;
</div>
if (!isVisible) {
return null;
}
if (name === 'tmdbId') {
return (
<TableRowCell key={name}>
{tmdbId}
</TableRowCell>
);
}
if (name === 'movieTitle') {
return (
<TableRowCell key={name}>
{movieTitle}
</TableRowCell>
);
}
if (name === 'movieYear') {
return (
<TableRowCell key={name}>
{movieYear}
</TableRowCell>
);
}
if (name === 'actions') {
return (
<TableRowCell
key={name}
className={styles.actions}
>
<IconButton
title={translate('RemoveFromBlocklist')}
name={icons.EDIT}
onPress={this.onEditImportExclusionPress}
/>
<IconButton
title={translate('RemoveFromBlocklist')}
name={icons.REMOVE}
kind={kinds.DANGER}
onPress={this.onDeleteImportExclusionPress}
/>
</TableRowCell>
);
}
return null;
})
}
<EditImportListExclusionModalConnector <EditImportListExclusionModalConnector
id={id} id={id}
@@ -94,7 +151,7 @@ class ImportListExclusion extends Component {
onConfirm={this.onConfirmDeleteImportExclusion} onConfirm={this.onConfirmDeleteImportExclusion}
onCancel={this.onDeleteImportExclusionModalClose} onCancel={this.onDeleteImportExclusionModalClose}
/> />
</div> </TableRow>
); );
} }
} }
@@ -104,6 +161,9 @@ ImportListExclusion.propTypes = {
movieTitle: PropTypes.string.isRequired, movieTitle: PropTypes.string.isRequired,
tmdbId: PropTypes.number.isRequired, tmdbId: PropTypes.number.isRequired,
movieYear: PropTypes.number.isRequired, movieYear: PropTypes.number.isRequired,
isSelected: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onSelectedChange: PropTypes.func.isRequired,
onConfirmDeleteImportExclusion: PropTypes.func.isRequired onConfirmDeleteImportExclusion: PropTypes.func.isRequired
}; };

View File

@@ -4,8 +4,12 @@ import FieldSet from 'Components/FieldSet';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import Link from 'Components/Link/Link'; import Link from 'Components/Link/Link';
import PageSectionContent from 'Components/Page/PageSectionContent'; import PageSectionContent from 'Components/Page/PageSectionContent';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector'; import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector';
import ImportListExclusion from './ImportListExclusion'; import ImportListExclusion from './ImportListExclusion';
import styles from './ImportListExclusions.css'; import styles from './ImportListExclusions.css';
@@ -19,6 +23,10 @@ class ImportListExclusions extends Component {
super(props, context); super(props, context);
this.state = { this.state = {
allSelected: false,
allUnselected: false,
lastToggled: null,
selectedState: {},
isAddImportExclusionModalOpen: false isAddImportExclusionModalOpen: false
}; };
} }
@@ -26,6 +34,16 @@ class ImportListExclusions extends Component {
// //
// Listeners // 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);
});
};
onAddImportExclusionPress = () => { onAddImportExclusionPress = () => {
this.setState({ isAddImportExclusionModalOpen: true }); this.setState({ isAddImportExclusionModalOpen: true });
}; };
@@ -41,41 +59,50 @@ class ImportListExclusions extends Component {
const { const {
items, items,
onConfirmDeleteImportExclusion, onConfirmDeleteImportExclusion,
columns,
...otherProps ...otherProps
} = this.props; } = this.props;
const {
allSelected,
allUnselected,
selectedState
} = this.state;
return ( return (
<FieldSet legend={translate('ListExclusions')}> <FieldSet legend={translate('ListExclusions')}>
<PageSectionContent <PageSectionContent
errorMessage={translate('UnableToLoadListExclusions')} errorMessage={translate('UnableToLoadListExclusions')}
{...otherProps} {...otherProps}
> >
<div className={styles.importListExclusionsHeader}>
<div className={styles.tmdbId}>
TMDb Id
</div>
<div className={styles.title}>
{translate('Title')}
</div>
<div className={styles.movieYear}>
{translate('Year')}
</div>
</div>
<div> <div>
{ <Table
items.map((item, index) => { selectAll={true}
return ( allSelected={allSelected}
<ImportListExclusion allUnselected={allUnselected}
key={item.id} columns={columns}
{...item} {...otherProps}
{...otherProps} onSelectAllChange={this.onSelectAllChange}
index={index} >
onConfirmDeleteImportExclusion={onConfirmDeleteImportExclusion} <TableBody>
/> {
); items.map((item, index) => {
}) return (
} <ImportListExclusion
key={item.id}
isSelected={selectedState[item.id] || false}
{...item}
{...otherProps}
columns={columns}
index={index}
onSelectedChange={this.onSelectedChange}
onConfirmDeleteImportExclusion={onConfirmDeleteImportExclusion}
/>
);
})
}
</TableBody>
</Table>
</div> </div>
<div className={styles.addImportExclusion}> <div className={styles.addImportExclusion}>
@@ -101,6 +128,7 @@ class ImportListExclusions extends Component {
ImportListExclusions.propTypes = { ImportListExclusions.propTypes = {
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
error: PropTypes.object, error: PropTypes.object,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
onConfirmDeleteImportExclusion: PropTypes.func.isRequired onConfirmDeleteImportExclusion: PropTypes.func.isRequired
}; };

View File

@@ -4,6 +4,7 @@ import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHand
import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler'; import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer'; import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import { createThunk } from 'Store/thunks'; import { createThunk } from 'Store/thunks';
import translate from 'Utilities/String/translate';
// //
// Variables // Variables
@@ -48,7 +49,34 @@ export default {
items: [], items: [],
isSaving: false, isSaving: false,
saveError: null, saveError: null,
pendingChanges: {} pendingChanges: {},
columns: [
{
name: 'tmdbId',
label: 'TmdbId',
isSortable: true,
isVisible: true
},
{
name: 'movieTitle',
label: translate('Title'),
isSortable: true,
isVisible: true
},
{
name: 'movieYear',
label: translate('Year'),
isSortable: true,
isVisible: true
},
{
name: 'actions',
columnLabel: translate('Actions'),
isVisible: true,
isModifiable: false
}
]
}, },
// //

View File

@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.CustomFormats; using NzbDrone.Core.CustomFormats;
using Radarr.Http; using Radarr.Http;
using Radarr.Http.REST; using Radarr.Http.REST;