mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-08 05:51:47 -07:00
New: Interactive Search from Album Detail Page
This commit is contained in:
parent
ff3f52eb3f
commit
ec2dc34098
35 changed files with 361 additions and 1783 deletions
|
@ -5,7 +5,6 @@ import IconButton from 'Components/Link/IconButton';
|
||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import albumEntities from 'Album/albumEntities';
|
|
||||||
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
||||||
import EpisodeLanguage from 'Album/EpisodeLanguage';
|
import EpisodeLanguage from 'Album/EpisodeLanguage';
|
||||||
import EpisodeQuality from 'Album/EpisodeQuality';
|
import EpisodeQuality from 'Album/EpisodeQuality';
|
||||||
|
@ -53,7 +52,6 @@ class HistoryRow extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
albumId,
|
|
||||||
artist,
|
artist,
|
||||||
album,
|
album,
|
||||||
track,
|
track,
|
||||||
|
@ -114,12 +112,8 @@ class HistoryRow extends Component {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<AlbumTitleLink
|
<AlbumTitleLink
|
||||||
albumId={albumId}
|
foreignAlbumId={album.foreignAlbumId}
|
||||||
albumEntity={albumEntities.ALBUMS}
|
title={album.title}
|
||||||
artistId={artist.id}
|
|
||||||
albumTitle={album.title}
|
|
||||||
showOpenArtistButton={true}
|
|
||||||
showOpenAlbumButton={true}
|
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
|
|
|
@ -156,12 +156,8 @@ class QueueRow extends Component {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<AlbumTitleLink
|
<AlbumTitleLink
|
||||||
albumId={album.id}
|
foreignAlbumId={album.foreignAlbumId}
|
||||||
artistId={artist.id}
|
title={album.title}
|
||||||
trackFileId={album.trackFileId}
|
|
||||||
albumTitle={album.title}
|
|
||||||
showOpenArtistButton={true}
|
|
||||||
showOpenAlbumButton={true}
|
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import AlbumDetailsModalContentConnector from './AlbumDetailsModalContentConnector';
|
|
||||||
|
|
||||||
class AlbumDetailsModal extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
onModalClose,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<AlbumDetailsModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumDetailsModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlbumDetailsModal;
|
|
|
@ -1,48 +0,0 @@
|
||||||
.artistName {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
margin: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs {
|
|
||||||
margin-top: -32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabList {
|
|
||||||
margin: 0 0 10px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
position: relative;
|
|
||||||
bottom: -1px;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 6px 12px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-top: none;
|
|
||||||
list-style: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedTab {
|
|
||||||
border-color: $borderColor;
|
|
||||||
border-radius: 0 0 5px 5px;
|
|
||||||
background-color: rgba(239, 239, 239, 0.4);
|
|
||||||
color: $black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabPanel {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.openArtistButton {
|
|
||||||
composes: button from 'Components/Link/Button.css';
|
|
||||||
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.openButtons {
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
|
||||||
import albumEntities from 'Album/albumEntities';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
|
||||||
import MonitorToggleButton from 'Components/MonitorToggleButton';
|
|
||||||
import AlbumHistoryConnector from './History/AlbumHistoryConnector';
|
|
||||||
import AlbumSearchConnector from './Search/AlbumSearchConnector';
|
|
||||||
import styles from './AlbumDetailsModalContent.css';
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
'history',
|
|
||||||
'search'
|
|
||||||
];
|
|
||||||
|
|
||||||
class AlbumDetailsModalContent extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
selectedTab: props.selectedTab
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onTabSelect = (index, lastIndex) => {
|
|
||||||
this.setState({ selectedTab: tabs[index] });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
albumId,
|
|
||||||
artistName,
|
|
||||||
foreignArtistId,
|
|
||||||
foreignAlbumId,
|
|
||||||
artistMonitored,
|
|
||||||
albumTitle,
|
|
||||||
monitored,
|
|
||||||
isSaving,
|
|
||||||
showOpenArtistButton,
|
|
||||||
showOpenAlbumButton,
|
|
||||||
startInteractiveSearch,
|
|
||||||
onMonitorAlbumPress,
|
|
||||||
onModalClose
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const artistLink = `/artist/${foreignArtistId}`;
|
|
||||||
const albumLink = `/album/${foreignAlbumId}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<ModalHeader>
|
|
||||||
<MonitorToggleButton
|
|
||||||
className={styles.toggleButton}
|
|
||||||
id={albumId}
|
|
||||||
monitored={monitored}
|
|
||||||
size={18}
|
|
||||||
isDisabled={!artistMonitored}
|
|
||||||
isSaving={isSaving}
|
|
||||||
onPress={onMonitorAlbumPress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className={styles.artistName}>
|
|
||||||
{artistName}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className={styles.separator}>-</span>
|
|
||||||
|
|
||||||
{albumTitle}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<Tabs
|
|
||||||
className={styles.tabs}
|
|
||||||
selectedIndex={tabs.indexOf(this.state.selectedTab)}
|
|
||||||
onSelect={this.onTabSelect}
|
|
||||||
>
|
|
||||||
<TabList
|
|
||||||
className={styles.tabList}
|
|
||||||
>
|
|
||||||
<Tab
|
|
||||||
className={styles.tab}
|
|
||||||
selectedClassName={styles.selectedTab}
|
|
||||||
>
|
|
||||||
History
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
className={styles.tab}
|
|
||||||
selectedClassName={styles.selectedTab}
|
|
||||||
>
|
|
||||||
Search
|
|
||||||
</Tab>
|
|
||||||
</TabList>
|
|
||||||
|
|
||||||
<TabPanel className={styles.tabPanel}>
|
|
||||||
<AlbumHistoryConnector
|
|
||||||
albumId={albumId}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
<TabPanel className={styles.tabPanel}>
|
|
||||||
<AlbumSearchConnector
|
|
||||||
albumId={albumId}
|
|
||||||
startInteractiveSearch={startInteractiveSearch}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
</Tabs>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter >
|
|
||||||
<div className={styles.openButtons}>
|
|
||||||
{
|
|
||||||
showOpenArtistButton &&
|
|
||||||
<Button
|
|
||||||
className={styles.openArtistButton}
|
|
||||||
to={artistLink}
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
Open Artist
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
showOpenAlbumButton &&
|
|
||||||
<Button
|
|
||||||
className={styles.openAlbumButton}
|
|
||||||
to={albumLink}
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
Open Album
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumDetailsModalContent.propTypes = {
|
|
||||||
albumId: PropTypes.number.isRequired,
|
|
||||||
albumEntity: PropTypes.string.isRequired,
|
|
||||||
artistId: PropTypes.number.isRequired,
|
|
||||||
artistName: PropTypes.string.isRequired,
|
|
||||||
foreignArtistId: PropTypes.string.isRequired,
|
|
||||||
foreignAlbumId: PropTypes.string.isRequired,
|
|
||||||
artistMonitored: PropTypes.bool.isRequired,
|
|
||||||
releaseDate: PropTypes.string.isRequired,
|
|
||||||
albumLabel: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
albumTitle: PropTypes.string.isRequired,
|
|
||||||
monitored: PropTypes.bool.isRequired,
|
|
||||||
isSaving: PropTypes.bool,
|
|
||||||
showOpenArtistButton: PropTypes.bool,
|
|
||||||
showOpenAlbumButton: PropTypes.bool,
|
|
||||||
selectedTab: PropTypes.string.isRequired,
|
|
||||||
startInteractiveSearch: PropTypes.bool.isRequired,
|
|
||||||
onMonitorAlbumPress: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
AlbumDetailsModalContent.defaultProps = {
|
|
||||||
selectedTab: 'history',
|
|
||||||
albumLabel: ['Unknown'],
|
|
||||||
albumEntity: albumEntities.ALBUMS,
|
|
||||||
startInteractiveSearch: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlbumDetailsModalContent;
|
|
|
@ -1,125 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions';
|
|
||||||
import { toggleAlbumMonitored } from 'Store/Actions/albumActions';
|
|
||||||
import createAlbumSelector from 'Store/Selectors/createAlbumSelector';
|
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
|
||||||
import albumEntities from 'Album/albumEntities';
|
|
||||||
import { fetchTracks, clearTracks } from 'Store/Actions/trackActions';
|
|
||||||
import AlbumDetailsModalContent from './AlbumDetailsModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createAlbumSelector(),
|
|
||||||
createArtistSelector(),
|
|
||||||
(album, artist) => {
|
|
||||||
const {
|
|
||||||
artistName,
|
|
||||||
foreignArtistId,
|
|
||||||
monitored: artistMonitored
|
|
||||||
} = artist;
|
|
||||||
|
|
||||||
return {
|
|
||||||
artistName,
|
|
||||||
foreignArtistId,
|
|
||||||
artistMonitored,
|
|
||||||
...album
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
|
||||||
return {
|
|
||||||
dispatchCancelFetchReleases() {
|
|
||||||
dispatch(cancelFetchReleases());
|
|
||||||
},
|
|
||||||
|
|
||||||
dispatchClearReleases() {
|
|
||||||
dispatch(clearReleases());
|
|
||||||
},
|
|
||||||
|
|
||||||
dispatchFetchTracks({ artistId, albumId }) {
|
|
||||||
dispatch(fetchTracks({ artistId, albumId }));
|
|
||||||
},
|
|
||||||
|
|
||||||
dispatchClearTracks() {
|
|
||||||
dispatch(clearTracks());
|
|
||||||
},
|
|
||||||
|
|
||||||
onMonitorAlbumPress(monitored) {
|
|
||||||
const {
|
|
||||||
albumId,
|
|
||||||
albumEntity
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
dispatch(toggleAlbumMonitored({
|
|
||||||
albumEntity,
|
|
||||||
albumId,
|
|
||||||
monitored
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class AlbumDetailsModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
componentDidMount() {
|
|
||||||
this._populate();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
// Clear pending releases here so we can reshow the search
|
|
||||||
// results even after switching tabs.
|
|
||||||
this._unpopulate();
|
|
||||||
this.props.dispatchCancelFetchReleases();
|
|
||||||
this.props.dispatchClearReleases();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Control
|
|
||||||
|
|
||||||
_populate() {
|
|
||||||
const artistId = this.props.artistId;
|
|
||||||
const albumId = this.props.albumId;
|
|
||||||
this.props.dispatchFetchTracks({ artistId, albumId });
|
|
||||||
}
|
|
||||||
|
|
||||||
_unpopulate() {
|
|
||||||
this.props.dispatchClearTracks();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
dispatchClearReleases,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AlbumDetailsModalContent {...otherProps} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumDetailsModalContentConnector.propTypes = {
|
|
||||||
albumId: PropTypes.number.isRequired,
|
|
||||||
albumEntity: PropTypes.string.isRequired,
|
|
||||||
artistId: PropTypes.number.isRequired,
|
|
||||||
dispatchFetchTracks: PropTypes.func.isRequired,
|
|
||||||
dispatchClearTracks: PropTypes.func.isRequired,
|
|
||||||
dispatchCancelFetchReleases: PropTypes.func.isRequired,
|
|
||||||
dispatchClearReleases: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
AlbumDetailsModalContentConnector.defaultProps = {
|
|
||||||
albumEntity: albumEntities.ALBUMS
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(AlbumDetailsModalContentConnector);
|
|
|
@ -4,7 +4,7 @@ import { icons } from 'Helpers/Props';
|
||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import AlbumDetailsModal from './AlbumDetailsModal';
|
import InteractiveAlbumSearchModal from './Search/InteractiveAlbumSearchModal';
|
||||||
import styles from './AlbumSearchCell.css';
|
import styles from './AlbumSearchCell.css';
|
||||||
|
|
||||||
class AlbumSearchCell extends Component {
|
class AlbumSearchCell extends Component {
|
||||||
|
@ -37,8 +37,6 @@ class AlbumSearchCell extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
albumId,
|
albumId,
|
||||||
artistId,
|
|
||||||
albumTitle,
|
|
||||||
isSearching,
|
isSearching,
|
||||||
onSearchPress,
|
onSearchPress,
|
||||||
...otherProps
|
...otherProps
|
||||||
|
@ -57,13 +55,9 @@ class AlbumSearchCell extends Component {
|
||||||
onPress={this.onManualSearchPress}
|
onPress={this.onManualSearchPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AlbumDetailsModal
|
<InteractiveAlbumSearchModal
|
||||||
isOpen={this.state.isDetailsModalOpen}
|
isOpen={this.state.isDetailsModalOpen}
|
||||||
albumId={albumId}
|
albumId={albumId}
|
||||||
artistId={artistId}
|
|
||||||
albumTitle={albumTitle}
|
|
||||||
selectedTab="search"
|
|
||||||
startInteractiveSearch={true}
|
|
||||||
onModalClose={this.onDetailsModalClose}
|
onModalClose={this.onDetailsModalClose}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Link from 'Components/Link/Link';
|
|
||||||
|
|
||||||
function AlbumTitleDetailLink({ foreignAlbumId, title }) {
|
|
||||||
const link = `/album/${foreignAlbumId}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link to={link}>
|
|
||||||
{title}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumTitleDetailLink.propTypes = {
|
|
||||||
foreignAlbumId: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlbumTitleDetailLink;
|
|
|
@ -1,68 +1,20 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import AlbumDetailsModal from 'Album/AlbumDetailsModal';
|
|
||||||
import styles from './AlbumTitleLink.css';
|
|
||||||
|
|
||||||
class AlbumTitleLink extends Component {
|
function AlbumTitleLink({ foreignAlbumId, title }) {
|
||||||
|
const link = `/album/${foreignAlbumId}`;
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isDetailsModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onLinkPress = () => {
|
|
||||||
this.setState({ isDetailsModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.setState({ isDetailsModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
albumTitle,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Link to={link}>
|
||||||
<Link
|
{title}
|
||||||
className={styles.link}
|
|
||||||
onPress={this.onLinkPress}
|
|
||||||
>
|
|
||||||
{albumTitle}
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<AlbumDetailsModal
|
|
||||||
isOpen={this.state.isDetailsModalOpen}
|
|
||||||
albumTitle={albumTitle}
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AlbumTitleLink.propTypes = {
|
AlbumTitleLink.propTypes = {
|
||||||
albumTitle: PropTypes.string.isRequired
|
foreignAlbumId: PropTypes.string.isRequired,
|
||||||
};
|
title: PropTypes.string.isRequired
|
||||||
|
|
||||||
AlbumTitleLink.defaultProps = {
|
|
||||||
showArtistButton: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AlbumTitleLink;
|
export default AlbumTitleLink;
|
||||||
|
|
|
@ -22,6 +22,7 @@ import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||||
import AlbumDetailsMediumConnector from './AlbumDetailsMediumConnector';
|
import AlbumDetailsMediumConnector from './AlbumDetailsMediumConnector';
|
||||||
import ArtistHistoryModal from 'Artist/History/ArtistHistoryModal';
|
import ArtistHistoryModal from 'Artist/History/ArtistHistoryModal';
|
||||||
|
import InteractiveAlbumSearchModal from 'Album/Search/InteractiveAlbumSearchModal';
|
||||||
import TrackFileEditorModal from 'TrackFile/Editor/TrackFileEditorModal';
|
import TrackFileEditorModal from 'TrackFile/Editor/TrackFileEditorModal';
|
||||||
|
|
||||||
import styles from './AlbumDetails.css';
|
import styles from './AlbumDetails.css';
|
||||||
|
@ -53,6 +54,7 @@ class AlbumDetails extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
isOrganizeModalOpen: false,
|
isOrganizeModalOpen: false,
|
||||||
isArtistHistoryModalOpen: false,
|
isArtistHistoryModalOpen: false,
|
||||||
|
isInteractiveSearchModalOpen: false,
|
||||||
isManageTracksOpen: false,
|
isManageTracksOpen: false,
|
||||||
isEditAlbumModalOpen: false,
|
isEditAlbumModalOpen: false,
|
||||||
allExpanded: false,
|
allExpanded: false,
|
||||||
|
@ -88,6 +90,14 @@ class AlbumDetails extends Component {
|
||||||
this.setState({ isManageTracksOpen: false });
|
this.setState({ isManageTracksOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onInteractiveSearchPress = () => {
|
||||||
|
this.setState({ isInteractiveSearchModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
onInteractiveSearchModalClose = () => {
|
||||||
|
this.setState({ isInteractiveSearchModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
onArtistHistoryPress = () => {
|
onArtistHistoryPress = () => {
|
||||||
this.setState({ isArtistHistoryModalOpen: true });
|
this.setState({ isArtistHistoryModalOpen: true });
|
||||||
}
|
}
|
||||||
|
@ -147,6 +157,7 @@ class AlbumDetails extends Component {
|
||||||
const {
|
const {
|
||||||
isOrganizeModalOpen,
|
isOrganizeModalOpen,
|
||||||
isArtistHistoryModalOpen,
|
isArtistHistoryModalOpen,
|
||||||
|
isInteractiveSearchModalOpen,
|
||||||
isEditAlbumModalOpen,
|
isEditAlbumModalOpen,
|
||||||
isManageTracksOpen,
|
isManageTracksOpen,
|
||||||
allExpanded,
|
allExpanded,
|
||||||
|
@ -173,6 +184,12 @@ class AlbumDetails extends Component {
|
||||||
onPress={onSearchPress}
|
onPress={onSearchPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<PageToolbarButton
|
||||||
|
label="Interactive Search"
|
||||||
|
iconName={icons.INTERACTIVE}
|
||||||
|
onPress={this.onInteractiveSearchPress}
|
||||||
|
/>
|
||||||
|
|
||||||
<PageToolbarSeparator />
|
<PageToolbarSeparator />
|
||||||
|
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
|
@ -396,6 +413,12 @@ class AlbumDetails extends Component {
|
||||||
onModalClose={this.onManageTracksModalClose}
|
onModalClose={this.onManageTracksModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<InteractiveAlbumSearchModal
|
||||||
|
isOpen={isInteractiveSearchModalOpen}
|
||||||
|
albumId={id}
|
||||||
|
onModalClose={this.onInteractiveSearchModalClose}
|
||||||
|
/>
|
||||||
|
|
||||||
<ArtistHistoryModal
|
<ArtistHistoryModal
|
||||||
isOpen={isArtistHistoryModalOpen}
|
isOpen={isArtistHistoryModalOpen}
|
||||||
artistId={artist.id}
|
artistId={artist.id}
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
import Table from 'Components/Table/Table';
|
|
||||||
import TableBody from 'Components/Table/TableBody';
|
|
||||||
import AlbumHistoryRow from './AlbumHistoryRow';
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'eventType',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sourceTitle',
|
|
||||||
label: 'Source Title',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'language',
|
|
||||||
label: 'Language',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'quality',
|
|
||||||
label: 'Quality',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'date',
|
|
||||||
label: 'Date',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'details',
|
|
||||||
label: 'Details',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'actions',
|
|
||||||
label: 'Actions',
|
|
||||||
isVisible: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
class AlbumHistory extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
items,
|
|
||||||
onMarkAsFailedPress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const hasItems = !!items.length;
|
|
||||||
|
|
||||||
if (isFetching) {
|
|
||||||
return (
|
|
||||||
<LoadingIndicator />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFetching && !!error) {
|
|
||||||
return (
|
|
||||||
<div>Unable to load album history.</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPopulated && !hasItems && !error) {
|
|
||||||
return (
|
|
||||||
<div>No album history.</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPopulated && hasItems && !error) {
|
|
||||||
return (
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
>
|
|
||||||
<TableBody>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<AlbumHistoryRow
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
onMarkAsFailedPress={onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumHistory.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
AlbumHistory.defaultProps = {
|
|
||||||
selectedTab: 'details'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlbumHistory;
|
|
|
@ -1,63 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { fetchAlbumHistory, clearAlbumHistory, albumHistoryMarkAsFailed } from 'Store/Actions/albumHistoryActions';
|
|
||||||
import AlbumHistory from './AlbumHistory';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.albumHistory,
|
|
||||||
(albumHistory) => {
|
|
||||||
return albumHistory;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchAlbumHistory,
|
|
||||||
clearAlbumHistory,
|
|
||||||
albumHistoryMarkAsFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
class AlbumHistoryConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchAlbumHistory({ albumId: this.props.albumId });
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.props.clearAlbumHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onMarkAsFailedPress = (historyId) => {
|
|
||||||
this.props.albumHistoryMarkAsFailed({ historyId, albumId: this.props.albumId });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<AlbumHistory
|
|
||||||
{...this.props}
|
|
||||||
onMarkAsFailedPress={this.onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumHistoryConnector.propTypes = {
|
|
||||||
albumId: PropTypes.number.isRequired,
|
|
||||||
fetchAlbumHistory: PropTypes.func.isRequired,
|
|
||||||
clearAlbumHistory: PropTypes.func.isRequired,
|
|
||||||
albumHistoryMarkAsFailed: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(AlbumHistoryConnector);
|
|
|
@ -1,6 +0,0 @@
|
||||||
.details,
|
|
||||||
.actions {
|
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 65px;
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import IconButton from 'Components/Link/IconButton';
|
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|
||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
|
||||||
import TableRow from 'Components/Table/TableRow';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
|
||||||
import EpisodeLanguage from 'Album/EpisodeLanguage';
|
|
||||||
import EpisodeQuality from 'Album/EpisodeQuality';
|
|
||||||
import HistoryDetailsConnector from 'Activity/History/Details/HistoryDetailsConnector';
|
|
||||||
import HistoryEventTypeCell from 'Activity/History/HistoryEventTypeCell';
|
|
||||||
import styles from './AlbumHistoryRow.css';
|
|
||||||
|
|
||||||
function getTitle(eventType) {
|
|
||||||
switch (eventType) {
|
|
||||||
case 'grabbed': return 'Grabbed';
|
|
||||||
case 'artistFolderImported': return 'Artist Folder Imported';
|
|
||||||
case 'downloadFolderImported': return 'Download Folder Imported';
|
|
||||||
case 'downloadFailed': return 'Download Failed';
|
|
||||||
case 'trackFileDeleted': return 'Track File Deleted';
|
|
||||||
case 'trackFileRenamed': return 'Track File Renamed';
|
|
||||||
default: return 'Unknown';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AlbumHistoryRow 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,
|
|
||||||
language,
|
|
||||||
languageCutoffNotMet,
|
|
||||||
quality,
|
|
||||||
qualityCutoffNotMet,
|
|
||||||
date,
|
|
||||||
data
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
isMarkAsFailedModalOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow>
|
|
||||||
<HistoryEventTypeCell
|
|
||||||
eventType={eventType}
|
|
||||||
data={data}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
{sourceTitle}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
<EpisodeLanguage
|
|
||||||
language={language}
|
|
||||||
isCutoffNotMet={languageCutoffNotMet}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
|
|
||||||
<EpisodeQuality
|
|
||||||
quality={quality}
|
|
||||||
isCutoffNotMet={qualityCutoffNotMet}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<RelativeDateCellConnector
|
|
||||||
date={date}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.details}>
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Icon
|
|
||||||
name={icons.INFO}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
title={getTitle(eventType)}
|
|
||||||
body={
|
|
||||||
<HistoryDetailsConnector
|
|
||||||
eventType={eventType}
|
|
||||||
sourceTitle={sourceTitle}
|
|
||||||
data={data}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.LEFT}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.actions}>
|
|
||||||
{
|
|
||||||
eventType === 'grabbed' &&
|
|
||||||
<IconButton
|
|
||||||
name={icons.REMOVE}
|
|
||||||
onPress={this.onMarkAsFailedPress}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={isMarkAsFailedModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title="Mark as Failed"
|
|
||||||
message={`Are you sure you want to mark '${sourceTitle}' as failed?`}
|
|
||||||
confirmLabel="Mark as Failed"
|
|
||||||
onConfirm={this.onConfirmMarkAsFailed}
|
|
||||||
onCancel={this.onMarkAsFailedModalClose}
|
|
||||||
/>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumHistoryRow.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
eventType: PropTypes.string.isRequired,
|
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
|
||||||
language: PropTypes.object.isRequired,
|
|
||||||
languageCutoffNotMet: PropTypes.bool.isRequired,
|
|
||||||
quality: PropTypes.object.isRequired,
|
|
||||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
|
||||||
date: PropTypes.string.isRequired,
|
|
||||||
data: PropTypes.object.isRequired,
|
|
||||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlbumHistoryRow;
|
|
|
@ -1,16 +0,0 @@
|
||||||
.buttonContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
composes: button from 'Components/Link/Button.css';
|
|
||||||
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonIcon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { icons, kinds, sizes } from 'Helpers/Props';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import styles from './AlbumSearch.css';
|
|
||||||
|
|
||||||
function AlbumSearch(props) {
|
|
||||||
const {
|
|
||||||
onQuickSearchPress,
|
|
||||||
onInteractiveSearchPress
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={styles.buttonContainer}>
|
|
||||||
<Button
|
|
||||||
className={styles.button}
|
|
||||||
size={sizes.LARGE}
|
|
||||||
onPress={onQuickSearchPress}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
className={styles.buttonIcon}
|
|
||||||
name={icons.QUICK}
|
|
||||||
/>
|
|
||||||
|
|
||||||
Quick Search
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.buttonContainer}>
|
|
||||||
<Button
|
|
||||||
className={styles.button}
|
|
||||||
kind={kinds.PRIMARY}
|
|
||||||
size={sizes.LARGE}
|
|
||||||
onPress={onInteractiveSearchPress}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
className={styles.buttonIcon}
|
|
||||||
name={icons.INTERACTIVE}
|
|
||||||
/>
|
|
||||||
|
|
||||||
Interactive Search
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumSearch.propTypes = {
|
|
||||||
onQuickSearchPress: PropTypes.func.isRequired,
|
|
||||||
onInteractiveSearchPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlbumSearch;
|
|
|
@ -1,90 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
|
||||||
import * as commandNames from 'Commands/commandNames';
|
|
||||||
import AlbumSearch from './AlbumSearch';
|
|
||||||
import InteractiveAlbumSearchConnector from './InteractiveAlbumSearchConnector';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.releases,
|
|
||||||
(releases) => {
|
|
||||||
return {
|
|
||||||
isPopulated: releases.isPopulated
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
executeCommand
|
|
||||||
};
|
|
||||||
|
|
||||||
class AlbumSearchConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isInteractiveSearchOpen: props.startInteractiveSearch
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
if (this.props.isPopulated) {
|
|
||||||
this.setState({ isInteractiveSearchOpen: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onQuickSearchPress = () => {
|
|
||||||
this.props.executeCommand({
|
|
||||||
name: commandNames.ALBUM_SEARCH,
|
|
||||||
albumIds: [this.props.albumId]
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onModalClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
onInteractiveSearchPress = () => {
|
|
||||||
this.setState({ isInteractiveSearchOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.state.isInteractiveSearchOpen) {
|
|
||||||
return (
|
|
||||||
<InteractiveAlbumSearchConnector
|
|
||||||
{...this.props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AlbumSearch
|
|
||||||
{...this.props}
|
|
||||||
onQuickSearchPress={this.onQuickSearchPress}
|
|
||||||
onInteractiveSearchPress={this.onInteractiveSearchPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumSearchConnector.propTypes = {
|
|
||||||
albumId: PropTypes.number.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
startInteractiveSearch: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
executeCommand: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(AlbumSearchConnector);
|
|
|
@ -1,130 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { icons, sortDirections } from 'Helpers/Props';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import Table from 'Components/Table/Table';
|
|
||||||
import TableBody from 'Components/Table/TableBody';
|
|
||||||
import InteractiveAlbumSearchRow from './InteractiveAlbumSearchRow';
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'protocol',
|
|
||||||
label: 'Source',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'age',
|
|
||||||
label: 'Age',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
label: 'Title',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'indexer',
|
|
||||||
label: 'Indexer',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'size',
|
|
||||||
label: 'Size',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'peers',
|
|
||||||
label: 'Peers',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'qualityWeight',
|
|
||||||
label: 'Quality',
|
|
||||||
isSortable: true,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'rejections',
|
|
||||||
label: React.createElement(Icon, { name: icons.DANGER }),
|
|
||||||
isSortable: true,
|
|
||||||
fixedSortDirection: sortDirections.ASCENDING,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'releaseWeight',
|
|
||||||
label: React.createElement(Icon, { name: icons.DOWNLOAD }),
|
|
||||||
isSortable: true,
|
|
||||||
fixedSortDirection: sortDirections.ASCENDING,
|
|
||||||
isVisible: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function InteractiveAlbumSearch(props) {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
items,
|
|
||||||
sortKey,
|
|
||||||
sortDirection,
|
|
||||||
longDateFormat,
|
|
||||||
timeFormat,
|
|
||||||
onSortPress,
|
|
||||||
onGrabPress
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (isFetching) {
|
|
||||||
return <LoadingIndicator />;
|
|
||||||
} else if (!isFetching && !!error) {
|
|
||||||
return <div>Unable to load results for this album search. Try again later.</div>;
|
|
||||||
} else if (isPopulated && !items.length) {
|
|
||||||
return <div>No results found.</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
sortKey={sortKey}
|
|
||||||
sortDirection={sortDirection}
|
|
||||||
onSortPress={onSortPress}
|
|
||||||
>
|
|
||||||
<TableBody>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<InteractiveAlbumSearchRow
|
|
||||||
key={item.guid}
|
|
||||||
{...item}
|
|
||||||
longDateFormat={longDateFormat}
|
|
||||||
timeFormat={timeFormat}
|
|
||||||
onGrabPress={onGrabPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
InteractiveAlbumSearch.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
sortKey: PropTypes.string,
|
|
||||||
sortDirection: PropTypes.string,
|
|
||||||
longDateFormat: PropTypes.string.isRequired,
|
|
||||||
timeFormat: PropTypes.string.isRequired,
|
|
||||||
onSortPress: PropTypes.func.isRequired,
|
|
||||||
onGrabPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default InteractiveAlbumSearch;
|
|
|
@ -1,90 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import connectSection from 'Store/connectSection';
|
|
||||||
import { fetchReleases, setReleasesSort, grabRelease } from 'Store/Actions/releaseActions';
|
|
||||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
|
||||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
|
||||||
import InteractiveAlbumSearch from './InteractiveAlbumSearch';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createClientSideCollectionSelector(),
|
|
||||||
createUISettingsSelector(),
|
|
||||||
(releases, uiSettings) => {
|
|
||||||
return {
|
|
||||||
longDateFormat: uiSettings.longDateFormat,
|
|
||||||
timeFormat: uiSettings.timeFormat,
|
|
||||||
...releases
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchReleases,
|
|
||||||
setReleasesSort,
|
|
||||||
grabRelease
|
|
||||||
};
|
|
||||||
|
|
||||||
class InteractiveAlbumSearchConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const {
|
|
||||||
albumId,
|
|
||||||
isPopulated
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
// If search results are not yet isPopulated fetch them,
|
|
||||||
// otherwise re-show the existing props.
|
|
||||||
|
|
||||||
if (!isPopulated) {
|
|
||||||
this.props.fetchReleases({
|
|
||||||
albumId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSortPress = (sortKey, sortDirection) => {
|
|
||||||
this.props.setReleasesSort({ sortKey, sortDirection });
|
|
||||||
}
|
|
||||||
|
|
||||||
onGrabPress = (guid, indexerId) => {
|
|
||||||
this.props.grabRelease({ guid, indexerId });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<InteractiveAlbumSearch
|
|
||||||
{...this.props}
|
|
||||||
onSortPress={this.onSortPress}
|
|
||||||
onGrabPress={this.onGrabPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InteractiveAlbumSearchConnector.propTypes = {
|
|
||||||
albumId: PropTypes.number.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
fetchReleases: PropTypes.func.isRequired,
|
|
||||||
setReleasesSort: PropTypes.func.isRequired,
|
|
||||||
grabRelease: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connectSection(
|
|
||||||
createMapStateToProps,
|
|
||||||
mapDispatchToProps,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
{ section: 'releases' }
|
|
||||||
)(InteractiveAlbumSearchConnector);
|
|
31
frontend/src/Album/Search/InteractiveAlbumSearchModal.js
Normal file
31
frontend/src/Album/Search/InteractiveAlbumSearchModal.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import InteractiveAlbumSearchModalContentConnector from './InteractiveAlbumSearchModalContentConnector';
|
||||||
|
|
||||||
|
function InteractiveAlbumSearchModal(props) {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
>
|
||||||
|
<InteractiveAlbumSearchModalContentConnector
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
InteractiveAlbumSearchModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InteractiveAlbumSearchModal;
|
169
frontend/src/Album/Search/InteractiveAlbumSearchModalContent.js
Normal file
169
frontend/src/Album/Search/InteractiveAlbumSearchModalContent.js
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { icons, sortDirections } from 'Helpers/Props';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import Table from 'Components/Table/Table';
|
||||||
|
import TableBody from 'Components/Table/TableBody';
|
||||||
|
import InteractiveAlbumSearchRow from './InteractiveAlbumSearchRow';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'protocol',
|
||||||
|
label: 'Source',
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'age',
|
||||||
|
label: 'Age',
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
label: 'Title',
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'indexer',
|
||||||
|
label: 'Indexer',
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'size',
|
||||||
|
label: 'Size',
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'peers',
|
||||||
|
label: 'Peers',
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'qualityWeight',
|
||||||
|
label: 'Quality',
|
||||||
|
isSortable: true,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rejections',
|
||||||
|
label: React.createElement(Icon, { name: icons.DANGER }),
|
||||||
|
isSortable: true,
|
||||||
|
fixedSortDirection: sortDirections.ASCENDING,
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'releaseWeight',
|
||||||
|
label: React.createElement(Icon, { name: icons.DOWNLOAD }),
|
||||||
|
isSortable: true,
|
||||||
|
fixedSortDirection: sortDirections.ASCENDING,
|
||||||
|
isVisible: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
class InteractiveAlbumSearchModalContent extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
isFetching,
|
||||||
|
isPopulated,
|
||||||
|
error,
|
||||||
|
items,
|
||||||
|
sortKey,
|
||||||
|
sortDirection,
|
||||||
|
longDateFormat,
|
||||||
|
timeFormat,
|
||||||
|
onSortPress,
|
||||||
|
onGrabPress,
|
||||||
|
onModalClose
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const hasItems = !!items.length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
Interactive Album Search
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
{
|
||||||
|
isFetching &&
|
||||||
|
<LoadingIndicator />
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
!isFetching && !!error &&
|
||||||
|
<div>Unable to load releases.</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isPopulated && !hasItems && !error &&
|
||||||
|
<div>No results.</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isPopulated && hasItems && !error &&
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
sortKey={sortKey}
|
||||||
|
sortDirection={sortDirection}
|
||||||
|
onSortPress={onSortPress}
|
||||||
|
>
|
||||||
|
<TableBody>
|
||||||
|
{
|
||||||
|
items.map((item) => {
|
||||||
|
return (
|
||||||
|
<InteractiveAlbumSearchRow
|
||||||
|
key={item.guid}
|
||||||
|
{...item}
|
||||||
|
longDateFormat={longDateFormat}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
onGrabPress={onGrabPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
}
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={onModalClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InteractiveAlbumSearchModalContent.propTypes = {
|
||||||
|
isFetching: PropTypes.bool.isRequired,
|
||||||
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
|
error: PropTypes.object,
|
||||||
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
longDateFormat: PropTypes.string.isRequired,
|
||||||
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
sortKey: PropTypes.string,
|
||||||
|
sortDirection: PropTypes.string,
|
||||||
|
onSortPress: PropTypes.func.isRequired,
|
||||||
|
onGrabPress: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InteractiveAlbumSearchModalContent;
|
|
@ -0,0 +1,109 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import connectSection from 'Store/connectSection';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { fetchReleases, clearReleases, cancelFetchReleases, setReleasesSort, grabRelease } from 'Store/Actions/releaseActions';
|
||||||
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
|
import InteractiveAlbumSearchModalContent from './InteractiveAlbumSearchModalContent';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
createClientSideCollectionSelector(),
|
||||||
|
createUISettingsSelector(),
|
||||||
|
(releases, uiSettings) => {
|
||||||
|
return {
|
||||||
|
longDateFormat: uiSettings.longDateFormat,
|
||||||
|
timeFormat: uiSettings.timeFormat,
|
||||||
|
...releases
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
|
return {
|
||||||
|
dispatchFetchReleases({ albumId }) {
|
||||||
|
dispatch(fetchReleases({ albumId }));
|
||||||
|
},
|
||||||
|
|
||||||
|
dispatchCancelFetchReleases() {
|
||||||
|
dispatch(cancelFetchReleases());
|
||||||
|
},
|
||||||
|
|
||||||
|
dispatchClearReleases() {
|
||||||
|
dispatch(clearReleases());
|
||||||
|
},
|
||||||
|
|
||||||
|
dispatchSetReleasesSort({ sortKey, sortDirection }) {
|
||||||
|
dispatch(setReleasesSort({ sortKey, sortDirection }));
|
||||||
|
},
|
||||||
|
|
||||||
|
dispatchGrabRelease({ guid, indexerId }) {
|
||||||
|
dispatch(grabRelease({ guid, indexerId }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class InteractiveAlbumSearchModalContentConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const {
|
||||||
|
albumId
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
this.props.dispatchFetchReleases({
|
||||||
|
albumId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.dispatchCancelFetchReleases();
|
||||||
|
this.props.dispatchClearReleases();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onSortPress = (sortKey, sortDirection) => {
|
||||||
|
this.props.dispatchSetReleasesSort({ sortKey, sortDirection });
|
||||||
|
}
|
||||||
|
|
||||||
|
onGrabPress = (guid, indexerId) => {
|
||||||
|
this.props.dispatchGrabRelease({ guid, indexerId });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<InteractiveAlbumSearchModalContent
|
||||||
|
{...this.props}
|
||||||
|
onSortPress={this.onSortPress}
|
||||||
|
onGrabPress={this.onGrabPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InteractiveAlbumSearchModalContentConnector.propTypes = {
|
||||||
|
albumId: PropTypes.number,
|
||||||
|
dispatchFetchReleases: PropTypes.func.isRequired,
|
||||||
|
dispatchClearReleases: PropTypes.func.isRequired,
|
||||||
|
dispatchCancelFetchReleases: PropTypes.func.isRequired,
|
||||||
|
dispatchSetReleasesSort: PropTypes.func.isRequired,
|
||||||
|
dispatchGrabRelease: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connectSection(
|
||||||
|
createMapStateToProps,
|
||||||
|
createMapDispatchToProps,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ section: 'releases' }
|
||||||
|
)(InteractiveAlbumSearchModalContentConnector);
|
|
@ -1,81 +0,0 @@
|
||||||
import moment from 'moment';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import isInNextWeek from 'Utilities/Date/isInNextWeek';
|
|
||||||
import isToday from 'Utilities/Date/isToday';
|
|
||||||
import isTomorrow from 'Utilities/Date/isTomorrow';
|
|
||||||
import { kinds, sizes } from 'Helpers/Props';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
|
|
||||||
function AlbumReleasing(props) {
|
|
||||||
const {
|
|
||||||
releaseDate,
|
|
||||||
albumLabel,
|
|
||||||
shortDateFormat,
|
|
||||||
showRelativeDates
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const networkLabel = (
|
|
||||||
<Label
|
|
||||||
kind={kinds.INFO}
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
>
|
|
||||||
{albumLabel}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!releaseDate) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
TBA on {networkLabel}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!showRelativeDates) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{moment(releaseDate).format(shortDateFormat)} on {networkLabel}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isToday(releaseDate)) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
Today on {networkLabel}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTomorrow(releaseDate)) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
Tomorrow on {networkLabel}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInNextWeek(releaseDate)) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{moment(releaseDate).format('dddd')} on {networkLabel}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{moment(releaseDate).format(shortDateFormat)} on {networkLabel}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumReleasing.propTypes = {
|
|
||||||
releaseDate: PropTypes.string.isRequired,
|
|
||||||
albumLabel: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
|
||||||
showRelativeDates: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlbumReleasing;
|
|
|
@ -1,19 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
|
||||||
import AlbumReleasing from './AlbumReleasing';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createUISettingsSelector(),
|
|
||||||
(uiSettings) => {
|
|
||||||
return _.pick(uiSettings, [
|
|
||||||
'shortDateFormat',
|
|
||||||
'showRelativeDates'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(AlbumReleasing);
|
|
|
@ -1,48 +0,0 @@
|
||||||
.infoTitle {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overview,
|
|
||||||
.files {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filesHeader {
|
|
||||||
display: flex;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filesHeader {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
border-bottom: 1px solid $borderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileRow {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.path {
|
|
||||||
@add-mixin truncate;
|
|
||||||
|
|
||||||
flex: 1 0 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.size,
|
|
||||||
.quality {
|
|
||||||
flex: 0 0 125px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
flex: 0 0 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointMedium) {
|
|
||||||
.size,
|
|
||||||
.quality {
|
|
||||||
flex: 0 0 80px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { kinds, sizes } from 'Helpers/Props';
|
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
|
|
||||||
import Table from 'Components/Table/Table';
|
|
||||||
import TableBody from 'Components/Table/TableBody';
|
|
||||||
import AlbumReleasingConnector from './AlbumReleasingConnector';
|
|
||||||
import TrackDetailRow from './TrackDetailRow';
|
|
||||||
import styles from './AlbumSummary.css';
|
|
||||||
|
|
||||||
class AlbumSummary extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isRemoveTrackFileModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onRemoveTrackFilePress = () => {
|
|
||||||
this.setState({ isRemoveTrackFileModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onConfirmRemoveTrackFile = () => {
|
|
||||||
this.props.onDeleteTrackFile();
|
|
||||||
this.setState({ isRemoveTrackFileModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onRemoveTrackFileModalClose = () => {
|
|
||||||
this.setState({ isRemoveTrackFileModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
qualityProfileId,
|
|
||||||
overview,
|
|
||||||
releaseDate,
|
|
||||||
albumLabel,
|
|
||||||
path,
|
|
||||||
items,
|
|
||||||
size,
|
|
||||||
quality,
|
|
||||||
qualityCutoffNotMet,
|
|
||||||
columns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const hasOverview = !!overview;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<span className={styles.infoTitle}>Releases</span>
|
|
||||||
|
|
||||||
<AlbumReleasingConnector
|
|
||||||
releaseDate={releaseDate}
|
|
||||||
albumLabel={albumLabel}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<span className={styles.infoTitle}>Quality Profile</span>
|
|
||||||
|
|
||||||
<Label
|
|
||||||
kind={kinds.PRIMARY}
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
>
|
|
||||||
<QualityProfileNameConnector
|
|
||||||
qualityProfileId={qualityProfileId}
|
|
||||||
/>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.overview}>
|
|
||||||
{
|
|
||||||
hasOverview ?
|
|
||||||
overview :
|
|
||||||
'No album overview.'
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
<div className={styles.albums}>
|
|
||||||
{
|
|
||||||
items.length ?
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
>
|
|
||||||
<TableBody>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<TrackDetailRow
|
|
||||||
key={item.id}
|
|
||||||
columns={columns}
|
|
||||||
{...item}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableBody>
|
|
||||||
</Table> :
|
|
||||||
|
|
||||||
<div className={styles.noAlbums}>
|
|
||||||
No tracks in this group
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={this.state.isRemoveTrackFileModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title="Delete Track File"
|
|
||||||
message={`Are you sure you want to delete '${path}'?`}
|
|
||||||
confirmLabel="Delete"
|
|
||||||
onConfirm={this.onConfirmRemoveTrackFile}
|
|
||||||
onCancel={this.onRemoveTrackFileModalClose}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumSummary.propTypes = {
|
|
||||||
qualityProfileId: PropTypes.number.isRequired,
|
|
||||||
overview: PropTypes.string,
|
|
||||||
albumLabel: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
releaseDate: PropTypes.string.isRequired,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
path: PropTypes.string,
|
|
||||||
size: PropTypes.number,
|
|
||||||
quality: PropTypes.object,
|
|
||||||
qualityCutoffNotMet: PropTypes.bool,
|
|
||||||
onDeleteTrackFile: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AlbumSummary;
|
|
|
@ -1,46 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { deleteTrackFile } from 'Store/Actions/trackFileActions';
|
|
||||||
import createAlbumSelector from 'Store/Selectors/createAlbumSelector';
|
|
||||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
|
||||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
|
||||||
import AlbumSummary from './AlbumSummary';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.tracks,
|
|
||||||
createAlbumSelector(),
|
|
||||||
createCommandsSelector(),
|
|
||||||
createDimensionsSelector(),
|
|
||||||
createArtistSelector(),
|
|
||||||
(tracks, album, commands, dimensions, artist) => {
|
|
||||||
const filteredItems = _.filter(tracks.items, { albumId: album.id });
|
|
||||||
const mediumSortedItems = _.orderBy(filteredItems, 'absoluteTrackNumber');
|
|
||||||
const items = _.orderBy(mediumSortedItems, 'mediumNumber');
|
|
||||||
|
|
||||||
return {
|
|
||||||
network: album.label,
|
|
||||||
qualityProfileId: artist.qualityProfileId,
|
|
||||||
releaseDate: album.releaseDate,
|
|
||||||
overview: album.overview,
|
|
||||||
items,
|
|
||||||
columns: tracks.columns
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
|
||||||
return {
|
|
||||||
onDeleteTrackFile() {
|
|
||||||
dispatch(deleteTrackFile({
|
|
||||||
id: props.trackFileId,
|
|
||||||
albumEntity: props.albumEntity
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(AlbumSummary);
|
|
|
@ -1,31 +0,0 @@
|
||||||
.title {
|
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.monitored {
|
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trackNumber {
|
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.audio {
|
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language,
|
|
||||||
.video,
|
|
||||||
.status {
|
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import TableRow from 'Components/Table/TableRow';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|
||||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
|
||||||
import MediaInfoConnector from 'TrackFile/MediaInfoConnector';
|
|
||||||
import * as mediaInfoTypes from 'TrackFile/mediaInfoTypes';
|
|
||||||
import EpisodeStatusConnector from 'Album/EpisodeStatusConnector';
|
|
||||||
|
|
||||||
import styles from './TrackDetailRow.css';
|
|
||||||
|
|
||||||
class TrackDetailRow extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
title,
|
|
||||||
mediumNumber,
|
|
||||||
absoluteTrackNumber,
|
|
||||||
duration,
|
|
||||||
columns,
|
|
||||||
trackFileId
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow>
|
|
||||||
{
|
|
||||||
columns.map((column) => {
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
isVisible
|
|
||||||
} = column;
|
|
||||||
|
|
||||||
if (!isVisible) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'medium') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.trackNumber}
|
|
||||||
>
|
|
||||||
{mediumNumber}
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'absoluteTrackNumber') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.trackNumber}
|
|
||||||
>
|
|
||||||
{absoluteTrackNumber}
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'title') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.title}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'duration') {
|
|
||||||
return (
|
|
||||||
<TableRowCell key={name}>
|
|
||||||
{
|
|
||||||
formatTimeSpan(duration)
|
|
||||||
}
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'audioInfo') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.audio}
|
|
||||||
>
|
|
||||||
<MediaInfoConnector
|
|
||||||
type={mediaInfoTypes.AUDIO}
|
|
||||||
trackFileId={trackFileId}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'status') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.status}
|
|
||||||
>
|
|
||||||
<EpisodeStatusConnector
|
|
||||||
albumId={id}
|
|
||||||
trackFileId={trackFileId}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TrackDetailRow.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
duration: PropTypes.number.isRequired,
|
|
||||||
trackFileId: PropTypes.number.isRequired,
|
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
mediumNumber: PropTypes.number.isRequired,
|
|
||||||
absoluteTrackNumber: PropTypes.number.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TrackDetailRow;
|
|
|
@ -8,7 +8,7 @@ import Label from 'Components/Label';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||||
import AlbumSearchCellConnector from 'Album/AlbumSearchCellConnector';
|
import AlbumSearchCellConnector from 'Album/AlbumSearchCellConnector';
|
||||||
import AlbumTitleDetailLink from 'Album/AlbumTitleDetailLink';
|
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
||||||
import styles from './AlbumRow.css';
|
import styles from './AlbumRow.css';
|
||||||
|
|
||||||
function getTrackCountKind(monitored, trackFileCount, trackCount) {
|
function getTrackCountKind(monitored, trackFileCount, trackCount) {
|
||||||
|
@ -121,7 +121,7 @@ class AlbumRow extends Component {
|
||||||
key={name}
|
key={name}
|
||||||
className={styles.title}
|
className={styles.title}
|
||||||
>
|
>
|
||||||
<AlbumTitleDetailLink
|
<AlbumTitleLink
|
||||||
title={title}
|
title={title}
|
||||||
foreignAlbumId={foreignAlbumId}
|
foreignAlbumId={foreignAlbumId}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import VirtualTableRow from 'Components/Table/VirtualTableRow';
|
||||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||||
import AlbumTitleDetailLink from 'Album/AlbumTitleDetailLink';
|
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
||||||
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||||
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||||
import ArtistStatusCell from './ArtistStatusCell';
|
import ArtistStatusCell from './ArtistStatusCell';
|
||||||
|
@ -181,7 +181,7 @@ class ArtistIndexRow extends Component {
|
||||||
key={name}
|
key={name}
|
||||||
className={styles[name]}
|
className={styles[name]}
|
||||||
>
|
>
|
||||||
<AlbumTitleDetailLink
|
<AlbumTitleLink
|
||||||
title={nextAlbum.title}
|
title={nextAlbum.title}
|
||||||
foreignAlbumId={nextAlbum.foreignAlbumId}
|
foreignAlbumId={nextAlbum.foreignAlbumId}
|
||||||
/>
|
/>
|
||||||
|
@ -205,7 +205,7 @@ class ArtistIndexRow extends Component {
|
||||||
key={name}
|
key={name}
|
||||||
className={styles[name]}
|
className={styles[name]}
|
||||||
>
|
>
|
||||||
<AlbumTitleDetailLink
|
<AlbumTitleLink
|
||||||
title={lastAlbum.title}
|
title={lastAlbum.title}
|
||||||
foreignAlbumId={lastAlbum.foreignAlbumId}
|
foreignAlbumId={lastAlbum.foreignAlbumId}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,8 +7,6 @@ import { icons } from 'Helpers/Props';
|
||||||
import getStatusStyle from 'Calendar/getStatusStyle';
|
import getStatusStyle from 'Calendar/getStatusStyle';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import albumEntities from 'Album/albumEntities';
|
|
||||||
import AlbumDetailsModal from 'Album/AlbumDetailsModal';
|
|
||||||
import CalendarEventQueueDetails from 'Calendar/Events/CalendarEventQueueDetails';
|
import CalendarEventQueueDetails from 'Calendar/Events/CalendarEventQueueDetails';
|
||||||
import styles from './AgendaEvent.css';
|
import styles from './AgendaEvent.css';
|
||||||
|
|
||||||
|
@ -109,16 +107,6 @@ class AgendaEvent extends Component {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<AlbumDetailsModal
|
|
||||||
isOpen={this.state.isDetailsModalOpen}
|
|
||||||
albumId={id}
|
|
||||||
albumEntity={albumEntities.CALENDAR}
|
|
||||||
artistId={artist.id}
|
|
||||||
albumTitle={title}
|
|
||||||
showOpenArtistButton={true}
|
|
||||||
onModalClose={this.onDetailsModalClose}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import getStatusStyle from 'Calendar/getStatusStyle';
|
||||||
import albumEntities from 'Album/albumEntities';
|
import albumEntities from 'Album/albumEntities';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import AlbumDetailsModal from 'Album/AlbumDetailsModal';
|
|
||||||
import CalendarEventQueueDetails from './CalendarEventQueueDetails';
|
import CalendarEventQueueDetails from './CalendarEventQueueDetails';
|
||||||
import styles from './CalendarEvent.css';
|
import styles from './CalendarEvent.css';
|
||||||
|
|
||||||
|
@ -107,16 +106,6 @@ class CalendarEvent extends Component {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<AlbumDetailsModal
|
|
||||||
isOpen={this.state.isDetailsModalOpen}
|
|
||||||
albumId={id}
|
|
||||||
albumEntity={albumEntities.CALENDAR}
|
|
||||||
artistId={artist.id}
|
|
||||||
albumTitle={title}
|
|
||||||
showOpenArtistButton={true}
|
|
||||||
onModalClose={this.onDetailsModalClose}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ function CutoffUnmetRow(props) {
|
||||||
trackFileId,
|
trackFileId,
|
||||||
artist,
|
artist,
|
||||||
releaseDate,
|
releaseDate,
|
||||||
|
foreignAlbumId,
|
||||||
albumType,
|
albumType,
|
||||||
title,
|
title,
|
||||||
isSelected,
|
isSelected,
|
||||||
|
@ -59,12 +60,8 @@ function CutoffUnmetRow(props) {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<AlbumTitleLink
|
<AlbumTitleLink
|
||||||
albumId={id}
|
foreignAlbumId={foreignAlbumId}
|
||||||
artistId={artist.id}
|
title={title}
|
||||||
albumEntity={albumEntities.WANTED_CUTOFF_UNMET}
|
|
||||||
albumTitle={title}
|
|
||||||
showOpenArtistButton={true}
|
|
||||||
showOpenAlbumButton={true}
|
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
|
@ -140,6 +137,7 @@ CutoffUnmetRow.propTypes = {
|
||||||
trackFileId: PropTypes.number,
|
trackFileId: PropTypes.number,
|
||||||
artist: PropTypes.object.isRequired,
|
artist: PropTypes.object.isRequired,
|
||||||
releaseDate: PropTypes.string.isRequired,
|
releaseDate: PropTypes.string.isRequired,
|
||||||
|
foreignAlbumId: PropTypes.string.isRequired,
|
||||||
albumType: PropTypes.string.isRequired,
|
albumType: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
isSelected: PropTypes.bool,
|
isSelected: PropTypes.bool,
|
||||||
|
|
|
@ -18,6 +18,7 @@ function MissingRow(props) {
|
||||||
artist,
|
artist,
|
||||||
releaseDate,
|
releaseDate,
|
||||||
albumType,
|
albumType,
|
||||||
|
foreignAlbumId,
|
||||||
title,
|
title,
|
||||||
isSelected,
|
isSelected,
|
||||||
columns,
|
columns,
|
||||||
|
@ -76,12 +77,8 @@ function MissingRow(props) {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<AlbumTitleLink
|
<AlbumTitleLink
|
||||||
albumId={id}
|
foreignAlbumId={foreignAlbumId}
|
||||||
artistId={artist.id}
|
title={title}
|
||||||
albumEntity={albumEntities.WANTED_MISSING}
|
|
||||||
albumTitle={title}
|
|
||||||
showOpenArtistButton={true}
|
|
||||||
showOpenAlbumButton={true}
|
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
|
@ -144,6 +141,7 @@ MissingRow.propTypes = {
|
||||||
// trackFileId: PropTypes.number,
|
// trackFileId: PropTypes.number,
|
||||||
artist: PropTypes.object.isRequired,
|
artist: PropTypes.object.isRequired,
|
||||||
releaseDate: PropTypes.string.isRequired,
|
releaseDate: PropTypes.string.isRequired,
|
||||||
|
foreignAlbumId: PropTypes.string.isRequired,
|
||||||
albumType: PropTypes.string.isRequired,
|
albumType: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
isSelected: PropTypes.bool,
|
isSelected: PropTypes.bool,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue