mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-19 22:04:56 -04:00
New: Upstream Updates
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Link from 'Components/Link/Link';
|
||||
@@ -120,8 +121,13 @@ class AddNewMovie extends Component {
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>Failed to load search results, please try again.</div>
|
||||
!isFetching && !!error ?
|
||||
<div className={styles.message}>
|
||||
<div className={styles.helpText}>
|
||||
Failed to load search results, please try again.
|
||||
</div>
|
||||
<div>{getErrorMessage(error)}</div>
|
||||
</div> : null
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -99,10 +99,10 @@ class ImportMovieSelectFolder extends Component {
|
||||
Some tips to ensure the import goes smoothly:
|
||||
<ul>
|
||||
<li className={styles.tip}>
|
||||
Make sure your files include the quality in the name. eg. <span className={styles.code}>movie.2008.bluray.mkv</span>
|
||||
Make sure that your files include the quality in their filenames. eg. <span className={styles.code}>movie.2008.bluray.mkv</span>
|
||||
</li>
|
||||
<li className={styles.tip}>
|
||||
Point Radarr to the folder containing all of your movies not a specific one. eg. <span className={styles.code}>"{isWindows ? 'C:\\movies' : '/movies'}"</span> and not <span className={styles.code}>"{isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}"</span>
|
||||
Point Radarr to the folder containing all of your movies, not a specific one. eg. <span className={styles.code}>"{isWindows ? 'C:\\movies' : '/movies'}"</span> and not <span className={styles.code}>"{isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}"</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.episodeInfo {
|
||||
color: $calendarTextDim;
|
||||
}
|
||||
|
||||
.seriesTitle,
|
||||
.episodeTitle {
|
||||
@add-mixin truncate;
|
||||
|
||||
flex: 1 0 1px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
@@ -37,6 +40,10 @@
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.airTime {
|
||||
color: $calendarTextDim;
|
||||
}
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
.seriesTitle {
|
||||
@add-mixin truncate;
|
||||
|
||||
flex: 1 0 1px;
|
||||
margin-right: 10px;
|
||||
color: #3a3f51;
|
||||
@@ -23,10 +22,12 @@
|
||||
|
||||
.airTime {
|
||||
flex: 1 0 1px;
|
||||
color: $calendarTextDim;
|
||||
}
|
||||
|
||||
.episodeInfo {
|
||||
margin-left: 10px;
|
||||
color: $calendarTextDim;
|
||||
}
|
||||
|
||||
.absoluteEpisodeNumber {
|
||||
@@ -80,3 +81,7 @@
|
||||
.premiere {
|
||||
composes: premiere from '~Calendar/Events/CalendarEvent.css';
|
||||
}
|
||||
|
||||
.unaired {
|
||||
composes: unaired from '~Calendar/Events/CalendarEvent.css';
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ class FileBrowserModalContent extends Component {
|
||||
<Scroller
|
||||
ref={this.setScrollerRef}
|
||||
className={styles.scroller}
|
||||
scrollDirection={scrollDirections.BOTH}
|
||||
>
|
||||
{
|
||||
!!error &&
|
||||
@@ -152,7 +153,10 @@ class FileBrowserModalContent extends Component {
|
||||
|
||||
{
|
||||
isPopulated && !error &&
|
||||
<Table columns={columns}>
|
||||
<Table
|
||||
horizontalScroll={false}
|
||||
columns={columns}
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
emptyParent &&
|
||||
|
||||
@@ -132,6 +132,7 @@ class FilterBuilderModalContent extends Component {
|
||||
filterBuilderProps,
|
||||
isSaving,
|
||||
saveError,
|
||||
onCancelPress,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
|
||||
@@ -190,7 +191,7 @@ class FilterBuilderModalContent extends Component {
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>
|
||||
<Button onPress={onCancelPress}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
@@ -220,6 +221,7 @@ FilterBuilderModalContent.propTypes = {
|
||||
dispatchDeleteCustomFilter: PropTypes.func.isRequired,
|
||||
onSaveCustomFilterPress: PropTypes.func.isRequired,
|
||||
dispatchSetFilter: PropTypes.func.isRequired,
|
||||
onCancelPress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,17 @@ class FilterModal extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
onCancelPress = () => {
|
||||
if (this.state.filterBuilder) {
|
||||
this.setState({
|
||||
filterBuilder: false,
|
||||
id: null
|
||||
});
|
||||
} else {
|
||||
this.onModalClose();
|
||||
}
|
||||
}
|
||||
|
||||
onModalClose = () => {
|
||||
this.setState({
|
||||
filterBuilder: false,
|
||||
@@ -67,6 +78,7 @@ class FilterModal extends Component {
|
||||
<FilterBuilderModalContentConnector
|
||||
{...otherProps}
|
||||
id={id}
|
||||
onCancelPress={this.onCancelPress}
|
||||
onModalClose={this.onModalClose}
|
||||
/> :
|
||||
<CustomFiltersModalContentConnector
|
||||
|
||||
@@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
||||
import { Manager, Popper, Reference } from 'react-popper';
|
||||
import classNames from 'classnames';
|
||||
import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
||||
import isMobileUtil from 'Utilities/isMobile';
|
||||
import { isMobile as isMobileUtil } from 'Utilities/mobile';
|
||||
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||
import { icons, sizes, scrollDirections } from 'Helpers/Props';
|
||||
import Icon from 'Components/Icon';
|
||||
|
||||
@@ -18,10 +18,19 @@ class PathInput extends Component {
|
||||
this._node = document.getElementById('portal-root');
|
||||
|
||||
this.state = {
|
||||
value: props.value,
|
||||
isFileBrowserModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { value } = this.props;
|
||||
|
||||
if (prevProps.value !== value) {
|
||||
this.setState({ value });
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
@@ -51,11 +60,8 @@ class PathInput extends Component {
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onInputChange = (event, { newValue }) => {
|
||||
this.props.onChange({
|
||||
name: this.props.name,
|
||||
value: newValue
|
||||
});
|
||||
onInputChange = ({ value }) => {
|
||||
this.setState({ value });
|
||||
}
|
||||
|
||||
onInputKeyDown = (event) => {
|
||||
@@ -77,6 +83,11 @@ class PathInput extends Component {
|
||||
}
|
||||
|
||||
onInputBlur = () => {
|
||||
this.props.onChange({
|
||||
name: this.props.name,
|
||||
value: this.state.value
|
||||
});
|
||||
|
||||
this.props.onClearPaths();
|
||||
}
|
||||
|
||||
@@ -108,13 +119,18 @@ class PathInput extends Component {
|
||||
const {
|
||||
className,
|
||||
name,
|
||||
value,
|
||||
paths,
|
||||
includeFiles,
|
||||
hasFileBrowser,
|
||||
onChange,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
value,
|
||||
isFileBrowserModalOpen
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<AutoSuggestInput
|
||||
@@ -130,7 +146,7 @@ class PathInput extends Component {
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onChange={onChange}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
|
||||
{
|
||||
@@ -144,7 +160,7 @@ class PathInput extends Component {
|
||||
</FormInputButton>
|
||||
|
||||
<FileBrowserModal
|
||||
isOpen={this.state.isFileBrowserModalOpen}
|
||||
isOpen={isFileBrowserModalOpen}
|
||||
name={name}
|
||||
value={value}
|
||||
includeFiles={includeFiles}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
.internalInput {
|
||||
flex: 1 1 0%;
|
||||
margin-top: -6px;
|
||||
margin-left: 3px;
|
||||
min-width: 20%;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
bottom: -1px;
|
||||
left: -1px;
|
||||
display: flex;
|
||||
align-items: start;
|
||||
flex-wrap: wrap;
|
||||
padding: 6px 16px;
|
||||
height: 33px;
|
||||
padding: 1px 16px;
|
||||
min-height: 33px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.tag {
|
||||
composes: link from '~Components/Link/Link.css';
|
||||
|
||||
height: 31px;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { kinds } from 'Helpers/Props';
|
||||
import tagShape from 'Helpers/Props/Shapes/tagShape';
|
||||
import Label from 'Components/Label';
|
||||
import Link from 'Components/Link/Link';
|
||||
import styles from './TagInputTag.css';
|
||||
|
||||
class TagInputTag extends Component {
|
||||
|
||||
@@ -31,9 +32,9 @@ class TagInputTag extends Component {
|
||||
tag,
|
||||
kind
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={styles.tag}
|
||||
tabIndex={-1}
|
||||
onPress={this.onDelete}
|
||||
>
|
||||
|
||||
@@ -39,6 +39,7 @@ class Menu extends Component {
|
||||
|
||||
this._scheduleUpdate = null;
|
||||
this._menuButtonId = getUniqueElememtId();
|
||||
this._menuContentId = getUniqueElememtId();
|
||||
|
||||
this.state = {
|
||||
isMenuOpen: false,
|
||||
@@ -99,12 +100,14 @@ class Menu extends Component {
|
||||
window.addEventListener('resize', this.onWindowResize);
|
||||
window.addEventListener('scroll', this.onWindowScroll, { capture: true });
|
||||
window.addEventListener('click', this.onWindowClick);
|
||||
window.addEventListener('touchstart', this.onTouchStart);
|
||||
}
|
||||
|
||||
_removeListener() {
|
||||
window.removeEventListener('resize', this.onWindowResize);
|
||||
window.removeEventListener('scroll', this.onWindowScroll, { capture: true });
|
||||
window.removeEventListener('click', this.onWindowClick);
|
||||
window.removeEventListener('touchstart', this.onTouchStart);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -123,6 +126,30 @@ class Menu extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
onTouchStart = (event) => {
|
||||
const menuButton = document.getElementById(this._menuButtonId);
|
||||
const menuContent = document.getElementById(this._menuContentId);
|
||||
|
||||
if (!menuButton || !menuContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.targetTouches.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = event.targetTouches[0].target;
|
||||
|
||||
if (
|
||||
!menuButton.contains(target) &&
|
||||
!menuContent.contains(target) &&
|
||||
this.state.isMenuOpen
|
||||
) {
|
||||
this.setState({ isMenuOpen: false });
|
||||
this._removeListener();
|
||||
}
|
||||
}
|
||||
|
||||
onWindowResize = () => {
|
||||
this.setMaxHeight();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ class MenuContent extends Component {
|
||||
const {
|
||||
forwardedRef,
|
||||
className,
|
||||
id,
|
||||
children,
|
||||
style,
|
||||
isOpen
|
||||
@@ -19,6 +20,7 @@ class MenuContent extends Component {
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
ref={forwardedRef}
|
||||
className={className}
|
||||
style={style}
|
||||
@@ -38,6 +40,7 @@ class MenuContent extends Component {
|
||||
MenuContent.propTypes = {
|
||||
forwardedRef: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
id: PropTypes.string.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
style: PropTypes.object,
|
||||
isOpen: PropTypes.bool
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.menuItem {
|
||||
@add-mixin truncate;
|
||||
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
padding: 10px 20px;
|
||||
@@ -17,3 +16,8 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.isDisabled {
|
||||
color: $disabledColor;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Link from 'Components/Link/Link';
|
||||
import styles from './MenuItem.css';
|
||||
|
||||
@@ -12,12 +13,17 @@ class MenuItem extends Component {
|
||||
const {
|
||||
className,
|
||||
children,
|
||||
isDisabled,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={className}
|
||||
className={classNames(
|
||||
className,
|
||||
isDisabled && styles.isDisabled
|
||||
)}
|
||||
isDisabled={isDisabled}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
@@ -28,11 +34,13 @@ class MenuItem extends Component {
|
||||
|
||||
MenuItem.propTypes = {
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node.isRequired
|
||||
children: PropTypes.node.isRequired,
|
||||
isDisabled: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
MenuItem.defaultProps = {
|
||||
className: styles.menuItem
|
||||
className: styles.menuItem,
|
||||
isDisabled: false
|
||||
};
|
||||
|
||||
export default MenuItem;
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.modalOpenIOS {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sizes
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,8 @@ import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import elementClass from 'element-class';
|
||||
import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
||||
import { isIOS } from 'Utilities/mobile';
|
||||
import { setScrollLock } from 'Utilities/scrollLock';
|
||||
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import ErrorBoundary from 'Components/Error/ErrorBoundary';
|
||||
@@ -31,6 +33,7 @@ class Modal extends Component {
|
||||
this._node = document.getElementById('portal-root');
|
||||
this._backgroundRef = null;
|
||||
this._modalId = getUniqueElememtId();
|
||||
this._bodyScrollTop = 0;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -69,7 +72,14 @@ class Modal extends Component {
|
||||
window.addEventListener('keydown', this.onKeyDown);
|
||||
|
||||
if (openModals.length === 1) {
|
||||
elementClass(document.body).add(styles.modalOpen);
|
||||
if (isIOS()) {
|
||||
setScrollLock(true);
|
||||
const scrollTop = document.body.scrollTop;
|
||||
this._bodyScrollTop = scrollTop;
|
||||
elementClass(document.body).add(styles.modalOpenIOS);
|
||||
} else {
|
||||
elementClass(document.body).add(styles.modalOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +88,14 @@ class Modal extends Component {
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
|
||||
if (openModals.length === 0) {
|
||||
elementClass(document.body).remove(styles.modalOpen);
|
||||
setScrollLock(false);
|
||||
|
||||
if (isIOS()) {
|
||||
elementClass(document.body).remove(styles.modalOpenIOS);
|
||||
document.body.scrollTop = this._bodyScrollTop;
|
||||
} else {
|
||||
elementClass(document.body).remove(styles.modalOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ ModalBody.propTypes = {
|
||||
className: PropTypes.string,
|
||||
innerClassName: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
scrollDirection: PropTypes.oneOf([scrollDirections.NONE, scrollDirections.HORIZONTAL, scrollDirections.VERTICAL])
|
||||
scrollDirection: PropTypes.oneOf(scrollDirections.all)
|
||||
};
|
||||
|
||||
ModalBody.defaultProps = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { isLocked } from 'Utilities/scrollLock';
|
||||
import { scrollDirections } from 'Helpers/Props';
|
||||
import OverlayScroller from 'Components/Scroller/OverlayScroller';
|
||||
import Scroller from 'Components/Scroller/Scroller';
|
||||
@@ -7,6 +8,17 @@ import styles from './PageContentBody.css';
|
||||
|
||||
class PageContentBody extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onScroll = (props) => {
|
||||
const { onScroll } = this.props;
|
||||
|
||||
if (this.props.onScroll && !isLocked()) {
|
||||
onScroll(props);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -27,6 +39,7 @@ class PageContentBody extends Component {
|
||||
className={className}
|
||||
scrollDirection={scrollDirections.VERTICAL}
|
||||
{...otherProps}
|
||||
onScroll={this.onScroll}
|
||||
>
|
||||
<div className={innerClassName}>
|
||||
{children}
|
||||
@@ -41,6 +54,7 @@ PageContentBody.propTypes = {
|
||||
innerClassName: PropTypes.string,
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
onScroll: PropTypes.func,
|
||||
dispatch: PropTypes.func
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
@add-mixin scrollbar;
|
||||
@add-mixin scrollbarTrack;
|
||||
@add-mixin scrollbarThumb;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.none {
|
||||
@@ -26,3 +27,11 @@
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.both {
|
||||
overflow: scroll;
|
||||
|
||||
&.autoScroll {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class Scroller extends Component {
|
||||
|
||||
Scroller.propTypes = {
|
||||
className: PropTypes.string,
|
||||
scrollDirection: PropTypes.oneOf([scrollDirections.NONE, scrollDirections.HORIZONTAL, scrollDirections.VERTICAL]).isRequired,
|
||||
scrollDirection: PropTypes.oneOf(scrollDirections.all).isRequired,
|
||||
autoScroll: PropTypes.bool.isRequired,
|
||||
scrollTop: PropTypes.number,
|
||||
children: PropTypes.node,
|
||||
|
||||
@@ -9,6 +9,7 @@ import titleCase from 'Utilities/String/titleCase';
|
||||
import { fetchCommands, updateCommand, finishCommand } from 'Store/Actions/commandActions';
|
||||
import { setAppValue, setVersion } from 'Store/Actions/appActions';
|
||||
import { update, updateItem, removeItem } from 'Store/Actions/baseActions';
|
||||
import { fetchMovies } from 'Store/Actions/movieActions';
|
||||
import { fetchHealth } from 'Store/Actions/systemActions';
|
||||
import { fetchQueue, fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||
@@ -72,6 +73,7 @@ const mapDispatchToProps = {
|
||||
dispatchFetchQueue: fetchQueue,
|
||||
dispatchFetchQueueDetails: fetchQueueDetails,
|
||||
dispatchFetchRootFolders: fetchRootFolders,
|
||||
dispatchFetchMovies: fetchMovies,
|
||||
dispatchFetchTags: fetchTags,
|
||||
dispatchFetchTagDetails: fetchTagDetails
|
||||
};
|
||||
@@ -258,6 +260,7 @@ class SignalRConnector extends Component {
|
||||
|
||||
const {
|
||||
dispatchFetchCommands,
|
||||
dispatchFetchMovies,
|
||||
dispatchSetAppValue
|
||||
} = this.props;
|
||||
|
||||
@@ -265,6 +268,7 @@ class SignalRConnector extends Component {
|
||||
// are in sync after reconnecting.
|
||||
|
||||
if (this.props.isReconnecting || this.props.isDisconnected) {
|
||||
dispatchFetchMovies();
|
||||
dispatchFetchCommands();
|
||||
repopulatePage();
|
||||
}
|
||||
@@ -346,6 +350,7 @@ SignalRConnector.propTypes = {
|
||||
dispatchFetchQueue: PropTypes.func.isRequired,
|
||||
dispatchFetchQueueDetails: PropTypes.func.isRequired,
|
||||
dispatchFetchRootFolders: PropTypes.func.isRequired,
|
||||
dispatchFetchMovies: PropTypes.func.isRequired,
|
||||
dispatchFetchTags: PropTypes.func.isRequired,
|
||||
dispatchFetchTagDetails: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
.tableContainer {
|
||||
overflow-x: auto;
|
||||
&.horizontalScroll {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.table {
|
||||
@@ -10,7 +12,12 @@
|
||||
|
||||
@media only screen and (max-width: $breakpointSmall) {
|
||||
.tableContainer {
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
width: fit-content;
|
||||
|
||||
&.horizontalScroll {
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { icons, scrollDirections } from 'Helpers/Props';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import Scroller from 'Components/Scroller/Scroller';
|
||||
@@ -28,6 +29,7 @@ function getTableHeaderCellProps(props) {
|
||||
function Table(props) {
|
||||
const {
|
||||
className,
|
||||
horizontalScroll,
|
||||
selectAll,
|
||||
columns,
|
||||
optionsComponent,
|
||||
@@ -41,14 +43,22 @@ function Table(props) {
|
||||
|
||||
return (
|
||||
<Scroller
|
||||
className={styles.tableContainer}
|
||||
scrollDirection={scrollDirections.HORIZONTAL}
|
||||
className={classNames(
|
||||
styles.tableContainer,
|
||||
horizontalScroll && styles.horizontalScroll
|
||||
)}
|
||||
scrollDirection={
|
||||
horizontalScroll ?
|
||||
scrollDirections.HORIZONTAL :
|
||||
scrollDirections.NONE
|
||||
}
|
||||
>
|
||||
<table className={className}>
|
||||
<TableHeader>
|
||||
{
|
||||
selectAll &&
|
||||
<TableSelectAllHeaderCell {...otherProps} />
|
||||
selectAll ?
|
||||
<TableSelectAllHeaderCell {...otherProps} /> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
@@ -111,6 +121,7 @@ function Table(props) {
|
||||
|
||||
Table.propTypes = {
|
||||
className: PropTypes.string,
|
||||
horizontalScroll: PropTypes.bool.isRequired,
|
||||
selectAll: PropTypes.bool.isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
optionsComponent: PropTypes.elementType,
|
||||
@@ -123,6 +134,7 @@ Table.propTypes = {
|
||||
|
||||
Table.defaultProps = {
|
||||
className: styles.table,
|
||||
horizontalScroll: true,
|
||||
selectAll: false
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { WindowScroller } from 'react-virtualized';
|
||||
import { isLocked } from 'Utilities/scrollLock';
|
||||
import { scrollDirections } from 'Helpers/Props';
|
||||
import Measure from 'Components/Measure';
|
||||
import Scroller from 'Components/Scroller/Scroller';
|
||||
@@ -83,6 +84,16 @@ class VirtualTable extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
onScroll = (props) => {
|
||||
if (isLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { onScroll } = this.props;
|
||||
|
||||
onScroll(props);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -107,7 +118,7 @@ class VirtualTable extends Component {
|
||||
<Measure onMeasure={this.onMeasure}>
|
||||
<WindowScroller
|
||||
scrollElement={isSmallScreen ? undefined : this._contentBodyNode}
|
||||
onScroll={onScroll}
|
||||
onScroll={this.onScroll}
|
||||
>
|
||||
{({ height, isScrolling }) => {
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { Manager, Popper, Reference } from 'react-popper';
|
||||
import classNames from 'classnames';
|
||||
import isMobileUtil from 'Utilities/isMobile';
|
||||
import { isMobile as isMobileUtil } from 'Utilities/mobile';
|
||||
import { kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import Portal from 'Components/Portal';
|
||||
import styles from './Tooltip.css';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const NONE = 'none';
|
||||
export const BOTH = 'both';
|
||||
export const HORIZONTAL = 'horizontal';
|
||||
export const VERTICAL = 'vertical';
|
||||
|
||||
export const all = [NONE, HORIZONTAL, VERTICAL];
|
||||
export const all = [NONE, HORIZONTAL, VERTICAL, BOTH];
|
||||
|
||||
@@ -5,7 +5,7 @@ import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
import selectAll from 'Utilities/Table/selectAll';
|
||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import { align, icons, kinds, scrollDirections } from 'Helpers/Props';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Icon from 'Components/Icon';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
@@ -73,7 +73,7 @@ const filterExistingFilesOptions = {
|
||||
|
||||
const importModeOptions = [
|
||||
{ key: 'move', value: 'Move Files' },
|
||||
{ key: 'copy', value: 'Copy Files' }
|
||||
{ key: 'copy', value: 'Hardlink/Copy Files' }
|
||||
];
|
||||
|
||||
const SELECT = 'select';
|
||||
@@ -217,7 +217,7 @@ class InteractiveImportModalContent extends Component {
|
||||
Manual Import - {title || folder}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<ModalBody scrollDirection={scrollDirections.BOTH}>
|
||||
{
|
||||
showFilterExistingFiles &&
|
||||
<div className={styles.filterContainer}>
|
||||
@@ -270,6 +270,7 @@ class InteractiveImportModalContent extends Component {
|
||||
isPopulated && !!items.length && !isFetching && !isFetching &&
|
||||
<Table
|
||||
columns={columns}
|
||||
horizontalScroll={false}
|
||||
selectAll={true}
|
||||
allSelected={allSelected}
|
||||
allUnselected={allUnselected}
|
||||
|
||||
@@ -43,6 +43,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||
componentDidMount() {
|
||||
const {
|
||||
downloadId,
|
||||
movieId,
|
||||
folder
|
||||
} = this.props;
|
||||
|
||||
@@ -52,6 +53,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||
|
||||
this.props.dispatchFetchInteractiveImportItems({
|
||||
downloadId,
|
||||
movieId,
|
||||
folder,
|
||||
filterExistingFiles
|
||||
});
|
||||
@@ -65,11 +67,13 @@ class InteractiveImportModalContentConnector extends Component {
|
||||
if (prevState.filterExistingFiles !== filterExistingFiles) {
|
||||
const {
|
||||
downloadId,
|
||||
movieId,
|
||||
folder
|
||||
} = this.props;
|
||||
|
||||
this.props.dispatchFetchInteractiveImportItems({
|
||||
downloadId,
|
||||
movieId,
|
||||
folder,
|
||||
filterExistingFiles
|
||||
});
|
||||
@@ -172,6 +176,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||
|
||||
InteractiveImportModalContentConnector.propTypes = {
|
||||
downloadId: PropTypes.string,
|
||||
movieId: PropTypes.number,
|
||||
folder: PropTypes.string,
|
||||
filterExistingFiles: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
.protocol {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.indexer {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 85px;
|
||||
}
|
||||
|
||||
.quality,
|
||||
.language {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
@@ -20,3 +32,9 @@
|
||||
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.peers {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ class InteractiveSearchRow extends Component {
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
<TableRowCell>
|
||||
<TableRowCell className={styles.protocol}>
|
||||
<ProtocolLabel
|
||||
protocol={protocol}
|
||||
/>
|
||||
@@ -143,7 +143,7 @@ class InteractiveSearchRow extends Component {
|
||||
</Link>
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell>
|
||||
<TableRowCell className={styles.indexer}>
|
||||
{indexer}
|
||||
</TableRowCell>
|
||||
|
||||
@@ -151,7 +151,7 @@ class InteractiveSearchRow extends Component {
|
||||
{formatBytes(size)}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell>
|
||||
<TableRowCell className={styles.peers}>
|
||||
{
|
||||
protocol === 'torrent' &&
|
||||
<Peers
|
||||
|
||||
@@ -22,6 +22,7 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||
import MovieFileEditorTable from 'MovieFile/Editor/MovieFileEditorTable';
|
||||
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
|
||||
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
|
||||
@@ -35,7 +36,6 @@ import MovieDetailsLinks from './MovieDetailsLinks';
|
||||
import InteractiveSearchTable from '../../InteractiveSearch/InteractiveSearchTable';
|
||||
// import MovieTagsConnector from './MovieTagsConnector';
|
||||
import styles from './MovieDetails.css';
|
||||
import InteractiveImportModal from '../../InteractiveImport/InteractiveImportModal';
|
||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
||||
|
||||
const defaultFontSize = parseInt(fonts.defaultFontSize);
|
||||
@@ -528,6 +528,7 @@ class MovieDetails extends Component {
|
||||
|
||||
<InteractiveImportModal
|
||||
isOpen={isInteractiveImportModalOpen}
|
||||
movieId={id}
|
||||
folder={path}
|
||||
allowMovieChange={false}
|
||||
showFilterExistingFiles={true}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
.link {
|
||||
composes: link from '~Components/Link/Link.css';
|
||||
}
|
||||
|
||||
display: block;
|
||||
.unavailablePath {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.unavailableLabel {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.freeSpace,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import Label from 'Components/Label';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import Link from 'Components/Link/Link';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
@@ -12,30 +13,45 @@ function RootFolderRow(props) {
|
||||
const {
|
||||
id,
|
||||
path,
|
||||
accessible,
|
||||
freeSpace,
|
||||
unmappedFolders,
|
||||
onDeletePress
|
||||
} = props;
|
||||
|
||||
const unmappedFoldersCount = unmappedFolders.length || '-';
|
||||
const isUnavailable = !accessible;
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
<TableRowCell>
|
||||
<Link
|
||||
className={styles.link}
|
||||
to={`/add/import/${id}`}
|
||||
>
|
||||
{path}
|
||||
</Link>
|
||||
{
|
||||
isUnavailable ?
|
||||
<div className={styles.unavailablePath}>
|
||||
{path}
|
||||
|
||||
<Label
|
||||
className={styles.unavailableLabel}
|
||||
kind={kinds.DANGER}
|
||||
>
|
||||
Unavailable
|
||||
</Label>
|
||||
</div> :
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to={`/add/import/${id}`}
|
||||
>
|
||||
{path}
|
||||
</Link>
|
||||
}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.freeSpace}>
|
||||
{formatBytes(freeSpace) || '-'}
|
||||
{(isUnavailable || isNaN(freeSpace)) ? '-' : formatBytes(freeSpace)}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.unmappedFolders}>
|
||||
{unmappedFoldersCount}
|
||||
{isUnavailable ? '-' : unmappedFolders.length}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.actions}>
|
||||
@@ -52,13 +68,13 @@ function RootFolderRow(props) {
|
||||
RootFolderRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
freeSpace: PropTypes.number.isRequired,
|
||||
accessible: PropTypes.bool.isRequired,
|
||||
freeSpace: PropTypes.number,
|
||||
unmappedFolders: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onDeletePress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
RootFolderRow.defaultProps = {
|
||||
freeSpace: 0,
|
||||
unmappedFolders: []
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,12 @@ import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
|
||||
const logLevelOptions = [
|
||||
{ key: 'info', value: 'Info' },
|
||||
{ key: 'debug', value: 'Debug' },
|
||||
{ key: 'trace', value: 'Trace' }
|
||||
];
|
||||
|
||||
function LoggingSettings(props) {
|
||||
const {
|
||||
settings,
|
||||
@@ -16,12 +22,6 @@ function LoggingSettings(props) {
|
||||
logLevel
|
||||
} = settings;
|
||||
|
||||
const logLevelOptions = [
|
||||
{ key: 'info', value: 'Info' },
|
||||
{ key: 'debug', value: 'Debug' },
|
||||
{ key: 'trace', value: 'Trace' }
|
||||
];
|
||||
|
||||
return (
|
||||
<FieldSet legend="Logging">
|
||||
<FormGroup>
|
||||
|
||||
@@ -12,6 +12,7 @@ import FormLabel from 'Components/Form/FormLabel';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import RootFoldersConnector from 'RootFolder/RootFoldersConnector';
|
||||
import NamingConnector from './Naming/NamingConnector';
|
||||
import AddRootFolderConnector from './RootFolder/AddRootFolderConnector';
|
||||
|
||||
const rescanAfterRefreshOptions = [
|
||||
{ key: 'always', value: 'Always' },
|
||||
@@ -135,6 +136,23 @@ class MediaManagement extends Component {
|
||||
</FormGroup>
|
||||
}
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>Minimum Free Space</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
unit='MB'
|
||||
name="minimumFreeSpaceWhenImporting"
|
||||
helpText="Prevent import if it would leave less than this amount of disk space available"
|
||||
onChange={onInputChange}
|
||||
{...settings.minimumFreeSpaceWhenImporting}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
@@ -281,6 +299,23 @@ class MediaManagement extends Component {
|
||||
{...settings.recycleBin}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Recycling Bin Cleanup</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="recycleBinCleanupDays"
|
||||
helpText="Set to 0 to disable automatic cleanup"
|
||||
helpTextWarning="Files in the recycle bin older than the selected number of days will be cleaned up automatically"
|
||||
min={0}
|
||||
onChange={onInputChange}
|
||||
{...settings.recycleBinCleanupDays}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FieldSet>
|
||||
|
||||
{
|
||||
@@ -374,6 +409,7 @@ class MediaManagement extends Component {
|
||||
|
||||
<FieldSet legend="Root Folders">
|
||||
<RootFoldersConnector />
|
||||
<AddRootFolderConnector />
|
||||
</FieldSet>
|
||||
</PageContentBodyConnector>
|
||||
</PageContent>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.addRootFolderButtonContainer {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.importButtonIcon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { icons, kinds, sizes } from 'Helpers/Props';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Icon from 'Components/Icon';
|
||||
import FileBrowserModal from 'Components/FileBrowser/FileBrowserModal';
|
||||
import styles from './AddRootFolder.css';
|
||||
|
||||
class AddRootFolder extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isAddNewRootFolderModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
onAddNewRootFolderPress = () => {
|
||||
this.setState({ isAddNewRootFolderModalOpen: true });
|
||||
}
|
||||
|
||||
onNewRootFolderSelect = ({ value }) => {
|
||||
this.props.onNewRootFolderSelect(value);
|
||||
}
|
||||
|
||||
onAddRootFolderModalClose = () => {
|
||||
this.setState({ isAddNewRootFolderModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.addRootFolderButtonContainer}>
|
||||
<Button
|
||||
kind={kinds.PRIMARY}
|
||||
size={sizes.LARGE}
|
||||
onPress={this.onAddNewRootFolderPress}
|
||||
>
|
||||
<Icon
|
||||
className={styles.importButtonIcon}
|
||||
name={icons.DRIVE}
|
||||
/>
|
||||
Add Root Folder
|
||||
</Button>
|
||||
|
||||
<FileBrowserModal
|
||||
isOpen={this.state.isAddNewRootFolderModalOpen}
|
||||
name="rootFolderPath"
|
||||
value=""
|
||||
onChange={this.onNewRootFolderSelect}
|
||||
onModalClose={this.onAddRootFolderModalClose}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddRootFolder.propTypes = {
|
||||
onNewRootFolderSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AddRootFolder;
|
||||
@@ -0,0 +1,13 @@
|
||||
import { connect } from 'react-redux';
|
||||
import AddRootFolder from './AddRootFolder';
|
||||
import { addRootFolder } from 'Store/Actions/rootFolderActions';
|
||||
|
||||
function createMapDispatchToProps(dispatch) {
|
||||
return {
|
||||
onNewRootFolderSelect(path) {
|
||||
dispatch(addRootFolder({ path }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, createMapDispatchToProps)(AddRootFolder);
|
||||
@@ -238,7 +238,11 @@ class EditQualityProfileModalContent extends Component {
|
||||
id ?
|
||||
<div
|
||||
className={styles.deleteButtonContainer}
|
||||
title={isInUse ? 'Can\'t delete a quality profile that is attached to a movie' : undefined}
|
||||
title={
|
||||
isInUse ?
|
||||
'Can\'t delete a quality profile that is attached to a movie' :
|
||||
undefined
|
||||
}
|
||||
>
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
|
||||
@@ -147,6 +147,7 @@ function TagDetailsModalContent(props) {
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
kind={kinds.DANGER}
|
||||
title={isTagUsed ? 'Cannot be deleted while in use' : undefined}
|
||||
isDisabled={isTagUsed}
|
||||
onPress={onDeleteTagPress}
|
||||
>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
.tag {
|
||||
composes: card from '~Components/Card.css';
|
||||
|
||||
width: 150px;
|
||||
flex: 150px 0 1;
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-bottom: 20px;
|
||||
white-space: nowrap;
|
||||
font-weight: 300;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ export const toggleMovieMonitored = createThunk(TOGGLE_MOVIE_MONITORED);
|
||||
|
||||
export const setMovieValue = createAction(SET_MOVIE_VALUE, (payload) => {
|
||||
return {
|
||||
section: 'movies',
|
||||
section,
|
||||
...payload
|
||||
};
|
||||
});
|
||||
|
||||
@@ -174,6 +174,7 @@ module.exports = {
|
||||
|
||||
calendarTodayBackgroundColor: '#ddd',
|
||||
calendarBorderColor: '#cecece',
|
||||
calendarTextDim: '#666',
|
||||
|
||||
//
|
||||
// Table
|
||||
|
||||
@@ -26,6 +26,7 @@ function getInternalLink(source) {
|
||||
/>
|
||||
);
|
||||
case 'DownloadClientCheck':
|
||||
case 'DownloadClientStatusCheck':
|
||||
case 'ImportMechanismCheck':
|
||||
return (
|
||||
<IconButton
|
||||
@@ -67,6 +68,7 @@ function getTestLink(source, props) {
|
||||
/>
|
||||
);
|
||||
case 'DownloadClientCheck':
|
||||
case 'DownloadClientStatusCheck':
|
||||
return (
|
||||
<SpinnerIconButton
|
||||
name={icons.TEST}
|
||||
|
||||
@@ -27,5 +27,5 @@
|
||||
.actions {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 20px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import MobileDetect from 'mobile-detect';
|
||||
|
||||
export default function isMobile() {
|
||||
const mobileDetect = new MobileDetect(window.navigator.userAgent);
|
||||
|
||||
return mobileDetect.mobile() != null;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import MobileDetect from 'mobile-detect';
|
||||
|
||||
const mobileDetect = new MobileDetect(window.navigator.userAgent);
|
||||
|
||||
export function isMobile() {
|
||||
|
||||
return mobileDetect.mobile() != null;
|
||||
}
|
||||
|
||||
export function isIOS() {
|
||||
return mobileDetect.is('iOS');
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Allow iOS devices to disable scrolling of the body/virtual table
|
||||
// when a modal is open. This will prevent focusing an input in a
|
||||
// modal causing the modal to close due to scrolling.
|
||||
|
||||
let scrollLock = false;
|
||||
|
||||
export function isLocked() {
|
||||
return scrollLock;
|
||||
}
|
||||
|
||||
export function setScrollLock(locked) {
|
||||
scrollLock = locked;
|
||||
}
|
||||
Reference in New Issue
Block a user