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:
Qstick
2019-02-23 17:39:11 -05:00
committed by GitHub
parent f126eafd26
commit 3f064c94b9
409 changed files with 6882 additions and 3176 deletions
@@ -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,
@@ -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
@@ -67,9 +67,7 @@ class EditDownloadClientModalContent extends Component {
{
!isFetching && !error &&
<Form
{...otherProps}
>
<Form {...otherProps}>
{
!!message &&
<Alert
@@ -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,
@@ -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 {
@@ -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);