mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-26 22:46:37 -04:00
New: Release Profiles, Frontend updates (#580)
* New: Release Profiles - UI Updates * New: Release Profiles - API Changes * New: Release Profiles - Test Updates * New: Release Profiles - Backend Updates * New: Interactive Artist Search * New: Change Montiored on Album Details Page * New: Show Duration on Album Details Page * Fixed: Manual Import not working if no albums are Missing * Fixed: Sort search input by sortTitle * Fixed: Queue columnLabel throwing JS error
This commit is contained in:
+10
-10
@@ -19,9 +19,9 @@ class AddDownloadClientModalContent extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
isFetching,
|
||||
error,
|
||||
isPopulated,
|
||||
isSchemaFetching,
|
||||
isSchemaPopulated,
|
||||
schemaError,
|
||||
usenetDownloadClients,
|
||||
torrentDownloadClients,
|
||||
onDownloadClientSelect,
|
||||
@@ -31,22 +31,22 @@ class AddDownloadClientModalContent extends Component {
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Add DownloadClient
|
||||
Add Download Client
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{
|
||||
isFetching &&
|
||||
isSchemaFetching &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
!isSchemaFetching && !!schemaError &&
|
||||
<div>Unable to add a new downloadClient, please try again.</div>
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && !error &&
|
||||
isSchemaPopulated && !schemaError &&
|
||||
<div>
|
||||
|
||||
<Alert kind={kinds.INFO}>
|
||||
@@ -103,9 +103,9 @@ class AddDownloadClientModalContent extends Component {
|
||||
}
|
||||
|
||||
AddDownloadClientModalContent.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
isSchemaFetching: PropTypes.bool.isRequired,
|
||||
isSchemaPopulated: PropTypes.bool.isRequired,
|
||||
schemaError: PropTypes.object,
|
||||
usenetDownloadClients: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
torrentDownloadClients: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onDownloadClientSelect: PropTypes.func.isRequired,
|
||||
|
||||
+6
-6
@@ -11,9 +11,9 @@ function createMapStateToProps() {
|
||||
(state) => state.settings.downloadClients,
|
||||
(downloadClients) => {
|
||||
const {
|
||||
isFetching,
|
||||
error,
|
||||
isPopulated,
|
||||
isSchemaFetching,
|
||||
isSchemaPopulated,
|
||||
schemaError,
|
||||
schema
|
||||
} = downloadClients;
|
||||
|
||||
@@ -21,9 +21,9 @@ function createMapStateToProps() {
|
||||
const torrentDownloadClients = _.filter(schema, { protocol: 'torrent' });
|
||||
|
||||
return {
|
||||
isFetching,
|
||||
error,
|
||||
isPopulated,
|
||||
isSchemaFetching,
|
||||
isSchemaPopulated,
|
||||
schemaError,
|
||||
usenetDownloadClients,
|
||||
torrentDownloadClients
|
||||
};
|
||||
|
||||
@@ -68,12 +68,18 @@ class DownloadClient extends Component {
|
||||
</div>
|
||||
|
||||
<div className={styles.enabled}>
|
||||
<Label
|
||||
kind={enable ? kinds.SUCCESS : kinds.DANGER}
|
||||
outline={!enable}
|
||||
>
|
||||
Enabled
|
||||
</Label>
|
||||
{
|
||||
enable ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
Enabled
|
||||
</Label> :
|
||||
<Label
|
||||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
>
|
||||
Disabled
|
||||
</Label>
|
||||
}
|
||||
</div>
|
||||
|
||||
<EditDownloadClientModalConnector
|
||||
|
||||
+1
-3
@@ -67,9 +67,7 @@ class EditDownloadClientModalContent extends Component {
|
||||
|
||||
{
|
||||
!isFetching && !error &&
|
||||
<Form
|
||||
{...otherProps}
|
||||
>
|
||||
<Form {...otherProps}>
|
||||
{
|
||||
!!message &&
|
||||
<Alert
|
||||
|
||||
+5
-4
@@ -23,6 +23,7 @@ function EditRemotePathMappingModalContent(props) {
|
||||
isSaving,
|
||||
saveError,
|
||||
item,
|
||||
downloadClientHosts,
|
||||
onInputChange,
|
||||
onSavePress,
|
||||
onModalClose,
|
||||
@@ -55,17 +56,16 @@ function EditRemotePathMappingModalContent(props) {
|
||||
|
||||
{
|
||||
!isFetching && !error &&
|
||||
<Form
|
||||
{...otherProps}
|
||||
>
|
||||
<Form {...otherProps}>
|
||||
<FormGroup>
|
||||
<FormLabel>Host</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
type={inputTypes.AUTO_COMPLETE}
|
||||
name="host"
|
||||
helpText="The same host you specified for the remote Download Client"
|
||||
{...host}
|
||||
values={downloadClientHosts}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
@@ -140,6 +140,7 @@ EditRemotePathMappingModalContent.propTypes = {
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
item: PropTypes.shape(remotePathMappingShape).isRequired,
|
||||
downloadClientHosts: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
onInputChange: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
|
||||
+28
-9
@@ -13,11 +13,29 @@ const newRemotePathMapping = {
|
||||
localPath: ''
|
||||
};
|
||||
|
||||
const selectDownloadClientHosts = createSelector(
|
||||
(state) => state.settings.downloadClients.items,
|
||||
(downloadClients) => {
|
||||
return downloadClients.reduce((acc, downloadClient) => {
|
||||
const host = downloadClient.fields.find((field) => {
|
||||
return field.name === 'host';
|
||||
});
|
||||
|
||||
if (host && !acc.includes(host.value)) {
|
||||
acc.push(host.value);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
);
|
||||
|
||||
function createRemotePathMappingSelector() {
|
||||
return createSelector(
|
||||
(state, { id }) => id,
|
||||
(state) => state.settings.remotePathMappings,
|
||||
(id, remotePathMappings) => {
|
||||
selectDownloadClientHosts,
|
||||
(id, remotePathMappings, downloadClientHosts) => {
|
||||
const {
|
||||
isFetching,
|
||||
error,
|
||||
@@ -37,7 +55,8 @@ function createRemotePathMappingSelector() {
|
||||
isSaving,
|
||||
saveError,
|
||||
item: settings.settings,
|
||||
...settings
|
||||
...settings,
|
||||
downloadClientHosts
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -55,8 +74,8 @@ function createMapStateToProps() {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setRemotePathMappingValue,
|
||||
saveRemotePathMapping
|
||||
dispatchSetRemotePathMappingValue: setRemotePathMappingValue,
|
||||
dispatchSaveRemotePathMapping: saveRemotePathMapping
|
||||
};
|
||||
|
||||
class EditRemotePathMappingModalContentConnector extends Component {
|
||||
@@ -67,7 +86,7 @@ class EditRemotePathMappingModalContentConnector extends Component {
|
||||
componentDidMount() {
|
||||
if (!this.props.id) {
|
||||
Object.keys(newRemotePathMapping).forEach((name) => {
|
||||
this.props.setRemotePathMappingValue({
|
||||
this.props.dispatchSetRemotePathMappingValue({
|
||||
name,
|
||||
value: newRemotePathMapping[name]
|
||||
});
|
||||
@@ -85,11 +104,11 @@ class EditRemotePathMappingModalContentConnector extends Component {
|
||||
// Listeners
|
||||
|
||||
onInputChange = ({ name, value }) => {
|
||||
this.props.setRemotePathMappingValue({ name, value });
|
||||
this.props.dispatchSetRemotePathMappingValue({ name, value });
|
||||
}
|
||||
|
||||
onSavePress = () => {
|
||||
this.props.saveRemotePathMapping({ id: this.props.id });
|
||||
this.props.dispatchSaveRemotePathMapping({ id: this.props.id });
|
||||
}
|
||||
|
||||
//
|
||||
@@ -111,8 +130,8 @@ EditRemotePathMappingModalContentConnector.propTypes = {
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
item: PropTypes.object.isRequired,
|
||||
setRemotePathMappingValue: PropTypes.func.isRequired,
|
||||
saveRemotePathMapping: PropTypes.func.isRequired,
|
||||
dispatchSetRemotePathMappingValue: PropTypes.func.isRequired,
|
||||
dispatchSaveRemotePathMapping: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
}
|
||||
|
||||
.host {
|
||||
flex: 0 0 300px;
|
||||
@add-mixin truncate;
|
||||
|
||||
flex: 0 1 300px;
|
||||
}
|
||||
|
||||
.path {
|
||||
flex: 0 0 400px;
|
||||
@add-mixin truncate;
|
||||
|
||||
flex: 0 1 400px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
}
|
||||
|
||||
.host {
|
||||
flex: 0 0 300px;
|
||||
@add-mixin truncate;
|
||||
|
||||
flex: 0 1 300px;
|
||||
}
|
||||
|
||||
.path {
|
||||
flex: 0 0 400px;
|
||||
@add-mixin truncate;
|
||||
|
||||
flex: 0 1 400px;
|
||||
}
|
||||
|
||||
.addRemotePathMapping {
|
||||
|
||||
+6
-6
@@ -17,8 +17,8 @@ function createMapStateToProps() {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchRemotePathMappings,
|
||||
deleteRemotePathMapping
|
||||
dispatchFetchRemotePathMappings: fetchRemotePathMappings,
|
||||
dispatchDeleteRemotePathMapping: deleteRemotePathMapping
|
||||
};
|
||||
|
||||
class RemotePathMappingsConnector extends Component {
|
||||
@@ -27,14 +27,14 @@ class RemotePathMappingsConnector extends Component {
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchRemotePathMappings();
|
||||
this.props.dispatchFetchRemotePathMappings();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onConfirmDeleteRemotePathMapping = (id) => {
|
||||
this.props.deleteRemotePathMapping({ id });
|
||||
this.props.dispatchDeleteRemotePathMapping({ id });
|
||||
}
|
||||
|
||||
//
|
||||
@@ -52,8 +52,8 @@ class RemotePathMappingsConnector extends Component {
|
||||
}
|
||||
|
||||
RemotePathMappingsConnector.propTypes = {
|
||||
fetchRemotePathMappings: PropTypes.func.isRequired,
|
||||
deleteRemotePathMapping: PropTypes.func.isRequired
|
||||
dispatchFetchRemotePathMappings: PropTypes.func.isRequired,
|
||||
dispatchDeleteRemotePathMapping: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(RemotePathMappingsConnector);
|
||||
|
||||
Reference in New Issue
Block a user