mirror of https://github.com/Radarr/Radarr
New: Rework Movie Details view
This commit is contained in:
parent
5acd41a7ea
commit
155b8f774b
|
@ -1,8 +1,8 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Slider from 'react-slick';
|
|
||||||
import TextTruncate from 'react-text-truncate';
|
import TextTruncate from 'react-text-truncate';
|
||||||
import EditCollectionModalConnector from 'Collection/Edit/EditCollectionModalConnector';
|
import EditCollectionModalConnector from 'Collection/Edit/EditCollectionModalConnector';
|
||||||
|
import Carousel from 'Components/Carousel';
|
||||||
import CheckInput from 'Components/Form/CheckInput';
|
import CheckInput from 'Components/Form/CheckInput';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
|
@ -16,9 +16,6 @@ import translate from 'Utilities/String/translate';
|
||||||
import CollectionMovieConnector from './CollectionMovieConnector';
|
import CollectionMovieConnector from './CollectionMovieConnector';
|
||||||
import styles from './CollectionOverview.css';
|
import styles from './CollectionOverview.css';
|
||||||
|
|
||||||
import 'slick-carousel/slick/slick.css';
|
|
||||||
import 'slick-carousel/slick/slick-theme.css';
|
|
||||||
|
|
||||||
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
|
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
|
||||||
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
|
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
|
||||||
const defaultFontSize = parseInt(fonts.defaultFontSize);
|
const defaultFontSize = parseInt(fonts.defaultFontSize);
|
||||||
|
@ -118,15 +115,6 @@ class CollectionOverview extends Component {
|
||||||
const contentHeight = getContentHeight(rowHeight, isSmallScreen);
|
const contentHeight = getContentHeight(rowHeight, isSmallScreen);
|
||||||
const overviewHeight = contentHeight - titleRowHeight - posterHeight;
|
const overviewHeight = contentHeight - titleRowHeight - posterHeight;
|
||||||
|
|
||||||
const sliderSettings = {
|
|
||||||
arrows: false,
|
|
||||||
dots: false,
|
|
||||||
infinite: false,
|
|
||||||
slidesToShow: 1,
|
|
||||||
slidesToScroll: 1,
|
|
||||||
variableWidth: true
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
|
@ -262,7 +250,7 @@ class CollectionOverview extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
<div className={styles.sliderContainer}>
|
<div className={styles.sliderContainer}>
|
||||||
<Slider ref={this.setSliderRef} {...sliderSettings}>
|
<Carousel setRef={this.setSliderRef}>
|
||||||
{movies.map((movie) => (
|
{movies.map((movie) => (
|
||||||
<div className={styles.movie} key={movie.tmdbId}>
|
<div className={styles.movie} key={movie.tmdbId}>
|
||||||
<CollectionMovieConnector
|
<CollectionMovieConnector
|
||||||
|
@ -275,7 +263,7 @@ class CollectionOverview extends Component {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Slider>
|
</Carousel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Slider from 'react-slick';
|
||||||
|
import styles from './Alert.css';
|
||||||
|
|
||||||
|
import 'slick-carousel/slick/slick.css';
|
||||||
|
import 'slick-carousel/slick/slick-theme.css';
|
||||||
|
|
||||||
|
function Carousel({ className, setRef, children, ...otherProps }) {
|
||||||
|
|
||||||
|
const sliderSettings = {
|
||||||
|
arrows: false,
|
||||||
|
dots: false,
|
||||||
|
infinite: false,
|
||||||
|
slidesToShow: 1,
|
||||||
|
slidesToScroll: 1,
|
||||||
|
variableWidth: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slider ref={setRef} {...sliderSettings}>
|
||||||
|
{children}
|
||||||
|
</Slider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Carousel.propTypes = {
|
||||||
|
className: PropTypes.string.isRequired,
|
||||||
|
setRef: PropTypes.func.isRequired,
|
||||||
|
children: PropTypes.node.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
Carousel.defaultProps = {
|
||||||
|
className: styles.alert
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Carousel;
|
|
@ -2,12 +2,15 @@ import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||||
|
import PageMenuButton from 'Components/Menu/PageMenuButton';
|
||||||
import Table from 'Components/Table/Table';
|
import Table from 'Components/Table/Table';
|
||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import { icons, sortDirections } from 'Helpers/Props';
|
import { align, icons, sortDirections } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
import InteractiveSearchFilterModalConnector from './InteractiveSearchFilterModalConnector';
|
||||||
import InteractiveSearchRowConnector from './InteractiveSearchRowConnector';
|
import InteractiveSearchRowConnector from './InteractiveSearchRowConnector';
|
||||||
import styles from './InteractiveSearchContent.css';
|
import styles from './InteractiveSearch.css';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
|
@ -22,20 +25,6 @@ const columns = [
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'releaseWeight',
|
|
||||||
label: React.createElement(Icon, { name: icons.DOWNLOAD }),
|
|
||||||
isSortable: true,
|
|
||||||
fixedSortDirection: sortDirections.ASCENDING,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'rejections',
|
|
||||||
label: React.createElement(Icon, { name: icons.DANGER }),
|
|
||||||
isSortable: true,
|
|
||||||
fixedSortDirection: sortDirections.ASCENDING,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: 'title',
|
||||||
label: translate('Title'),
|
label: translate('Title'),
|
||||||
|
@ -99,10 +88,24 @@ const columns = [
|
||||||
label: React.createElement(Icon, { name: icons.FLAG }),
|
label: React.createElement(Icon, { name: icons.FLAG }),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: 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 InteractiveSearchContent(props) {
|
function InteractiveSearch(props) {
|
||||||
const {
|
const {
|
||||||
searchPayload,
|
searchPayload,
|
||||||
isFetching,
|
isFetching,
|
||||||
|
@ -110,44 +113,63 @@ function InteractiveSearchContent(props) {
|
||||||
error,
|
error,
|
||||||
totalReleasesCount,
|
totalReleasesCount,
|
||||||
items,
|
items,
|
||||||
|
selectedFilterKey,
|
||||||
|
filters,
|
||||||
|
customFilters,
|
||||||
sortKey,
|
sortKey,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
longDateFormat,
|
longDateFormat,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
onSortPress,
|
onSortPress,
|
||||||
|
onFilterSelect,
|
||||||
onGrabPress
|
onGrabPress
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<div className={styles.filterMenuContainer}>
|
||||||
|
<FilterMenu
|
||||||
|
alignMenu={align.RIGHT}
|
||||||
|
selectedFilterKey={selectedFilterKey}
|
||||||
|
filters={filters}
|
||||||
|
customFilters={customFilters}
|
||||||
|
buttonComponent={PageMenuButton}
|
||||||
|
filterModalConnectorComponent={InteractiveSearchFilterModalConnector}
|
||||||
|
filterModalConnectorComponentProps={'movies'}
|
||||||
|
onFilterSelect={onFilterSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
isFetching &&
|
isFetching ? <LoadingIndicator /> : null
|
||||||
<LoadingIndicator />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && !!error &&
|
!isFetching && error ?
|
||||||
<div>
|
<div>
|
||||||
{translate('UnableToLoadResultsIntSearch')}
|
{translate('UnableToLoadResultsIntSearch')}
|
||||||
</div>
|
</div> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && isPopulated && !totalReleasesCount &&
|
!isFetching && isPopulated && !totalReleasesCount ?
|
||||||
<div>
|
<div>
|
||||||
{translate('NoResultsFound')}
|
{translate('NoResultsFound')}
|
||||||
</div>
|
</div> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!!totalReleasesCount && isPopulated && !items.length &&
|
!!totalReleasesCount && isPopulated && !items.length ?
|
||||||
<div>
|
<div>
|
||||||
{translate('AllResultsHiddenFilter')}
|
{translate('AllResultsHiddenFilter')}
|
||||||
</div>
|
</div> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !!items.length &&
|
isPopulated && !!items.length ?
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
sortKey={sortKey}
|
sortKey={sortKey}
|
||||||
|
@ -170,32 +192,38 @@ function InteractiveSearchContent(props) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
totalReleasesCount !== items.length && !!items.length &&
|
totalReleasesCount !== items.length && !!items.length ?
|
||||||
<div className={styles.filteredMessage}>
|
<div className={styles.filteredMessage}>
|
||||||
{translate('SomeResultsHiddenFilter')}
|
{translate('SomeResultsHiddenFilter')}
|
||||||
</div>
|
</div> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
InteractiveSearchContent.propTypes = {
|
InteractiveSearch.propTypes = {
|
||||||
searchPayload: PropTypes.object.isRequired,
|
searchPayload: PropTypes.object.isRequired,
|
||||||
isFetching: PropTypes.bool.isRequired,
|
isFetching: PropTypes.bool.isRequired,
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
totalReleasesCount: PropTypes.number.isRequired,
|
totalReleasesCount: PropTypes.number.isRequired,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
|
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
sortKey: PropTypes.string,
|
sortKey: PropTypes.string,
|
||||||
sortDirection: PropTypes.string,
|
sortDirection: PropTypes.string,
|
||||||
longDateFormat: PropTypes.string.isRequired,
|
longDateFormat: PropTypes.string.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired,
|
timeFormat: PropTypes.string.isRequired,
|
||||||
onSortPress: PropTypes.func.isRequired,
|
onSortPress: PropTypes.func.isRequired,
|
||||||
|
onFilterSelect: PropTypes.func.isRequired,
|
||||||
onGrabPress: PropTypes.func.isRequired
|
onGrabPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InteractiveSearchContent;
|
export default InteractiveSearch;
|
|
@ -5,7 +5,7 @@ import { createSelector } from 'reselect';
|
||||||
import * as releaseActions from 'Store/Actions/releaseActions';
|
import * as releaseActions from 'Store/Actions/releaseActions';
|
||||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
import InteractiveSearchContent from './InteractiveSearchContent';
|
import InteractiveSearch from './InteractiveSearch';
|
||||||
|
|
||||||
function createMapStateToProps(appState) {
|
function createMapStateToProps(appState) {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
|
@ -48,7 +48,7 @@ function createMapDispatchToProps(dispatch, props) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class InteractiveSearchContentConnector extends Component {
|
class InteractiveSearchConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -79,18 +79,18 @@ class InteractiveSearchContentConnector extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<InteractiveSearchContent
|
<InteractiveSearch
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InteractiveSearchContentConnector.propTypes = {
|
InteractiveSearchConnector.propTypes = {
|
||||||
searchPayload: PropTypes.object.isRequired,
|
searchPayload: PropTypes.object.isRequired,
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
dispatchFetchReleases: PropTypes.func.isRequired,
|
dispatchFetchReleases: PropTypes.func.isRequired,
|
||||||
dispatchClearReleases: PropTypes.func.isRequired
|
dispatchClearReleases: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(InteractiveSearchContentConnector);
|
export default connect(createMapStateToProps, createMapDispatchToProps)(InteractiveSearchConnector);
|
|
@ -1,15 +1,20 @@
|
||||||
.cell {
|
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.protocol {
|
.protocol {
|
||||||
composes: cell;
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
.indexer {
|
.indexer {
|
||||||
composes: cell;
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 85px;
|
width: 85px;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +22,9 @@
|
||||||
.quality,
|
.quality,
|
||||||
.customFormat,
|
.customFormat,
|
||||||
.language {
|
.language {
|
||||||
composes: cell;
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.language {
|
.language {
|
||||||
|
@ -25,7 +32,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.customFormatScore {
|
.customFormatScore {
|
||||||
composes: cell;
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 55px;
|
width: 55px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -35,34 +42,26 @@
|
||||||
.rejected,
|
.rejected,
|
||||||
.indexerFlags,
|
.indexerFlags,
|
||||||
.download {
|
.download {
|
||||||
composes: cell;
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.age,
|
.age,
|
||||||
.size {
|
.size {
|
||||||
composes: cell;
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peers {
|
.peers {
|
||||||
composes: cell;
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 75px;
|
width: 75px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
|
||||||
composes: cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title div {
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history {
|
.history {
|
||||||
composes: cell;
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 75px;
|
width: 75px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,46 +145,6 @@ class InteractiveSearchRow extends Component {
|
||||||
{formatAge(age, ageHours, ageMinutes)}
|
{formatAge(age, ageHours, ageMinutes)}
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
|
||||||
<TableRowCell className={styles.download}>
|
|
||||||
<SpinnerIconButton
|
|
||||||
name={getDownloadIcon(isGrabbing, isGrabbed, grabError)}
|
|
||||||
kind={grabError ? kinds.DANGER : kinds.DEFAULT}
|
|
||||||
title={getDownloadTooltip(isGrabbing, isGrabbed, grabError)}
|
|
||||||
isDisabled={isGrabbed}
|
|
||||||
isSpinning={isGrabbing}
|
|
||||||
onPress={downloadAllowed ? this.onGrabPress : this.onConfirmGrabPress}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.rejected}>
|
|
||||||
{
|
|
||||||
!!rejections.length &&
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Icon
|
|
||||||
name={icons.DANGER}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
title={translate('ReleaseRejected')}
|
|
||||||
body={
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
rejections.map((rejection, index) => {
|
|
||||||
return (
|
|
||||||
<li key={index}>
|
|
||||||
{rejection}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.title}>
|
<TableRowCell className={styles.title}>
|
||||||
<Link
|
<Link
|
||||||
to={infoUrl}
|
to={infoUrl}
|
||||||
|
@ -297,6 +257,46 @@ class InteractiveSearchRow extends Component {
|
||||||
}
|
}
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell className={styles.rejected}>
|
||||||
|
{
|
||||||
|
!!rejections.length &&
|
||||||
|
<Popover
|
||||||
|
anchor={
|
||||||
|
<Icon
|
||||||
|
name={icons.DANGER}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
title={translate('ReleaseRejected')}
|
||||||
|
body={
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
rejections.map((rejection, index) => {
|
||||||
|
return (
|
||||||
|
<li key={index}>
|
||||||
|
{rejection}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
position={tooltipPositions.LEFT}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell className={styles.download}>
|
||||||
|
<SpinnerIconButton
|
||||||
|
name={getDownloadIcon(isGrabbing, isGrabbed, grabError)}
|
||||||
|
kind={grabError ? kinds.DANGER : kinds.DEFAULT}
|
||||||
|
title={getDownloadTooltip(isGrabbing, isGrabbed, grabError)}
|
||||||
|
isDisabled={isGrabbed}
|
||||||
|
isSpinning={isGrabbing}
|
||||||
|
onPress={downloadAllowed ? this.onGrabPress : this.onConfirmGrabPress}
|
||||||
|
/>
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={this.state.isConfirmGrabModalOpen}
|
isOpen={this.state.isConfirmGrabModalOpen}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import InteractiveSearchContentConnector from './InteractiveSearchContentConnector';
|
|
||||||
|
|
||||||
function InteractiveSearchTable(props) {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<InteractiveSearchContentConnector
|
|
||||||
searchPayload={props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
InteractiveSearchTable.propTypes = {
|
|
||||||
};
|
|
||||||
|
|
||||||
export default InteractiveSearchTable;
|
|
|
@ -69,7 +69,8 @@ class MovieCastPoster extends Component {
|
||||||
|
|
||||||
const elementStyle = {
|
const elementStyle = {
|
||||||
width: `${posterWidth}px`,
|
width: `${posterWidth}px`,
|
||||||
height: `${posterHeight}px`
|
height: `${posterHeight}px`,
|
||||||
|
borderRadius: '5px'
|
||||||
};
|
};
|
||||||
|
|
||||||
const contentStyle = {
|
const contentStyle = {
|
||||||
|
|
|
@ -69,7 +69,8 @@ class MovieCrewPoster extends Component {
|
||||||
|
|
||||||
const elementStyle = {
|
const elementStyle = {
|
||||||
width: `${posterWidth}px`,
|
width: `${posterWidth}px`,
|
||||||
height: `${posterHeight}px`
|
height: `${posterHeight}px`,
|
||||||
|
borderRadius: '5px'
|
||||||
};
|
};
|
||||||
|
|
||||||
const contentStyle = {
|
const contentStyle = {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
$hoverScale: 1.05;
|
$hoverScale: 1.05;
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
border-radius: 5px;
|
||||||
transition: all 200ms ease-in;
|
transition: all 200ms ease-in;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.movie {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Grid, WindowScroller } from 'react-virtualized';
|
import Carousel from 'Components/Carousel';
|
||||||
import Measure from 'Components/Measure';
|
|
||||||
import dimensions from 'Styles/Variables/dimensions';
|
import dimensions from 'Styles/Variables/dimensions';
|
||||||
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
||||||
import MovieCreditPosterConnector from './MovieCreditPosterConnector';
|
import MovieCreditPosterConnector from './MovieCreditPosterConnector';
|
||||||
|
@ -169,56 +168,36 @@ class MovieCreditPosters extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
items
|
items,
|
||||||
|
itemComponent
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
width,
|
posterWidth,
|
||||||
columnWidth,
|
posterHeight
|
||||||
columnCount,
|
|
||||||
rowHeight
|
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const rowCount = Math.ceil(items.length / columnCount);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Measure
|
|
||||||
whitelist={['width']}
|
|
||||||
onMeasure={this.onMeasure}
|
|
||||||
>
|
|
||||||
<WindowScroller
|
|
||||||
scrollElement={undefined}
|
|
||||||
>
|
|
||||||
{({ height, registerChild, onChildScroll, scrollTop }) => {
|
|
||||||
if (!height) {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
<div className={styles.sliderContainer}>
|
||||||
<div ref={registerChild}>
|
<Carousel setRef={this.setSliderRef}>
|
||||||
<Grid
|
{items.map((movie) => (
|
||||||
ref={this.setGridRef}
|
<div className={styles.movie} key={movie.tmdbId}>
|
||||||
className={styles.grid}
|
<MovieCreditPosterConnector
|
||||||
autoHeight={true}
|
key={movie.order}
|
||||||
height={height}
|
component={itemComponent}
|
||||||
columnCount={columnCount}
|
posterWidth={posterWidth}
|
||||||
columnWidth={columnWidth}
|
posterHeight={posterHeight}
|
||||||
rowCount={rowCount}
|
tmdbId={movie.personTmdbId}
|
||||||
rowHeight={rowHeight}
|
personName={movie.personName}
|
||||||
width={width}
|
job={movie.job}
|
||||||
onScroll={onChildScroll}
|
character={movie.character}
|
||||||
scrollTop={scrollTop}
|
images={movie.images}
|
||||||
overscanRowCount={2}
|
|
||||||
cellRenderer={this.cellRenderer}
|
|
||||||
scrollToAlignment={'start'}
|
|
||||||
isScrollingOptOut={true}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
))}
|
||||||
}
|
</Carousel>
|
||||||
}
|
</div>
|
||||||
</WindowScroller>
|
|
||||||
</Measure>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
.alternateTitle {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import styles from './MovieAlternateTitles.css';
|
|
||||||
|
|
||||||
function MovieAlternateTitles({ alternateTitles }) {
|
|
||||||
return (
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
alternateTitles.filter((x, i, a) => a.indexOf(x) === i).map((alternateTitle) => {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
key={alternateTitle}
|
|
||||||
className={styles.alternateTitle}
|
|
||||||
>
|
|
||||||
{alternateTitle}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieAlternateTitles.propTypes = {
|
|
||||||
alternateTitles: PropTypes.arrayOf(PropTypes.string).isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieAlternateTitles;
|
|
|
@ -5,7 +5,7 @@
|
||||||
.header {
|
.header {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 375px;
|
height: 425px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.errorMessage {
|
.errorMessage {
|
||||||
|
@ -39,10 +39,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster {
|
.poster {
|
||||||
|
z-index: 2;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-right: 35px;
|
margin-right: 35px;
|
||||||
width: 217px;
|
width: 250px;
|
||||||
height: 319px;
|
height: 368px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
|
|
||||||
import TextTruncate from 'react-text-truncate';
|
import TextTruncate from 'react-text-truncate';
|
||||||
|
import FieldSet from 'Components/FieldSet';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import ImdbRating from 'Components/ImdbRating';
|
import ImdbRating from 'Components/ImdbRating';
|
||||||
import InfoLabel from 'Components/InfoLabel';
|
import InfoLabel from 'Components/InfoLabel';
|
||||||
|
@ -22,12 +22,11 @@ import Popover from 'Components/Tooltip/Popover';
|
||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||||
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
|
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
|
||||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||||
import InteractiveSearchFilterMenuConnector from 'InteractiveSearch/InteractiveSearchFilterMenuConnector';
|
|
||||||
import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable';
|
|
||||||
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
|
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
|
||||||
import EditMovieModalConnector from 'Movie/Edit/EditMovieModalConnector';
|
import EditMovieModalConnector from 'Movie/Edit/EditMovieModalConnector';
|
||||||
import MovieHistoryTable from 'Movie/History/MovieHistoryTable';
|
import MovieHistoryTable from 'Movie/History/MovieHistoryTable';
|
||||||
import MoviePoster from 'Movie/MoviePoster';
|
import MoviePoster from 'Movie/MoviePoster';
|
||||||
|
import MovieInteractiveSearchModalConnector from 'Movie/Search/MovieInteractiveSearchModalConnector';
|
||||||
import MovieFileEditorTable from 'MovieFile/Editor/MovieFileEditorTable';
|
import MovieFileEditorTable from 'MovieFile/Editor/MovieFileEditorTable';
|
||||||
import ExtraFileTable from 'MovieFile/Extras/ExtraFileTable';
|
import ExtraFileTable from 'MovieFile/Extras/ExtraFileTable';
|
||||||
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
|
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
|
||||||
|
@ -81,10 +80,10 @@ class MovieDetails extends Component {
|
||||||
isEditMovieModalOpen: false,
|
isEditMovieModalOpen: false,
|
||||||
isDeleteMovieModalOpen: false,
|
isDeleteMovieModalOpen: false,
|
||||||
isInteractiveImportModalOpen: false,
|
isInteractiveImportModalOpen: false,
|
||||||
|
isInteractiveSearchModalOpen: false,
|
||||||
allExpanded: false,
|
allExpanded: false,
|
||||||
allCollapsed: false,
|
allCollapsed: false,
|
||||||
expandedState: {},
|
expandedState: {},
|
||||||
selectedTabIndex: 0,
|
|
||||||
overviewHeight: 0,
|
overviewHeight: 0,
|
||||||
titleWidth: 0
|
titleWidth: 0
|
||||||
};
|
};
|
||||||
|
@ -137,6 +136,14 @@ class MovieDetails extends Component {
|
||||||
this.setState({ isEditMovieModalOpen: false });
|
this.setState({ isEditMovieModalOpen: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onInteractiveSearchPress = () => {
|
||||||
|
this.setState({ isInteractiveSearchModalOpen: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
onInteractiveSearchModalClose = () => {
|
||||||
|
this.setState({ isInteractiveSearchModalOpen: false });
|
||||||
|
};
|
||||||
|
|
||||||
onDeleteMoviePress = () => {
|
onDeleteMoviePress = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isEditMovieModalOpen: false,
|
isEditMovieModalOpen: false,
|
||||||
|
@ -298,9 +305,9 @@ class MovieDetails extends Component {
|
||||||
isEditMovieModalOpen,
|
isEditMovieModalOpen,
|
||||||
isDeleteMovieModalOpen,
|
isDeleteMovieModalOpen,
|
||||||
isInteractiveImportModalOpen,
|
isInteractiveImportModalOpen,
|
||||||
|
isInteractiveSearchModalOpen,
|
||||||
overviewHeight,
|
overviewHeight,
|
||||||
titleWidth,
|
titleWidth
|
||||||
selectedTabIndex
|
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const marqueeWidth = isSmallScreen ? titleWidth : (titleWidth - 150);
|
const marqueeWidth = isSmallScreen ? titleWidth : (titleWidth - 150);
|
||||||
|
@ -326,6 +333,14 @@ class MovieDetails extends Component {
|
||||||
onPress={onSearchPress}
|
onPress={onSearchPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<PageToolbarButton
|
||||||
|
label={translate('InteractiveSearch')}
|
||||||
|
iconName={icons.INTERACTIVE}
|
||||||
|
isSpinning={isSearching}
|
||||||
|
title={undefined}
|
||||||
|
onPress={this.onInteractiveSearchPress}
|
||||||
|
/>
|
||||||
|
|
||||||
<PageToolbarSeparator />
|
<PageToolbarSeparator />
|
||||||
|
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
|
@ -651,101 +666,39 @@ class MovieDetails extends Component {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<Tabs selectedIndex={this.state.tabIndex} onSelect={this.onTabSelect}>
|
<FieldSet legend={translate('History')}>
|
||||||
<TabList
|
|
||||||
className={styles.tabList}
|
|
||||||
>
|
|
||||||
<Tab
|
|
||||||
className={styles.tab}
|
|
||||||
selectedClassName={styles.selectedTab}
|
|
||||||
>
|
|
||||||
{translate('History')}
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
className={styles.tab}
|
|
||||||
selectedClassName={styles.selectedTab}
|
|
||||||
>
|
|
||||||
{translate('Search')}
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
className={styles.tab}
|
|
||||||
selectedClassName={styles.selectedTab}
|
|
||||||
>
|
|
||||||
{translate('Files')}
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
className={styles.tab}
|
|
||||||
selectedClassName={styles.selectedTab}
|
|
||||||
>
|
|
||||||
{translate('Titles')}
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
className={styles.tab}
|
|
||||||
selectedClassName={styles.selectedTab}
|
|
||||||
>
|
|
||||||
{translate('Cast')}
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
|
||||||
className={styles.tab}
|
|
||||||
selectedClassName={styles.selectedTab}
|
|
||||||
>
|
|
||||||
{translate('Crew')}
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
{
|
|
||||||
selectedTabIndex === 1 &&
|
|
||||||
<div className={styles.filterIcon}>
|
|
||||||
<InteractiveSearchFilterMenuConnector />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
</TabList>
|
|
||||||
|
|
||||||
<TabPanel>
|
|
||||||
<MovieHistoryTable
|
<MovieHistoryTable
|
||||||
movieId={id}
|
movieId={id}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</FieldSet>
|
||||||
|
|
||||||
<TabPanel>
|
<FieldSet legend={translate('Files')}>
|
||||||
<InteractiveSearchTable
|
|
||||||
movieId={id}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
<TabPanel>
|
|
||||||
<MovieFileEditorTable
|
<MovieFileEditorTable
|
||||||
movieId={id}
|
movieId={id}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ExtraFileTable
|
<ExtraFileTable
|
||||||
movieId={id}
|
movieId={id}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</FieldSet>
|
||||||
|
|
||||||
<TabPanel>
|
<FieldSet legend={translate('Cast')}>
|
||||||
<MovieTitlesTable
|
|
||||||
movieId={id}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
<TabPanel>
|
|
||||||
<MovieCastPostersConnector
|
<MovieCastPostersConnector
|
||||||
isSmallScreen={isSmallScreen}
|
isSmallScreen={isSmallScreen}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</FieldSet>
|
||||||
|
|
||||||
<TabPanel>
|
<FieldSet legend={translate('Crew')}>
|
||||||
<MovieCrewPostersConnector
|
<MovieCrewPostersConnector
|
||||||
isSmallScreen={isSmallScreen}
|
isSmallScreen={isSmallScreen}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</FieldSet>
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
|
<FieldSet legend={translate('Titles')}>
|
||||||
|
<MovieTitlesTable
|
||||||
|
movieId={id}
|
||||||
|
/>
|
||||||
|
</FieldSet>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<OrganizePreviewModalConnector
|
<OrganizePreviewModalConnector
|
||||||
|
@ -777,6 +730,12 @@ class MovieDetails extends Component {
|
||||||
showImportMode={false}
|
showImportMode={false}
|
||||||
onModalClose={this.onInteractiveImportModalClose}
|
onModalClose={this.onInteractiveImportModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<MovieInteractiveSearchModalConnector
|
||||||
|
isOpen={isInteractiveSearchModalOpen}
|
||||||
|
movieId={id}
|
||||||
|
onModalClose={this.onInteractiveSearchModalClose}
|
||||||
|
/>
|
||||||
</PageContentBody>
|
</PageContentBody>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
.container {
|
||||||
|
border: 1px solid $borderColor;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: $white;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MovieTitlesTableContentConnector from './MovieTitlesTableContentConnector';
|
import MovieTitlesTableContentConnector from './MovieTitlesTableContentConnector';
|
||||||
|
import styles from './MovieTitlesTable.css';
|
||||||
|
|
||||||
function MovieTitlesTable(props) {
|
function MovieTitlesTable(props) {
|
||||||
const {
|
const {
|
||||||
|
@ -7,9 +8,11 @@ function MovieTitlesTable(props) {
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
<MovieTitlesTableContentConnector
|
<MovieTitlesTableContentConnector
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
.container {
|
||||||
|
border: 1px solid $borderColor;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: $white;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MovieHistoryTableContentConnector from './MovieHistoryTableContentConnector';
|
import MovieHistoryTableContentConnector from './MovieHistoryTableContentConnector';
|
||||||
|
import styles from './MovieHistoryTable.css';
|
||||||
|
|
||||||
function MovieHistoryTable(props) {
|
function MovieHistoryTable(props) {
|
||||||
const {
|
const {
|
||||||
|
@ -7,9 +8,11 @@ function MovieHistoryTable(props) {
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
<MovieHistoryTableContentConnector
|
<MovieHistoryTableContentConnector
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import { sizes } from 'Helpers/Props';
|
||||||
|
import MovieInteractiveSearchModalContent from './MovieInteractiveSearchModalContent';
|
||||||
|
|
||||||
|
function MovieInteractiveSearchModal(props) {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
movieId,
|
||||||
|
onModalClose
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
closeOnBackgroundClick={false}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
size={sizes.EXTRA_LARGE}
|
||||||
|
>
|
||||||
|
<MovieInteractiveSearchModalContent
|
||||||
|
movieId={movieId}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
MovieInteractiveSearchModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
movieId: PropTypes.number.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MovieInteractiveSearchModal;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions';
|
||||||
|
import MovieInteractiveSearchModal from './MovieInteractiveSearchModal';
|
||||||
|
|
||||||
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
|
return {
|
||||||
|
onModalClose() {
|
||||||
|
dispatch(cancelFetchReleases());
|
||||||
|
dispatch(clearReleases());
|
||||||
|
props.onModalClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(null, createMapDispatchToProps)(MovieInteractiveSearchModal);
|
|
@ -0,0 +1,45 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import { scrollDirections } from 'Helpers/Props';
|
||||||
|
import InteractiveSearchConnector from 'InteractiveSearch/InteractiveSearchConnector';
|
||||||
|
|
||||||
|
function MovieInteractiveSearchModalContent(props) {
|
||||||
|
const {
|
||||||
|
movieId,
|
||||||
|
onModalClose
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
Interactive Search
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody scrollDirection={scrollDirections.BOTH}>
|
||||||
|
<InteractiveSearchConnector
|
||||||
|
searchPayload={{
|
||||||
|
movieId
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={onModalClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
MovieInteractiveSearchModalContent.propTypes = {
|
||||||
|
movieId: PropTypes.number.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MovieInteractiveSearchModalContent;
|
|
@ -1,5 +1,4 @@
|
||||||
.container {
|
.container {
|
||||||
margin-top: 20px;
|
|
||||||
border: 1px solid $borderColor;
|
border: 1px solid $borderColor;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
|
|
Loading…
Reference in New Issue