New: File info scrolls on mobile

Closes #4436
This commit is contained in:
Mark McDowall 2021-04-12 22:15:59 -07:00
parent 5938c38bc3
commit d29e254dcb
5 changed files with 277 additions and 68 deletions

View File

@ -0,0 +1,20 @@
.size {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 100px;
}
.language,
.quality {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 100px;
}
.actions {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
display: flex;
justify-content: flex-end;
width: 55px;
}

View File

@ -0,0 +1,179 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import formatBytes from 'Utilities/Number/formatBytes';
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 TableRow from 'Components/Table/TableRow';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import Popover from 'Components/Tooltip/Popover';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeQuality from 'Episode/EpisodeQuality';
import MediaInfo from './MediaInfo';
import styles from './EpisodeFileRow.css';
class EpisodeFileRow extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isRemoveEpisodeFileModalOpen: false
};
}
//
// Listeners
onRemoveEpisodeFilePress = () => {
this.setState({ isRemoveEpisodeFileModalOpen: true });
}
onConfirmRemoveEpisodeFile = () => {
this.props.onDeleteEpisodeFile();
this.setState({ isRemoveEpisodeFileModalOpen: false });
}
onRemoveEpisodeFileModalClose = () => {
this.setState({ isRemoveEpisodeFileModalOpen: false });
}
//
// Render
render() {
const {
path,
size,
language,
quality,
languageCutoffNotMet,
qualityCutoffNotMet,
mediaInfo,
columns
} = this.props;
return (
<TableRow>
{
columns.map((column) => {
const {
name,
isVisible
} = column;
if (!isVisible) {
return null;
}
if (name === 'path') {
return (
<TableRowCell key={name}>
{path}
</TableRowCell>
);
}
if (name === 'size') {
return (
<TableRowCell key={name}>
{formatBytes(size)}
</TableRowCell>
);
}
if (name === 'language') {
return (
<TableRowCell
key={name}
className={styles.language}
>
<EpisodeLanguage
language={language}
isCutoffNotMet={languageCutoffNotMet}
/>
</TableRowCell>
);
}
if (name === 'quality') {
return (
<TableRowCell
key={name}
className={styles.quality}
>
<EpisodeQuality
quality={quality}
isCutoffNotMet={qualityCutoffNotMet}
/>
</TableRowCell>
);
}
if (name === 'actions') {
return (
<TableRowCell
key={name}
className={styles.actions}
>
{
mediaInfo ?
<Popover
anchor={
<Icon
name={icons.MEDIA_INFO}
/>
}
title="Media Info"
body={<MediaInfo {...mediaInfo} />}
position={tooltipPositions.LEFT}
/> :
null
}
<IconButton
title="Delete episode from disk"
name={icons.REMOVE}
onPress={this.onRemoveEpisodeFilePress}
/>
</TableRowCell>
);
}
return null;
})
}
<ConfirmModal
isOpen={this.state.isRemoveEpisodeFileModalOpen}
kind={kinds.DANGER}
title="Delete Episode File"
message={`Are you sure you want to delete '${path}'?`}
confirmLabel="Delete"
onConfirm={this.onConfirmRemoveEpisodeFile}
onCancel={this.onRemoveEpisodeFileModalClose}
/>
</TableRow>
);
}
}
EpisodeFileRow.propTypes = {
path: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
language: PropTypes.object.isRequired,
languageCutoffNotMet: PropTypes.bool.isRequired,
quality: PropTypes.object.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired,
mediaInfo: PropTypes.object,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onDeleteEpisodeFile: PropTypes.func.isRequired
};
export default EpisodeFileRow;

View File

@ -4,8 +4,12 @@
font-weight: bold;
}
.overview,
.overview {
margin-top: 20px;
}
.files {
overflow-x: auto;
margin-top: 20px;
}
@ -25,9 +29,8 @@
}
.path {
@add-mixin truncate;
flex: 1 0 1px;
white-space: nowrap !important;
}
.size,
@ -46,3 +49,11 @@
flex: 0 0 80px;
}
}
@media only screen and (max-width: $breakpointSmall) {
.files {
overflow-y: hidden;
min-width: 100%;
width: 100%;
}
}

View File

@ -1,18 +1,48 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import formatBytes from 'Utilities/Number/formatBytes';
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
import Icon from 'Components/Icon';
import { kinds, sizes } from 'Helpers/Props';
import Label from 'Components/Label';
import IconButton from 'Components/Link/IconButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import Popover from 'Components/Tooltip/Popover';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
import EpisodeQuality from 'Episode/EpisodeQuality';
import EpisodeAiringConnector from './EpisodeAiringConnector';
import MediaInfo from './MediaInfo';
import EpisodeFileRow from './EpisodeFileRow';
import styles from './EpisodeSummary.css';
const columns = [
{
name: 'path',
label: 'Path',
isSortable: false,
isVisible: true
},
{
name: 'size',
label: 'Size',
isSortable: false,
isVisible: true
},
{
name: 'language',
label: 'Language',
isSortable: false,
isVisible: true
},
{
name: 'quality',
label: 'Quality',
isSortable: false,
isVisible: true
},
{
name: 'actions',
label: '',
isSortable: false,
isVisible: true
}
];
class EpisodeSummary extends Component {
//
@ -54,8 +84,11 @@ class EpisodeSummary extends Component {
mediaInfo,
path,
size,
language,
quality,
qualityCutoffNotMet
languageCutoffNotMet,
qualityCutoffNotMet,
onDeleteEpisodeFile
} = this.props;
const hasOverview = !!overview;
@ -93,63 +126,23 @@ class EpisodeSummary extends Component {
</div>
{
path &&
<div className={styles.files}>
<div className={styles.filesHeader}>
<div className={styles.path}>
Path
</div>
<div className={styles.size}>
Size
</div>
<div className={styles.quality}>
Quality
</div>
<div className={styles.actions} />
</div>
<div className={styles.fileRow}>
<div
className={styles.path}
title={path}
>
{path}
</div>
<div className={styles.size}>
{formatBytes(size)}
</div>
<div className={styles.quality}>
<EpisodeQuality
quality={quality}
isCutoffNotMet={qualityCutoffNotMet}
/>
</div>
<div className={styles.actions}>
<Popover
anchor={
<Icon
name={icons.MEDIA_INFO}
/>
}
title="Media Info"
body={<MediaInfo {...mediaInfo} />}
position={tooltipPositions.LEFT}
/>
<IconButton
title="Delete episode from disk"
name={icons.REMOVE}
onPress={this.onRemoveEpisodeFilePress}
/>
</div>
</div>
</div>
path ?
<Table columns={columns}>
<TableBody>
<EpisodeFileRow
path={path}
size={size}
language={language}
languageCutoffNotMet={languageCutoffNotMet}
quality={quality}
qualityCutoffNotMet={qualityCutoffNotMet}
mediaInfo={mediaInfo}
columns={columns}
onDeleteEpisodeFile={onDeleteEpisodeFile}
/>
</TableBody>
</Table> :
null
}
<ConfirmModal
@ -175,6 +168,8 @@ EpisodeSummary.propTypes = {
mediaInfo: PropTypes.object,
path: PropTypes.string,
size: PropTypes.number,
language: PropTypes.object,
languageCutoffNotMet: PropTypes.bool,
quality: PropTypes.object,
qualityCutoffNotMet: PropTypes.bool,
onDeleteEpisodeFile: PropTypes.func.isRequired

View File

@ -28,6 +28,8 @@ function createMapStateToProps() {
mediaInfo,
path,
size,
language,
languageCutoffNotMet,
quality,
qualityCutoffNotMet
} = episodeFile;
@ -40,6 +42,8 @@ function createMapStateToProps() {
mediaInfo,
path,
size,
language,
languageCutoffNotMet,
quality,
qualityCutoffNotMet
};