mirror of
https://github.com/Prowlarr/Prowlarr
synced 2025-12-06 08:34:28 +01:00
Cleanup unused frontend components
This commit is contained in:
parent
6dc475cf53
commit
2b6b17707d
110 changed files with 102 additions and 2602 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
# Explicitly set bash scripts to have unix endings
|
# Explicitly set bash scripts to have unix endings
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
macOS/Prowlarr text eol=lf
|
distribution/osx/Prowlarr text eol=lf
|
||||||
|
|
||||||
# Custom for Visual Studio
|
# Custom for Visual Studio
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,8 @@ stages:
|
||||||
- bash: ./build.sh --packages
|
- bash: ./build.sh --packages
|
||||||
displayName: Create Packages
|
displayName: Create Packages
|
||||||
- bash: |
|
- bash: |
|
||||||
setup/inno/ISCC.exe setup/prowlarr.iss //DFramework=net5.0
|
distribution/windows/setup/inno/ISCC.exe distribution/windows/setup/prowlarr.iss //DFramework=net5.0
|
||||||
cp setup/output/Prowlarr.*windows.net5.0.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x64-installer.exe
|
cp distribution/windows/setup/output/Prowlarr.*windows.net5.0.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||||
displayName: Create .NET Core Windows installer
|
displayName: Create .NET Core Windows installer
|
||||||
- publish: $(Build.ArtifactStagingDirectory)
|
- publish: $(Build.ArtifactStagingDirectory)
|
||||||
artifact: 'WindowsInstaller'
|
artifact: 'WindowsInstaller'
|
||||||
|
|
|
||||||
4
build.sh
4
build.sh
|
|
@ -21,7 +21,7 @@ UpdateVersionNumber()
|
||||||
echo "Updating Version Info"
|
echo "Updating Version Info"
|
||||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$PROWLARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$PROWLARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
||||||
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
||||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$PROWLARRVERSION<\/string>/g" macOS/Prowlarr.app/Contents/Info.plist
|
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$PROWLARRVERSION<\/string>/g" distribution/osx/Prowlarr.app/Contents/Info.plist
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,7 +163,7 @@ PackageMacOSApp()
|
||||||
|
|
||||||
rm -rf $folder
|
rm -rf $folder
|
||||||
mkdir -p $folder
|
mkdir -p $folder
|
||||||
cp -r macOS/Prowlarr.app $folder
|
cp -r distribution/osx/Prowlarr.app $folder
|
||||||
mkdir -p $folder/Prowlarr.app/Contents/MacOS
|
mkdir -p $folder/Prowlarr.app/Contents/MacOS
|
||||||
|
|
||||||
echo "Copying Binaries"
|
echo "Copying Binaries"
|
||||||
|
|
|
||||||
5
debian/changelog
vendored
5
debian/changelog
vendored
|
|
@ -1,5 +0,0 @@
|
||||||
nzbdrone {version} {branch}; urgency=low
|
|
||||||
|
|
||||||
* Automatic Release.
|
|
||||||
|
|
||||||
-- NzbDrone <contact@nzbdrone.com> Mon, 26 Aug 2013 00:00:00 -0700
|
|
||||||
1
debian/compat
vendored
1
debian/compat
vendored
|
|
@ -1 +0,0 @@
|
||||||
8
|
|
||||||
12
debian/control
vendored
12
debian/control
vendored
|
|
@ -1,12 +0,0 @@
|
||||||
Section: web
|
|
||||||
Priority: optional
|
|
||||||
Maintainer: Sonarr <contact@nzbdrone.com>
|
|
||||||
Source: nzbdrone
|
|
||||||
Homepage: https://sonarr.tv
|
|
||||||
Vcs-Git: git@github.com:Sonarr/Sonarr.git
|
|
||||||
Vcs-Browser: https://github.com/Sonarr/Sonarr
|
|
||||||
|
|
||||||
Package: nzbdrone
|
|
||||||
Architecture: all
|
|
||||||
Depends: libmono-cil-dev (>= 3.2), sqlite3 (>= 3.7), mediainfo (>= 0.7.52)
|
|
||||||
Description: Sonarr is an internet PVR
|
|
||||||
24
debian/copyright
vendored
24
debian/copyright
vendored
|
|
@ -1,24 +0,0 @@
|
||||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
|
||||||
Upstream-Name: nzbdrone
|
|
||||||
Source: https://github.com/Sonarr/Sonarr
|
|
||||||
|
|
||||||
Files: *
|
|
||||||
Copyright: 2010-2016 Sonarr <hello@sonarr.tv>
|
|
||||||
|
|
||||||
License: GPL-3.0+
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
.
|
|
||||||
This package is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
.
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
.
|
|
||||||
On Debian systems, the complete text of the GNU General
|
|
||||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
|
||||||
1
debian/install
vendored
1
debian/install
vendored
|
|
@ -1 +0,0 @@
|
||||||
nzbdrone_bin/* opt/NzbDrone
|
|
||||||
13
debian/rules
vendored
13
debian/rules
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
#!/usr/bin/make -f
|
|
||||||
# -*- makefile -*-
|
|
||||||
# Sample debian/rules that uses debhelper.
|
|
||||||
# This file was originally written by Joey Hess and Craig Small.
|
|
||||||
# As a special exception, when this file is copied by dh-make into a
|
|
||||||
# dh-make output file, you may use that output file without restriction.
|
|
||||||
# This special exception was added by Craig Small in version 0.37 of dh-make.
|
|
||||||
|
|
||||||
# Uncomment this to turn on verbose mode.
|
|
||||||
#export DH_VERBOSE=1
|
|
||||||
|
|
||||||
%:
|
|
||||||
dh $@
|
|
||||||
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
|
@ -49,8 +49,8 @@ Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts
|
||||||
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "..\_artifacts\windows\{#Framework}\Prowlarr\Prowlarr.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\..\..\_artifacts\windows\{#Framework}\Prowlarr\Prowlarr.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\_artifacts\windows\{#Framework}\Prowlarr\*"; Excludes: "Prowlarr.Update"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
Source: "..\..\..\_artifacts\windows\{#Framework}\Prowlarr\*"; Excludes: "Prowlarr.Update"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
|
|
@ -7,7 +7,6 @@ import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
|
||||||
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
|
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
|
||||||
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
|
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
|
||||||
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
|
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
|
||||||
import MovieStatusFilterBuilderRowValue from './MovieStatusFilterBuilderRowValue';
|
|
||||||
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
|
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
|
||||||
import TagFilterBuilderRowValueConnector from './TagFilterBuilderRowValueConnector';
|
import TagFilterBuilderRowValueConnector from './TagFilterBuilderRowValueConnector';
|
||||||
import styles from './FilterBuilderRow.css';
|
import styles from './FilterBuilderRow.css';
|
||||||
|
|
@ -60,9 +59,6 @@ function getRowValueConnector(selectedFilterBuilderProp) {
|
||||||
case filterBuilderValueTypes.PROTOCOL:
|
case filterBuilderValueTypes.PROTOCOL:
|
||||||
return ProtocolFilterBuilderRowValue;
|
return ProtocolFilterBuilderRowValue;
|
||||||
|
|
||||||
case filterBuilderValueTypes.MOVIE_STATUS:
|
|
||||||
return MovieStatusFilterBuilderRowValue;
|
|
||||||
|
|
||||||
case filterBuilderValueTypes.TAG:
|
case filterBuilderValueTypes.TAG:
|
||||||
return TagFilterBuilderRowValueConnector;
|
return TagFilterBuilderRowValueConnector;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
|
||||||
|
|
||||||
const protocols = [
|
|
||||||
{ id: 'tba', name: 'TBA' },
|
|
||||||
{ id: 'announced', name: 'Announced' },
|
|
||||||
{ id: 'inCinemas', name: 'In Cinemas' },
|
|
||||||
{ id: 'released', name: 'Released' },
|
|
||||||
{ id: 'deleted', name: 'Deleted' }
|
|
||||||
];
|
|
||||||
|
|
||||||
function MovieStatusFilterBuilderRowValue(props) {
|
|
||||||
return (
|
|
||||||
<FilterBuilderRowValue
|
|
||||||
tagList={protocols}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MovieStatusFilterBuilderRowValue;
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
.heart {
|
|
||||||
margin-right: 5px;
|
|
||||||
color: $themeRed;
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import styles from './HeartRating.css';
|
|
||||||
|
|
||||||
function HeartRating({ rating, iconSize, hideHeart }) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{
|
|
||||||
!hideHeart &&
|
|
||||||
<Icon
|
|
||||||
className={styles.heart}
|
|
||||||
name={icons.HEART}
|
|
||||||
size={iconSize}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{rating * 10}%
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
HeartRating.propTypes = {
|
|
||||||
rating: PropTypes.number.isRequired,
|
|
||||||
iconSize: PropTypes.number.isRequired,
|
|
||||||
hideHeart: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
HeartRating.defaultProps = {
|
|
||||||
iconSize: 14
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HeartRating;
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
.lists {
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { kinds, sizes } from 'Helpers/Props';
|
|
||||||
import Label from './Label';
|
|
||||||
import styles from './ImportListList.css';
|
|
||||||
|
|
||||||
function ImportListList({ lists, importListList }) {
|
|
||||||
return (
|
|
||||||
<div className={styles.lists}>
|
|
||||||
{
|
|
||||||
lists.map((t) => {
|
|
||||||
const list = _.find(importListList, { id: t });
|
|
||||||
|
|
||||||
if (!list) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
key={list.id}
|
|
||||||
kind={kinds.INFO}
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
>
|
|
||||||
{list.name}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportListList.propTypes = {
|
|
||||||
lists: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
importListList: PropTypes.arrayOf(PropTypes.object).isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
ImportListList.defaultProps = {
|
|
||||||
lists: []
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ImportListList;
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createImportListSelector from 'Store/Selectors/createImportListSelector';
|
|
||||||
import ImportListList from './ImportListList';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createImportListSelector(),
|
|
||||||
(importListList) => {
|
|
||||||
return {
|
|
||||||
importListList
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(ImportListList);
|
|
||||||
|
|
@ -1,181 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
const FPS = 20;
|
|
||||||
const STEP = 1;
|
|
||||||
const TIMEOUT = 1 / FPS * 1000;
|
|
||||||
|
|
||||||
class Marquee extends Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
text: PropTypes.string,
|
|
||||||
title: PropTypes.string,
|
|
||||||
hoverToStop: PropTypes.bool,
|
|
||||||
loop: PropTypes.bool,
|
|
||||||
className: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
text: '',
|
|
||||||
title: '',
|
|
||||||
hoverToStop: true,
|
|
||||||
loop: false
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
animatedWidth: 0,
|
|
||||||
overflowWidth: 0,
|
|
||||||
direction: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.measureText();
|
|
||||||
|
|
||||||
if (this.props.hoverToStop) {
|
|
||||||
this.startAnimation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
if (this.props.text.length !== nextProps.text.length) {
|
|
||||||
clearTimeout(this.marqueeTimer);
|
|
||||||
this.setState({ animatedWidth: 0, direction: 0 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.measureText();
|
|
||||||
|
|
||||||
if (this.props.hoverToStop) {
|
|
||||||
this.startAnimation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
clearTimeout(this.marqueeTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
onHandleMouseEnter = () => {
|
|
||||||
if (this.props.hoverToStop) {
|
|
||||||
clearTimeout(this.marqueeTimer);
|
|
||||||
} else if (this.state.overflowWidth > 0) {
|
|
||||||
this.startAnimation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onHandleMouseLeave = () => {
|
|
||||||
if (this.props.hoverToStop && this.state.overflowWidth > 0) {
|
|
||||||
this.startAnimation();
|
|
||||||
} else {
|
|
||||||
clearTimeout(this.marqueeTimer);
|
|
||||||
this.setState({ animatedWidth: 0 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
startAnimation = () => {
|
|
||||||
clearTimeout(this.marqueeTimer);
|
|
||||||
const isLeading = this.state.animatedWidth === 0;
|
|
||||||
const timeout = isLeading ? 0 : TIMEOUT;
|
|
||||||
|
|
||||||
const animate = () => {
|
|
||||||
const { overflowWidth } = this.state;
|
|
||||||
let animatedWidth = this.state.animatedWidth;
|
|
||||||
let direction = this.state.direction;
|
|
||||||
|
|
||||||
if (direction === 0) {
|
|
||||||
animatedWidth = this.state.animatedWidth + STEP;
|
|
||||||
} else {
|
|
||||||
animatedWidth = this.state.animatedWidth - STEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isRoundOver = animatedWidth < 0;
|
|
||||||
const endOfText = animatedWidth > overflowWidth;
|
|
||||||
|
|
||||||
if (endOfText) {
|
|
||||||
direction = direction === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRoundOver) {
|
|
||||||
if (this.props.loop) {
|
|
||||||
direction = direction === 0;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ animatedWidth, direction });
|
|
||||||
this.marqueeTimer = setTimeout(animate, TIMEOUT);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.marqueeTimer = setTimeout(animate, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
measureText = () => {
|
|
||||||
const container = this.container;
|
|
||||||
const node = this.text;
|
|
||||||
|
|
||||||
if (container && node) {
|
|
||||||
const containerWidth = container.offsetWidth;
|
|
||||||
const textWidth = node.offsetWidth;
|
|
||||||
const overflowWidth = textWidth - containerWidth;
|
|
||||||
|
|
||||||
if (overflowWidth !== this.state.overflowWidth) {
|
|
||||||
this.setState({ overflowWidth });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const style = {
|
|
||||||
position: 'relative',
|
|
||||||
right: this.state.animatedWidth,
|
|
||||||
whiteSpace: 'nowrap'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.overflowWidth < 0) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={(el) => {
|
|
||||||
this.container = el;
|
|
||||||
}}
|
|
||||||
className={`ui-marquee ${this.props.className}`}
|
|
||||||
style={{ overflow: 'hidden' }}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
ref={(el) => {
|
|
||||||
this.text = el;
|
|
||||||
}}
|
|
||||||
style={style}
|
|
||||||
title={(this.props.title && (this.props.text !== this.props.title)) ? `Original Title: ${this.props.title}` : this.props.text}
|
|
||||||
>
|
|
||||||
{this.props.text}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={(el) => {
|
|
||||||
this.container = el;
|
|
||||||
}}
|
|
||||||
className={`ui-marquee ${this.props.className}`.trim()}
|
|
||||||
style={{ overflow: 'hidden' }}
|
|
||||||
onMouseEnter={this.onHandleMouseEnter}
|
|
||||||
onMouseLeave={this.onHandleMouseLeave}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
ref={(el) => {
|
|
||||||
this.text = el;
|
|
||||||
}}
|
|
||||||
style={style}
|
|
||||||
title={(this.props.title && (this.props.text !== this.props.title)) ? `Original Title: ${this.props.title}` : this.props.text}
|
|
||||||
>
|
|
||||||
{this.props.text}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Marquee;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
.toggleButton {
|
|
||||||
composes: button from '~Components/Link/IconButton.css';
|
|
||||||
|
|
||||||
padding: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.isDisabled {
|
|
||||||
color: $disabledColor;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
import classNames from 'classnames';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import styles from './MonitorToggleButton.css';
|
|
||||||
|
|
||||||
function getTooltip(monitored, isDisabled) {
|
|
||||||
if (isDisabled) {
|
|
||||||
return 'Cannot toggle monitored state when movie is unmonitored';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (monitored) {
|
|
||||||
return 'Monitored, click to unmonitor';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Unmonitored, click to monitor';
|
|
||||||
}
|
|
||||||
|
|
||||||
class MonitorToggleButton extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onPress = (event) => {
|
|
||||||
const shiftKey = event.nativeEvent.shiftKey;
|
|
||||||
|
|
||||||
this.props.onPress(!this.props.monitored, { shiftKey });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
className,
|
|
||||||
monitored,
|
|
||||||
isDisabled,
|
|
||||||
isSaving,
|
|
||||||
size,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const iconName = monitored ? icons.MONITORED : icons.UNMONITORED;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SpinnerIconButton
|
|
||||||
className={classNames(
|
|
||||||
className,
|
|
||||||
isDisabled && styles.isDisabled
|
|
||||||
)}
|
|
||||||
name={iconName}
|
|
||||||
size={size}
|
|
||||||
title={getTooltip(monitored, isDisabled)}
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
isSpinning={isSaving}
|
|
||||||
{...otherProps}
|
|
||||||
onPress={this.onPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MonitorToggleButton.propTypes = {
|
|
||||||
className: PropTypes.string.isRequired,
|
|
||||||
monitored: PropTypes.bool.isRequired,
|
|
||||||
size: PropTypes.number,
|
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
onPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
MonitorToggleButton.defaultProps = {
|
|
||||||
className: styles.toggleButton,
|
|
||||||
isDisabled: false,
|
|
||||||
isSaving: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MonitorToggleButton;
|
|
||||||
|
|
@ -6,12 +6,12 @@ import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import MovieSearchResult from './MovieSearchResult';
|
import IndexerSearchResult from './IndexerSearchResult';
|
||||||
import styles from './MovieSearchInput.css';
|
import styles from './IndexerSearchInput.css';
|
||||||
|
|
||||||
const ADD_NEW_TYPE = 'addNew';
|
const ADD_NEW_TYPE = 'addNew';
|
||||||
|
|
||||||
class MovieSearchInput extends Component {
|
class IndexerSearchInput extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
|
@ -86,7 +86,7 @@ class MovieSearchInput extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MovieSearchResult
|
<IndexerSearchResult
|
||||||
{...item.item}
|
{...item.item}
|
||||||
match={item.matches[0]}
|
match={item.matches[0]}
|
||||||
/>
|
/>
|
||||||
|
|
@ -239,9 +239,9 @@ class MovieSearchInput extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieSearchInput.propTypes = {
|
IndexerSearchInput.propTypes = {
|
||||||
onGoToAddNewMovie: PropTypes.func.isRequired,
|
onGoToAddNewMovie: PropTypes.func.isRequired,
|
||||||
bindShortcut: PropTypes.func.isRequired
|
bindShortcut: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default keyboardShortcuts(MovieSearchInput);
|
export default keyboardShortcuts(IndexerSearchInput);
|
||||||
|
|
@ -5,14 +5,14 @@ import { setSearchDefault } from 'Store/Actions/releaseActions';
|
||||||
import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector';
|
import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector';
|
||||||
import createDeepEqualSelector from 'Store/Selectors/createDeepEqualSelector';
|
import createDeepEqualSelector from 'Store/Selectors/createDeepEqualSelector';
|
||||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||||
import MovieSearchInput from './MovieSearchInput';
|
import IndexerSearchInput from './IndexerSearchInput';
|
||||||
|
|
||||||
function createCleanMovieSelector() {
|
function createCleanMovieSelector() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createAllIndexersSelector(),
|
createAllIndexersSelector(),
|
||||||
createTagsSelector(),
|
createTagsSelector(),
|
||||||
(allMovies, allTags) => {
|
(allIndexers, allTags) => {
|
||||||
return allMovies.map((movie) => {
|
return allIndexers.map((movie) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
titleSlug,
|
titleSlug,
|
||||||
|
|
@ -66,4 +66,4 @@ function createMapDispatchToProps(dispatch, props) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(MovieSearchInput);
|
export default connect(createMapStateToProps, createMapDispatchToProps)(IndexerSearchInput);
|
||||||
|
|
@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import styles from './MovieSearchResult.css';
|
import styles from './IndexerSearchResult.css';
|
||||||
|
|
||||||
function MovieSearchResult(props) {
|
function IndexerSearchResult(props) {
|
||||||
const {
|
const {
|
||||||
match,
|
match,
|
||||||
title,
|
title,
|
||||||
|
|
@ -54,7 +54,7 @@ function MovieSearchResult(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieSearchResult.propTypes = {
|
IndexerSearchResult.propTypes = {
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
year: PropTypes.number.isRequired,
|
year: PropTypes.number.isRequired,
|
||||||
alternateTitles: PropTypes.arrayOf(PropTypes.object).isRequired,
|
alternateTitles: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
|
@ -62,4 +62,4 @@ MovieSearchResult.propTypes = {
|
||||||
match: PropTypes.object.isRequired
|
match: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieSearchResult;
|
export default IndexerSearchResult;
|
||||||
|
|
@ -5,8 +5,8 @@ import IconButton from 'Components/Link/IconButton';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
import IndexerSearchInputConnector from './IndexerSearchInputConnector';
|
||||||
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
|
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
|
||||||
import MovieSearchInputConnector from './MovieSearchInputConnector';
|
|
||||||
import PageHeaderActionsMenuConnector from './PageHeaderActionsMenuConnector';
|
import PageHeaderActionsMenuConnector from './PageHeaderActionsMenuConnector';
|
||||||
import styles from './PageHeader.css';
|
import styles from './PageHeader.css';
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ class PageHeader extends Component {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MovieSearchInputConnector />
|
<IndexerSearchInputConnector />
|
||||||
|
|
||||||
<div className={styles.right}>
|
<div className={styles.right}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|
|
||||||
|
|
@ -37,16 +37,6 @@ export const shortcuts = {
|
||||||
SCROLL_BOTTOM: {
|
SCROLL_BOTTOM: {
|
||||||
key: 'mod+end',
|
key: 'mod+end',
|
||||||
name: translate('MovieIndexScrollBottom')
|
name: translate('MovieIndexScrollBottom')
|
||||||
},
|
|
||||||
|
|
||||||
DETAILS_NEXT: {
|
|
||||||
key: '→',
|
|
||||||
name: translate('MovieDetailsNextMovie')
|
|
||||||
},
|
|
||||||
|
|
||||||
DETAILS_PREVIOUS: {
|
|
||||||
key: '←',
|
|
||||||
name: translate('MovieDetailsPreviousMovie')
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ export const CAPTCHA = 'captcha';
|
||||||
export const CARDIGANNCAPTCHA = 'cardigannCaptcha';
|
export const CARDIGANNCAPTCHA = 'cardigannCaptcha';
|
||||||
export const CHECK = 'check';
|
export const CHECK = 'check';
|
||||||
export const DEVICE = 'device';
|
export const DEVICE = 'device';
|
||||||
|
export const KEY_VALUE_LIST = 'keyValueList';
|
||||||
export const INFO = 'info';
|
export const INFO = 'info';
|
||||||
export const MOVIE_MONITORED_SELECT = 'movieMonitoredSelect';
|
export const MOVIE_MONITORED_SELECT = 'movieMonitoredSelect';
|
||||||
export const NUMBER = 'number';
|
export const NUMBER = 'number';
|
||||||
|
|
@ -12,6 +13,7 @@ export const PASSWORD = 'password';
|
||||||
export const PATH = 'path';
|
export const PATH = 'path';
|
||||||
export const INDEXER_FLAGS_SELECT = 'indexerFlagsSelect';
|
export const INDEXER_FLAGS_SELECT = 'indexerFlagsSelect';
|
||||||
export const SELECT = 'select';
|
export const SELECT = 'select';
|
||||||
|
export const DYNAMIC_SELECT = 'dynamicSelect';
|
||||||
export const TAG = 'tag';
|
export const TAG = 'tag';
|
||||||
export const TEXT = 'text';
|
export const TEXT = 'text';
|
||||||
export const TEXT_AREA = 'textArea';
|
export const TEXT_AREA = 'textArea';
|
||||||
|
|
@ -25,6 +27,7 @@ export const all = [
|
||||||
CARDIGANNCAPTCHA,
|
CARDIGANNCAPTCHA,
|
||||||
CHECK,
|
CHECK,
|
||||||
DEVICE,
|
DEVICE,
|
||||||
|
KEY_VALUE_LIST,
|
||||||
INFO,
|
INFO,
|
||||||
MOVIE_MONITORED_SELECT,
|
MOVIE_MONITORED_SELECT,
|
||||||
NUMBER,
|
NUMBER,
|
||||||
|
|
@ -33,6 +36,7 @@ export const all = [
|
||||||
PATH,
|
PATH,
|
||||||
INDEXER_FLAGS_SELECT,
|
INDEXER_FLAGS_SELECT,
|
||||||
SELECT,
|
SELECT,
|
||||||
|
DYNAMIC_SELECT,
|
||||||
TAG,
|
TAG,
|
||||||
TEXT,
|
TEXT,
|
||||||
TEXT_AREA,
|
TEXT_AREA,
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import OrganizeMovieModalContentConnector from './OrganizeMovieModalContentConnector';
|
|
||||||
|
|
||||||
function OrganizeMovieModal(props) {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
onModalClose,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<OrganizeMovieModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizeMovieModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrganizeMovieModal;
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
.renameIcon {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
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 { icons, kinds } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './OrganizeMovieModalContent.css';
|
|
||||||
|
|
||||||
function OrganizeMovieModalContent(props) {
|
|
||||||
const {
|
|
||||||
movieTitles,
|
|
||||||
onModalClose,
|
|
||||||
onOrganizeMoviePress
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
Organize Selected Movies
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<Alert>
|
|
||||||
Tip: To preview a rename... select "Cancel" then click any movie title and use the
|
|
||||||
<Icon
|
|
||||||
className={styles.renameIcon}
|
|
||||||
name={icons.ORGANIZE}
|
|
||||||
/>
|
|
||||||
</Alert>
|
|
||||||
|
|
||||||
<div className={styles.message}>
|
|
||||||
Are you sure you want to organize all files in the {movieTitles.length} selected movie(s)?
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
movieTitles.map((title) => {
|
|
||||||
return (
|
|
||||||
<li key={title}>
|
|
||||||
{title}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={onModalClose}>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onPress={onOrganizeMoviePress}
|
|
||||||
>
|
|
||||||
Organize
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizeMovieModalContent.propTypes = {
|
|
||||||
movieTitles: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
onOrganizeMoviePress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrganizeMovieModalContent;
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import * as commandNames from 'Commands/commandNames';
|
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
|
||||||
import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
|
|
||||||
import OrganizeMovieModalContent from './OrganizeMovieModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state, { movieIds }) => movieIds,
|
|
||||||
createAllMoviesSelector(),
|
|
||||||
(movieIds, allMovies) => {
|
|
||||||
const movies = _.intersectionWith(allMovies, movieIds, (s, id) => {
|
|
||||||
return s.id === id;
|
|
||||||
});
|
|
||||||
|
|
||||||
const sortedMovies = _.orderBy(movies, 'sortTitle');
|
|
||||||
const movieTitles = _.map(sortedMovies, 'title');
|
|
||||||
|
|
||||||
return {
|
|
||||||
movieTitles
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
executeCommand
|
|
||||||
};
|
|
||||||
|
|
||||||
class OrganizeMovieModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onOrganizeMoviePress = () => {
|
|
||||||
this.props.executeCommand({
|
|
||||||
name: commandNames.RENAME_MOVIE,
|
|
||||||
movieIds: this.props.movieIds
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onModalClose(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render(props) {
|
|
||||||
return (
|
|
||||||
<OrganizeMovieModalContent
|
|
||||||
{...this.props}
|
|
||||||
onOrganizeMoviePress={this.onOrganizeMoviePress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizeMovieModalContentConnector.propTypes = {
|
|
||||||
movieIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
executeCommand: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(OrganizeMovieModalContentConnector);
|
|
||||||
|
|
@ -11,10 +11,10 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import { align, icons, sortDirections } from 'Helpers/Props';
|
import { align, icons, sortDirections } from 'Helpers/Props';
|
||||||
|
import AddIndexerModal from 'Indexer/Add/AddIndexerModal';
|
||||||
|
import EditIndexerModalConnector from 'Indexer/Edit/EditIndexerModalConnector';
|
||||||
import IndexerEditorFooter from 'Indexer/Editor/IndexerEditorFooter.js';
|
import IndexerEditorFooter from 'Indexer/Editor/IndexerEditorFooter.js';
|
||||||
import NoIndexer from 'Indexer/NoIndexer';
|
import NoIndexer from 'Indexer/NoIndexer';
|
||||||
import AddIndexerModal from 'Settings/Indexers/Indexers/AddIndexerModal';
|
|
||||||
import EditIndexerModalConnector from 'Settings/Indexers/Indexers/EditIndexerModalConnector';
|
|
||||||
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||||
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
||||||
|
|
@ -23,14 +23,14 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||||
import selectAll from 'Utilities/Table/selectAll';
|
import selectAll from 'Utilities/Table/selectAll';
|
||||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||||
import IndexerIndexFooterConnector from './IndexerIndexFooterConnector';
|
import IndexerIndexFooterConnector from './IndexerIndexFooterConnector';
|
||||||
import MovieIndexFilterMenu from './Menus/MovieIndexFilterMenu';
|
import IndexerIndexFilterMenu from './Menus/IndexerIndexFilterMenu';
|
||||||
import MovieIndexSortMenu from './Menus/MovieIndexSortMenu';
|
import IndexerIndexSortMenu from './Menus/IndexerIndexSortMenu';
|
||||||
import MovieIndexTableConnector from './Table/MovieIndexTableConnector';
|
import IndexerIndexTableConnector from './Table/IndexerIndexTableConnector';
|
||||||
import MovieIndexTableOptionsConnector from './Table/MovieIndexTableOptionsConnector';
|
import IndexerIndexTableOptionsConnector from './Table/IndexerIndexTableOptionsConnector';
|
||||||
import styles from './IndexerIndex.css';
|
import styles from './IndexerIndex.css';
|
||||||
|
|
||||||
function getViewComponent() {
|
function getViewComponent() {
|
||||||
return MovieIndexTableConnector;
|
return IndexerIndexTableConnector;
|
||||||
}
|
}
|
||||||
|
|
||||||
class IndexerIndex extends Component {
|
class IndexerIndex extends Component {
|
||||||
|
|
@ -354,7 +354,7 @@ class IndexerIndex extends Component {
|
||||||
<TableOptionsModalWrapper
|
<TableOptionsModalWrapper
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
optionsComponent={MovieIndexTableOptionsConnector}
|
optionsComponent={IndexerIndexTableOptionsConnector}
|
||||||
>
|
>
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
label={translate('Options')}
|
label={translate('Options')}
|
||||||
|
|
@ -365,14 +365,14 @@ class IndexerIndex extends Component {
|
||||||
|
|
||||||
<PageToolbarSeparator />
|
<PageToolbarSeparator />
|
||||||
|
|
||||||
<MovieIndexSortMenu
|
<IndexerIndexSortMenu
|
||||||
sortKey={sortKey}
|
sortKey={sortKey}
|
||||||
sortDirection={sortDirection}
|
sortDirection={sortDirection}
|
||||||
isDisabled={hasNoIndexer}
|
isDisabled={hasNoIndexer}
|
||||||
onSortSelect={onSortSelect}
|
onSortSelect={onSortSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MovieIndexFilterMenu
|
<IndexerIndexFilterMenu
|
||||||
selectedFilterKey={selectedFilterKey}
|
selectedFilterKey={selectedFilterKey}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
customFilters={customFilters}
|
customFilters={customFilters}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { setMovieFilter } from 'Store/Actions/indexerIndexActions';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.movies.items,
|
(state) => state.indexers.items,
|
||||||
(state) => state.indexerIndex.filterBuilderProps,
|
(state) => state.indexerIndex.filterBuilderProps,
|
||||||
(sectionItems, filterBuilderProps) => {
|
(sectionItems, filterBuilderProps) => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -53,7 +53,7 @@ const mapDispatchToProps = {
|
||||||
dispatchExecuteCommand: executeCommand
|
dispatchExecuteCommand: executeCommand
|
||||||
};
|
};
|
||||||
|
|
||||||
class MovieIndexItemConnector extends Component {
|
class IndexerIndexItemConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
|
@ -78,10 +78,10 @@ class MovieIndexItemConnector extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieIndexItemConnector.propTypes = {
|
IndexerIndexItemConnector.propTypes = {
|
||||||
id: PropTypes.number,
|
id: PropTypes.number,
|
||||||
component: PropTypes.elementType.isRequired,
|
component: PropTypes.elementType.isRequired,
|
||||||
dispatchExecuteCommand: PropTypes.func.isRequired
|
dispatchExecuteCommand: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(MovieIndexItemConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(IndexerIndexItemConnector);
|
||||||
|
|
@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||||
import { align } from 'Helpers/Props';
|
import { align } from 'Helpers/Props';
|
||||||
import MovieIndexFilterModalConnector from 'Indexer/Index/MovieIndexFilterModalConnector';
|
import IndexerIndexFilterModalConnector from 'Indexer/Index/IndexerIndexFilterModalConnector';
|
||||||
|
|
||||||
function MovieIndexFilterMenu(props) {
|
function IndexerIndexFilterMenu(props) {
|
||||||
const {
|
const {
|
||||||
selectedFilterKey,
|
selectedFilterKey,
|
||||||
filters,
|
filters,
|
||||||
|
|
@ -20,13 +20,13 @@ function MovieIndexFilterMenu(props) {
|
||||||
selectedFilterKey={selectedFilterKey}
|
selectedFilterKey={selectedFilterKey}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
customFilters={customFilters}
|
customFilters={customFilters}
|
||||||
filterModalConnectorComponent={MovieIndexFilterModalConnector}
|
filterModalConnectorComponent={IndexerIndexFilterModalConnector}
|
||||||
onFilterSelect={onFilterSelect}
|
onFilterSelect={onFilterSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieIndexFilterMenu.propTypes = {
|
IndexerIndexFilterMenu.propTypes = {
|
||||||
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
|
@ -34,8 +34,8 @@ MovieIndexFilterMenu.propTypes = {
|
||||||
onFilterSelect: PropTypes.func.isRequired
|
onFilterSelect: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
MovieIndexFilterMenu.defaultProps = {
|
IndexerIndexFilterMenu.defaultProps = {
|
||||||
showCustomFilters: false
|
showCustomFilters: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieIndexFilterMenu;
|
export default IndexerIndexFilterMenu;
|
||||||
|
|
@ -6,7 +6,7 @@ import SortMenuItem from 'Components/Menu/SortMenuItem';
|
||||||
import { align, sortDirections } from 'Helpers/Props';
|
import { align, sortDirections } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
function MovieIndexSortMenu(props) {
|
function IndexerIndexSortMenu(props) {
|
||||||
const {
|
const {
|
||||||
sortKey,
|
sortKey,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
|
|
@ -78,11 +78,11 @@ function MovieIndexSortMenu(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieIndexSortMenu.propTypes = {
|
IndexerIndexSortMenu.propTypes = {
|
||||||
sortKey: PropTypes.string,
|
sortKey: PropTypes.string,
|
||||||
sortDirection: PropTypes.oneOf(sortDirections.all),
|
sortDirection: PropTypes.oneOf(sortDirections.all),
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
isDisabled: PropTypes.bool.isRequired,
|
||||||
onSortSelect: PropTypes.func.isRequired
|
onSortSelect: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieIndexSortMenu;
|
export default IndexerIndexSortMenu;
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Menu from 'Components/Menu/Menu';
|
|
||||||
import MenuContent from 'Components/Menu/MenuContent';
|
|
||||||
import SearchMenuItem from 'Components/Menu/SearchMenuItem';
|
|
||||||
import ToolbarMenuButton from 'Components/Menu/ToolbarMenuButton';
|
|
||||||
import { align, icons } from 'Helpers/Props';
|
|
||||||
|
|
||||||
class MovieIndexSearchMenu extends Component {
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isDisabled,
|
|
||||||
onSearchPress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
alignMenu={align.RIGHT}
|
|
||||||
>
|
|
||||||
<ToolbarMenuButton
|
|
||||||
iconName={icons.SEARCH}
|
|
||||||
text="Search"
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
/>
|
|
||||||
<MenuContent>
|
|
||||||
<SearchMenuItem
|
|
||||||
name="missingMoviesSearch"
|
|
||||||
onPress={onSearchPress}
|
|
||||||
>
|
|
||||||
Search Missing
|
|
||||||
</SearchMenuItem>
|
|
||||||
|
|
||||||
<SearchMenuItem
|
|
||||||
name="cutoffUnmetMoviesSearch"
|
|
||||||
onPress={onSearchPress}
|
|
||||||
>
|
|
||||||
Search Cutoff Unmet
|
|
||||||
</SearchMenuItem>
|
|
||||||
</MenuContent>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieIndexSearchMenu.propTypes = {
|
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
|
||||||
onSearchPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieIndexSearchMenu;
|
|
||||||
|
|
@ -8,7 +8,7 @@ import DeleteMovieModal from 'Indexer/Delete/DeleteMovieModal';
|
||||||
import EditMovieModalConnector from 'Indexer/Edit/EditMovieModalConnector';
|
import EditMovieModalConnector from 'Indexer/Edit/EditMovieModalConnector';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
class MovieIndexActionsCell extends Component {
|
class IndexerIndexActionsCell extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
|
@ -94,10 +94,10 @@ class MovieIndexActionsCell extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieIndexActionsCell.propTypes = {
|
IndexerIndexActionsCell.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
isRefreshingMovie: PropTypes.bool.isRequired,
|
isRefreshingMovie: PropTypes.bool.isRequired,
|
||||||
onRefreshMoviePress: PropTypes.func.isRequired
|
onRefreshMoviePress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieIndexActionsCell;
|
export default IndexerIndexActionsCell;
|
||||||
|
|
@ -6,10 +6,10 @@ import VirtualTableHeader from 'Components/Table/VirtualTableHeader';
|
||||||
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
|
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
|
||||||
import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell';
|
import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import MovieIndexTableOptionsConnector from './MovieIndexTableOptionsConnector';
|
import IndexerIndexTableOptionsConnector from './IndexerIndexTableOptionsConnector';
|
||||||
import styles from './MovieIndexHeader.css';
|
import styles from './IndexerIndexHeader.css';
|
||||||
|
|
||||||
class MovieIndexHeader extends Component {
|
class IndexerIndexHeader extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
|
@ -111,7 +111,7 @@ class MovieIndexHeader extends Component {
|
||||||
<TableOptionsModal
|
<TableOptionsModal
|
||||||
isOpen={this.state.isTableOptionsModalOpen}
|
isOpen={this.state.isTableOptionsModalOpen}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
optionsComponent={MovieIndexTableOptionsConnector}
|
optionsComponent={IndexerIndexTableOptionsConnector}
|
||||||
onTableOptionChange={onTableOptionChange}
|
onTableOptionChange={onTableOptionChange}
|
||||||
onModalClose={this.onTableOptionsModalClose}
|
onModalClose={this.onTableOptionsModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
@ -120,7 +120,7 @@ class MovieIndexHeader extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieIndexHeader.propTypes = {
|
IndexerIndexHeader.propTypes = {
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onTableOptionChange: PropTypes.func.isRequired,
|
onTableOptionChange: PropTypes.func.isRequired,
|
||||||
allSelected: PropTypes.bool.isRequired,
|
allSelected: PropTypes.bool.isRequired,
|
||||||
|
|
@ -129,4 +129,4 @@ MovieIndexHeader.propTypes = {
|
||||||
isMovieEditorActive: PropTypes.bool.isRequired
|
isMovieEditorActive: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieIndexHeader;
|
export default IndexerIndexHeader;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { setMovieTableOption } from 'Store/Actions/indexerIndexActions';
|
import { setMovieTableOption } from 'Store/Actions/indexerIndexActions';
|
||||||
import MovieIndexHeader from './MovieIndexHeader';
|
import IndexerIndexHeader from './IndexerIndexHeader';
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -10,4 +10,4 @@ function createMapDispatchToProps(dispatch, props) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(undefined, createMapDispatchToProps)(MovieIndexHeader);
|
export default connect(undefined, createMapDispatchToProps)(IndexerIndexHeader);
|
||||||
|
|
@ -8,7 +8,7 @@ import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCel
|
||||||
import TagListConnector from 'Components/TagListConnector';
|
import TagListConnector from 'Components/TagListConnector';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import DeleteIndexerModal from 'Indexer/Delete/DeleteIndexerModal';
|
import DeleteIndexerModal from 'Indexer/Delete/DeleteIndexerModal';
|
||||||
import EditIndexerModalConnector from 'Settings/Indexers/Indexers/EditIndexerModalConnector';
|
import EditIndexerModalConnector from 'Indexer/Edit/EditIndexerModalConnector';
|
||||||
import titleCase from 'Utilities/String/titleCase';
|
import titleCase from 'Utilities/String/titleCase';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import CapabilitiesLabel from './CapabilitiesLabel';
|
import CapabilitiesLabel from './CapabilitiesLabel';
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ import React, { Component } from 'react';
|
||||||
import VirtualTable from 'Components/Table/VirtualTable';
|
import VirtualTable from 'Components/Table/VirtualTable';
|
||||||
import VirtualTableRow from 'Components/Table/VirtualTableRow';
|
import VirtualTableRow from 'Components/Table/VirtualTableRow';
|
||||||
import { sortDirections } from 'Helpers/Props';
|
import { sortDirections } from 'Helpers/Props';
|
||||||
import MovieIndexItemConnector from 'Indexer/Index/MovieIndexItemConnector';
|
import IndexerIndexItemConnector from 'Indexer/Index/IndexerIndexItemConnector';
|
||||||
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
|
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
|
||||||
|
import IndexerIndexHeaderConnector from './IndexerIndexHeaderConnector';
|
||||||
import IndexerIndexRow from './IndexerIndexRow';
|
import IndexerIndexRow from './IndexerIndexRow';
|
||||||
import MovieIndexHeaderConnector from './MovieIndexHeaderConnector';
|
import styles from './IndexerIndexTable.css';
|
||||||
import styles from './MovieIndexTable.css';
|
|
||||||
|
|
||||||
class MovieIndexTable extends Component {
|
class IndexerIndexTable extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
|
@ -49,8 +49,7 @@ class MovieIndexTable extends Component {
|
||||||
columns,
|
columns,
|
||||||
selectedState,
|
selectedState,
|
||||||
onSelectedChange,
|
onSelectedChange,
|
||||||
isMovieEditorActive,
|
isMovieEditorActive
|
||||||
movieRuntimeFormat
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const movie = items[rowIndex];
|
const movie = items[rowIndex];
|
||||||
|
|
@ -60,7 +59,7 @@ class MovieIndexTable extends Component {
|
||||||
key={key}
|
key={key}
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
<MovieIndexItemConnector
|
<IndexerIndexItemConnector
|
||||||
key={movie.id}
|
key={movie.id}
|
||||||
component={IndexerIndexRow}
|
component={IndexerIndexRow}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
@ -68,7 +67,6 @@ class MovieIndexTable extends Component {
|
||||||
isSelected={selectedState[movie.id]}
|
isSelected={selectedState[movie.id]}
|
||||||
onSelectedChange={onSelectedChange}
|
onSelectedChange={onSelectedChange}
|
||||||
isMovieEditorActive={isMovieEditorActive}
|
isMovieEditorActive={isMovieEditorActive}
|
||||||
movieRuntimeFormat={movieRuntimeFormat}
|
|
||||||
/>
|
/>
|
||||||
</VirtualTableRow>
|
</VirtualTableRow>
|
||||||
);
|
);
|
||||||
|
|
@ -104,7 +102,7 @@ class MovieIndexTable extends Component {
|
||||||
overscanRowCount={2}
|
overscanRowCount={2}
|
||||||
rowRenderer={this.rowRenderer}
|
rowRenderer={this.rowRenderer}
|
||||||
header={
|
header={
|
||||||
<MovieIndexHeaderConnector
|
<IndexerIndexHeaderConnector
|
||||||
columns={columns}
|
columns={columns}
|
||||||
sortKey={sortKey}
|
sortKey={sortKey}
|
||||||
sortDirection={sortDirection}
|
sortDirection={sortDirection}
|
||||||
|
|
@ -122,7 +120,7 @@ class MovieIndexTable extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieIndexTable.propTypes = {
|
IndexerIndexTable.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
sortKey: PropTypes.string,
|
sortKey: PropTypes.string,
|
||||||
|
|
@ -136,8 +134,7 @@ MovieIndexTable.propTypes = {
|
||||||
selectedState: PropTypes.object.isRequired,
|
selectedState: PropTypes.object.isRequired,
|
||||||
onSelectedChange: PropTypes.func.isRequired,
|
onSelectedChange: PropTypes.func.isRequired,
|
||||||
onSelectAllChange: PropTypes.func.isRequired,
|
onSelectAllChange: PropTypes.func.isRequired,
|
||||||
isMovieEditorActive: PropTypes.bool.isRequired,
|
isMovieEditorActive: PropTypes.bool.isRequired
|
||||||
movieRuntimeFormat: PropTypes.string.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieIndexTable;
|
export default IndexerIndexTable;
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { setMovieSort } from 'Store/Actions/indexerIndexActions';
|
import { setMovieSort } from 'Store/Actions/indexerIndexActions';
|
||||||
import MovieIndexTable from './MovieIndexTable';
|
import IndexerIndexTable from './IndexerIndexTable';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.app.dimensions,
|
(state) => state.app.dimensions,
|
||||||
(state) => state.indexerIndex.tableOptions,
|
(state) => state.indexerIndex.tableOptions,
|
||||||
(state) => state.indexerIndex.columns,
|
(state) => state.indexerIndex.columns,
|
||||||
(state) => state.settings.ui.item.movieRuntimeFormat,
|
(dimensions, tableOptions, columns) => {
|
||||||
(dimensions, tableOptions, columns, movieRuntimeFormat) => {
|
|
||||||
return {
|
return {
|
||||||
isSmallScreen: dimensions.isSmallScreen,
|
isSmallScreen: dimensions.isSmallScreen,
|
||||||
showBanners: tableOptions.showBanners,
|
showBanners: tableOptions.showBanners,
|
||||||
columns,
|
columns
|
||||||
movieRuntimeFormat
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -28,4 +26,4 @@ function createMapDispatchToProps(dispatch, props) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(MovieIndexTable);
|
export default connect(createMapStateToProps, createMapDispatchToProps)(IndexerIndexTable);
|
||||||
|
|
@ -6,7 +6,7 @@ import FormLabel from 'Components/Form/FormLabel';
|
||||||
import { inputTypes } from 'Helpers/Props';
|
import { inputTypes } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
class MovieIndexTableOptions extends Component {
|
class IndexerIndexTableOptions extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
|
@ -69,9 +69,9 @@ class MovieIndexTableOptions extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieIndexTableOptions.propTypes = {
|
IndexerIndexTableOptions.propTypes = {
|
||||||
showSearchAction: PropTypes.bool.isRequired,
|
showSearchAction: PropTypes.bool.isRequired,
|
||||||
onTableOptionChange: PropTypes.func.isRequired
|
onTableOptionChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieIndexTableOptions;
|
export default IndexerIndexTableOptions;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import MovieIndexTableOptions from './MovieIndexTableOptions';
|
import IndexerIndexTableOptions from './IndexerIndexTableOptions';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
|
|
@ -11,4 +11,4 @@ function createMapStateToProps() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(MovieIndexTableOptions);
|
export default connect(createMapStateToProps)(IndexerIndexTableOptions);
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
|
||||||
import { kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
function MovieLanguage(props) {
|
|
||||||
const {
|
|
||||||
className,
|
|
||||||
languages,
|
|
||||||
isCutoffNotMet
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!languages) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (languages.length === 1) {
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
className={className}
|
|
||||||
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
|
|
||||||
>
|
|
||||||
{languages[0].name}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover
|
|
||||||
className={className}
|
|
||||||
anchor={
|
|
||||||
<Label
|
|
||||||
className={className}
|
|
||||||
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
|
|
||||||
>
|
|
||||||
Multi-Language
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
title={translate('Languages')}
|
|
||||||
body={
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
languages.map((language) => {
|
|
||||||
return (
|
|
||||||
<li key={language.id}>
|
|
||||||
{language.name}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.LEFT}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieLanguage.propTypes = {
|
|
||||||
className: PropTypes.string,
|
|
||||||
languages: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
isCutoffNotMet: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
MovieLanguage.defaultProps = {
|
|
||||||
isCutoffNotMet: true
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieLanguage;
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
import { kinds } from 'Helpers/Props';
|
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
|
||||||
|
|
||||||
function getTooltip(title, quality, size, isMonitored, isCutoffNotMet) {
|
|
||||||
const revision = quality.revision;
|
|
||||||
|
|
||||||
if (revision.real && revision.real > 0) {
|
|
||||||
title += ' [REAL]';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (revision.version && revision.version > 1) {
|
|
||||||
title += ' [PROPER]';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size) {
|
|
||||||
title += ` - ${formatBytes(size)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isMonitored) {
|
|
||||||
title += ' [Not Monitored]';
|
|
||||||
} else if (isCutoffNotMet) {
|
|
||||||
title += ' [Cutoff Not Met]';
|
|
||||||
}
|
|
||||||
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
function MovieQuality(props) {
|
|
||||||
const {
|
|
||||||
className,
|
|
||||||
title,
|
|
||||||
quality,
|
|
||||||
size,
|
|
||||||
isMonitored,
|
|
||||||
isCutoffNotMet
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
let kind = kinds.DEFAULT;
|
|
||||||
if (!isMonitored) {
|
|
||||||
kind = kinds.DISABLED;
|
|
||||||
} else if (isCutoffNotMet) {
|
|
||||||
kind = kinds.INVERSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!quality) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
className={className}
|
|
||||||
kind={kind}
|
|
||||||
title={getTooltip(title, quality, size, isMonitored, isCutoffNotMet)}
|
|
||||||
>
|
|
||||||
{quality.quality.name}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieQuality.propTypes = {
|
|
||||||
className: PropTypes.string,
|
|
||||||
title: PropTypes.string,
|
|
||||||
quality: PropTypes.object.isRequired,
|
|
||||||
size: PropTypes.number,
|
|
||||||
isMonitored: PropTypes.bool,
|
|
||||||
isCutoffNotMet: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
MovieQuality.defaultProps = {
|
|
||||||
title: '',
|
|
||||||
isMonitored: true
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieQuality;
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
|
|
||||||
export function getMovieStatusDetails(status) {
|
|
||||||
|
|
||||||
let statusDetails = {
|
|
||||||
icon: icons.ANNOUNCED,
|
|
||||||
title: 'Announced',
|
|
||||||
message: 'Movie is announced'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (status === 'deleted') {
|
|
||||||
statusDetails = {
|
|
||||||
icon: icons.MOVIE_DELETED,
|
|
||||||
title: 'Deleted',
|
|
||||||
message: 'Movie was deleted from TMDb'
|
|
||||||
};
|
|
||||||
} else if (status === 'inCinemas') {
|
|
||||||
statusDetails = {
|
|
||||||
icon: icons.IN_CINEMAS,
|
|
||||||
title: 'In Cinemas',
|
|
||||||
message: 'Movie is in Cinemas'
|
|
||||||
};
|
|
||||||
} else if (status === 'released') {
|
|
||||||
statusDetails = {
|
|
||||||
icon: icons.MOVIE_FILE,
|
|
||||||
title: 'Released',
|
|
||||||
message: 'Movie is released'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return statusDetails;
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { PureComponent } from 'react';
|
|
||||||
import Link from 'Components/Link/Link';
|
|
||||||
|
|
||||||
class MovieTitleLink extends PureComponent {
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
titleSlug,
|
|
||||||
title
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const link = `/movie/${titleSlug}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
to={link}
|
|
||||||
title={title}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieTitleLink.propTypes = {
|
|
||||||
titleSlug: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieTitleLink;
|
|
||||||
|
|
@ -11,9 +11,9 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import { align, icons, sortDirections } from 'Helpers/Props';
|
import { align, icons, sortDirections } from 'Helpers/Props';
|
||||||
|
import AddIndexerModal from 'Indexer/Add/AddIndexerModal';
|
||||||
|
import EditIndexerModalConnector from 'Indexer/Edit/EditIndexerModalConnector';
|
||||||
import NoIndexer from 'Indexer/NoIndexer';
|
import NoIndexer from 'Indexer/NoIndexer';
|
||||||
import AddIndexerModal from 'Settings/Indexers/Indexers/AddIndexerModal';
|
|
||||||
import EditIndexerModalConnector from 'Settings/Indexers/Indexers/EditIndexerModalConnector';
|
|
||||||
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||||
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
.indexer {
|
|
||||||
composes: card from '~Components/Card.css';
|
|
||||||
|
|
||||||
width: 290px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nameContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
@add-mixin truncate;
|
|
||||||
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cloneButton {
|
|
||||||
composes: button from '~Components/Link/IconButton.css';
|
|
||||||
|
|
||||||
height: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.enabled {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Card from 'Components/Card';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
import IconButton from 'Components/Link/IconButton';
|
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import EditIndexerModalConnector from './EditIndexerModalConnector';
|
|
||||||
import styles from './Indexer.css';
|
|
||||||
|
|
||||||
class Indexer extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isEditIndexerModalOpen: false,
|
|
||||||
isDeleteIndexerModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onEditIndexerPress = () => {
|
|
||||||
this.setState({ isEditIndexerModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onEditIndexerModalClose = () => {
|
|
||||||
this.setState({ isEditIndexerModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onDeleteIndexerPress = () => {
|
|
||||||
this.setState({
|
|
||||||
isEditIndexerModalOpen: false,
|
|
||||||
isDeleteIndexerModalOpen: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onDeleteIndexerModalClose= () => {
|
|
||||||
this.setState({ isDeleteIndexerModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onConfirmDeleteIndexer = () => {
|
|
||||||
this.props.onConfirmDeleteIndexer(this.props.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCloneIndexerPress = () => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
onCloneIndexerPress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onCloneIndexerPress(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
enable,
|
|
||||||
supportsRss,
|
|
||||||
priority,
|
|
||||||
showPriority
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
className={styles.indexer}
|
|
||||||
overlayContent={true}
|
|
||||||
onPress={this.onEditIndexerPress}
|
|
||||||
>
|
|
||||||
<div className={styles.nameContainer}>
|
|
||||||
<div className={styles.name}>
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<IconButton
|
|
||||||
className={styles.cloneButton}
|
|
||||||
title={translate('CloneIndexer')}
|
|
||||||
name={icons.CLONE}
|
|
||||||
onPress={this.onCloneIndexerPress}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.enabled}>
|
|
||||||
|
|
||||||
{
|
|
||||||
supportsRss && enable &&
|
|
||||||
<Label kind={kinds.SUCCESS}>
|
|
||||||
RSS
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
showPriority &&
|
|
||||||
<Label kind={kinds.DEFAULT}>
|
|
||||||
{translate('Priority')}: {priority}
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
!enable &&
|
|
||||||
<Label
|
|
||||||
kind={kinds.DISABLED}
|
|
||||||
outline={true}
|
|
||||||
>
|
|
||||||
{translate('Disabled')}
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EditIndexerModalConnector
|
|
||||||
id={id}
|
|
||||||
isOpen={this.state.isEditIndexerModalOpen}
|
|
||||||
onModalClose={this.onEditIndexerModalClose}
|
|
||||||
onDeleteIndexerPress={this.onDeleteIndexerPress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={this.state.isDeleteIndexerModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title={translate('DeleteIndexer')}
|
|
||||||
message={translate('DeleteIndexerMessageText', [name])}
|
|
||||||
confirmLabel={translate('Delete')}
|
|
||||||
onConfirm={this.onConfirmDeleteIndexer}
|
|
||||||
onCancel={this.onDeleteIndexerModalClose}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Indexer.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
enable: PropTypes.bool.isRequired,
|
|
||||||
supportsRss: PropTypes.bool.isRequired,
|
|
||||||
supportsSearch: PropTypes.bool.isRequired,
|
|
||||||
onCloneIndexerPress: PropTypes.func.isRequired,
|
|
||||||
onConfirmDeleteIndexer: PropTypes.func.isRequired,
|
|
||||||
priority: PropTypes.number.isRequired,
|
|
||||||
showPriority: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Indexer;
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
.indexers {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addIndexer {
|
|
||||||
composes: indexer from '~./Indexer.css';
|
|
||||||
|
|
||||||
background-color: $cardAlternateBackgroundColor;
|
|
||||||
color: $gray;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 5px 20px 0;
|
|
||||||
border: 1px solid $borderColor;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: $white;
|
|
||||||
}
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Card from 'Components/Card';
|
|
||||||
import FieldSet from 'Components/FieldSet';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import AddIndexerModal from './AddIndexerModal';
|
|
||||||
import EditIndexerModalConnector from './EditIndexerModalConnector';
|
|
||||||
import Indexer from './Indexer';
|
|
||||||
import styles from './Indexers.css';
|
|
||||||
|
|
||||||
class Indexers extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isAddIndexerModalOpen: false,
|
|
||||||
isEditIndexerModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onAddIndexerPress = () => {
|
|
||||||
this.setState({ isAddIndexerModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onCloneIndexerPress = (id) => {
|
|
||||||
this.props.dispatchCloneIndexer({ id });
|
|
||||||
this.setState({ isEditIndexerModalOpen: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onAddIndexerModalClose = ({ indexerSelected = false } = {}) => {
|
|
||||||
this.setState({
|
|
||||||
isAddIndexerModalOpen: false,
|
|
||||||
isEditIndexerModalOpen: indexerSelected
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onEditIndexerModalClose = () => {
|
|
||||||
this.setState({ isEditIndexerModalOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
items,
|
|
||||||
dispatchCloneIndexer,
|
|
||||||
onConfirmDeleteIndexer,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
isAddIndexerModalOpen,
|
|
||||||
isEditIndexerModalOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const showPriority = items.some((index) => index.priority !== 25);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldSet legend={translate('Indexers')}>
|
|
||||||
<PageSectionContent
|
|
||||||
errorMessage={translate('UnableToLoadIndexers')}
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
<div className={styles.indexers}>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<Indexer
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
showPriority={showPriority}
|
|
||||||
onCloneIndexerPress={this.onCloneIndexerPress}
|
|
||||||
onConfirmDeleteIndexer={onConfirmDeleteIndexer}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
<Card
|
|
||||||
className={styles.addIndexer}
|
|
||||||
onPress={this.onAddIndexerPress}
|
|
||||||
>
|
|
||||||
<div className={styles.center}>
|
|
||||||
<Icon
|
|
||||||
name={icons.ADD}
|
|
||||||
size={45}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AddIndexerModal
|
|
||||||
isOpen={isAddIndexerModalOpen}
|
|
||||||
onModalClose={this.onAddIndexerModalClose}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<EditIndexerModalConnector
|
|
||||||
isOpen={isEditIndexerModalOpen}
|
|
||||||
onModalClose={this.onEditIndexerModalClose}
|
|
||||||
/>
|
|
||||||
</PageSectionContent>
|
|
||||||
</FieldSet>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Indexers.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
dispatchCloneIndexer: PropTypes.func.isRequired,
|
|
||||||
onConfirmDeleteIndexer: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Indexers;
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { cloneIndexer, deleteIndexer, fetchIndexers } from 'Store/Actions/indexerActions';
|
|
||||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
|
||||||
import sortByName from 'Utilities/Array/sortByName';
|
|
||||||
import Indexers from './Indexers';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createSortedSectionSelector('indexers', sortByName),
|
|
||||||
(indexers) => indexers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
dispatchFetchIndexers: fetchIndexers,
|
|
||||||
dispatchDeleteIndexer: deleteIndexer,
|
|
||||||
dispatchCloneIndexer: cloneIndexer
|
|
||||||
};
|
|
||||||
|
|
||||||
class IndexersConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.dispatchFetchIndexers();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onConfirmDeleteIndexer = (id) => {
|
|
||||||
this.props.dispatchDeleteIndexer({ id });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Indexers
|
|
||||||
{...this.props}
|
|
||||||
onConfirmDeleteIndexer={this.onConfirmDeleteIndexer}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexersConnector.propTypes = {
|
|
||||||
dispatchFetchIndexers: PropTypes.func.isRequired,
|
|
||||||
dispatchDeleteIndexer: PropTypes.func.isRequired,
|
|
||||||
dispatchCloneIndexer: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(IndexersConnector);
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
.qualityDefinition {
|
|
||||||
display: flex;
|
|
||||||
align-content: stretch;
|
|
||||||
margin: 5px 0;
|
|
||||||
padding-top: 5px;
|
|
||||||
height: 45px;
|
|
||||||
border-top: 1px solid $borderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality,
|
|
||||||
.title {
|
|
||||||
flex: 0 1 250px;
|
|
||||||
padding-right: 20px;
|
|
||||||
line-height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sizeLimit {
|
|
||||||
flex: 0 1 500px;
|
|
||||||
padding-right: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider {
|
|
||||||
width: 100%;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar {
|
|
||||||
top: 9px;
|
|
||||||
margin: 0 5px;
|
|
||||||
height: 3px;
|
|
||||||
background-color: $sliderAccentColor;
|
|
||||||
box-shadow: 0 0 0 #000;
|
|
||||||
|
|
||||||
&:nth-child(3n+1) {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.handle {
|
|
||||||
top: 1px;
|
|
||||||
z-index: 0 !important;
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
border: 3px solid $sliderAccentColor;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: $white;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sizes {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.megabytesPerMinute {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex: 0 0 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sizeInput {
|
|
||||||
composes: input from '~Components/Form/TextInput.css';
|
|
||||||
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 5px;
|
|
||||||
padding: 6px;
|
|
||||||
width: 75px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointSmall) {
|
|
||||||
.qualityDefinition {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.qualityDefinition:first-child {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality {
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sizeLimit {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,308 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import ReactSlider from 'react-slider';
|
|
||||||
import NumberInput from 'Components/Form/NumberInput';
|
|
||||||
import TextInput from 'Components/Form/TextInput';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
|
||||||
import { kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
|
||||||
import roundNumber from 'Utilities/Number/roundNumber';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import QualityDefinitionLimits from './QualityDefinitionLimits';
|
|
||||||
import styles from './QualityDefinition.css';
|
|
||||||
|
|
||||||
const MIN = 0;
|
|
||||||
const MAX = 400;
|
|
||||||
|
|
||||||
const slider = {
|
|
||||||
min: MIN,
|
|
||||||
max: roundNumber(Math.pow(MAX, 1 / 1.1)),
|
|
||||||
step: 0.1
|
|
||||||
};
|
|
||||||
|
|
||||||
function getValue(inputValue) {
|
|
||||||
if (inputValue < MIN) {
|
|
||||||
return MIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputValue > MAX) {
|
|
||||||
return MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
return roundNumber(inputValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSliderValue(value, defaultValue) {
|
|
||||||
const sliderValue = value ? Math.pow(value, 1 / 1.1) : defaultValue;
|
|
||||||
|
|
||||||
return roundNumber(sliderValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
class QualityDefinition extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
sliderMinSize: getSliderValue(props.minSize, slider.min),
|
|
||||||
sliderMaxSize: getSliderValue(props.maxSize, slider.max),
|
|
||||||
sliderPreferredSize: getSliderValue(props.preferredSize, (slider.max - 3))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSliderChange = ([sliderMinSize, sliderPreferredSize, sliderMaxSize]) => {
|
|
||||||
this.setState({
|
|
||||||
sliderMinSize,
|
|
||||||
sliderMaxSize,
|
|
||||||
sliderPreferredSize
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onSizeChange({
|
|
||||||
minSize: roundNumber(Math.pow(sliderMinSize, 1.1)),
|
|
||||||
preferredSize: sliderPreferredSize === (slider.max - 3) ? null : roundNumber(Math.pow(sliderPreferredSize, 1.1)),
|
|
||||||
maxSize: sliderMaxSize === slider.max ? null : roundNumber(Math.pow(sliderMaxSize, 1.1))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onAfterSliderChange = () => {
|
|
||||||
const {
|
|
||||||
minSize,
|
|
||||||
maxSize,
|
|
||||||
preferredSize
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
sliderMiSize: getSliderValue(minSize, slider.min),
|
|
||||||
sliderMaxSize: getSliderValue(maxSize, slider.max),
|
|
||||||
sliderPreferredSize: getSliderValue(preferredSize, (slider.max - 3)) // fix
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMinSizeChange = ({ value }) => {
|
|
||||||
const minSize = getValue(value);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
sliderMinSize: getSliderValue(minSize, slider.min)
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onSizeChange({
|
|
||||||
minSize,
|
|
||||||
maxSize: this.props.maxSize,
|
|
||||||
preferredSize: this.props.preferredSize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onPreferredSizeChange = ({ value }) => {
|
|
||||||
const preferredSize = value === (MAX - 3) ? null : getValue(value);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
sliderPreferredSize: getSliderValue(preferredSize, slider.preferred)
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onSizeChange({
|
|
||||||
minSize: this.props.minSize,
|
|
||||||
maxSize: this.props.maxSize,
|
|
||||||
preferredSize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMaxSizeChange = ({ value }) => {
|
|
||||||
const maxSize = value === MAX ? null : getValue(value);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
sliderMaxSize: getSliderValue(maxSize, slider.max)
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onSizeChange({
|
|
||||||
minSize: this.props.minSize,
|
|
||||||
maxSize,
|
|
||||||
preferredSize: this.props.preferredSize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
quality,
|
|
||||||
title,
|
|
||||||
minSize,
|
|
||||||
maxSize,
|
|
||||||
preferredSize,
|
|
||||||
advancedSettings,
|
|
||||||
onTitleChange
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
sliderMinSize,
|
|
||||||
sliderMaxSize,
|
|
||||||
sliderPreferredSize
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const minBytes = minSize * 1024 * 1024;
|
|
||||||
const minSixty = `${formatBytes(minBytes * 60)}/h`;
|
|
||||||
|
|
||||||
const preferredBytes = preferredSize * 1024 * 1024;
|
|
||||||
const preferredSixty = preferredBytes ? `${formatBytes(preferredBytes * 60)}/h` : 'Unlimited';
|
|
||||||
|
|
||||||
const maxBytes = maxSize && maxSize * 1024 * 1024;
|
|
||||||
const maxSixty = maxBytes ? `${formatBytes(maxBytes * 60)}/h` : 'Unlimited';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.qualityDefinition}>
|
|
||||||
<div className={styles.quality}>
|
|
||||||
{quality.name}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.title}>
|
|
||||||
<TextInput
|
|
||||||
name={`${id}.${title}`}
|
|
||||||
value={title}
|
|
||||||
onChange={onTitleChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.sizeLimit}>
|
|
||||||
<ReactSlider
|
|
||||||
min={slider.min}
|
|
||||||
max={slider.max}
|
|
||||||
step={slider.step}
|
|
||||||
minDistance={3}
|
|
||||||
value={[sliderMinSize, sliderPreferredSize, sliderMaxSize]}
|
|
||||||
withTracks={true}
|
|
||||||
allowCross={false}
|
|
||||||
snapDragDisabled={true}
|
|
||||||
className={styles.slider}
|
|
||||||
trackClassName={styles.bar}
|
|
||||||
thumbClassName={styles.handle}
|
|
||||||
onChange={this.onSliderChange}
|
|
||||||
onAfterChange={this.onAfterSliderChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.sizes}>
|
|
||||||
<div>
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Label kind={kinds.INFO}>{minSixty}</Label>
|
|
||||||
}
|
|
||||||
title={translate('MinimumLimits')}
|
|
||||||
body={
|
|
||||||
<QualityDefinitionLimits
|
|
||||||
bytes={minBytes}
|
|
||||||
message={translate('NoMinimumForAnyRuntime')}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Label kind={kinds.SUCCESS}>{preferredSixty}</Label>
|
|
||||||
}
|
|
||||||
title={translate('PreferredSize')}
|
|
||||||
body={
|
|
||||||
<QualityDefinitionLimits
|
|
||||||
bytes={preferredBytes}
|
|
||||||
message={translate('NoLimitForAnyRuntime')}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Label kind={kinds.WARNING}>{maxSixty}</Label>
|
|
||||||
}
|
|
||||||
title={translate('MaximumLimits')}
|
|
||||||
body={
|
|
||||||
<QualityDefinitionLimits
|
|
||||||
bytes={maxBytes}
|
|
||||||
message={translate('NoLimitForAnyRuntime')}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
advancedSettings &&
|
|
||||||
<div className={styles.megabytesPerMinute}>
|
|
||||||
<div>
|
|
||||||
Min
|
|
||||||
|
|
||||||
<NumberInput
|
|
||||||
className={styles.sizeInput}
|
|
||||||
name={`${id}.min`}
|
|
||||||
value={minSize || MIN}
|
|
||||||
min={MIN}
|
|
||||||
max={preferredSize ? preferredSize - 5 : MAX - 5}
|
|
||||||
step={0.1}
|
|
||||||
isFloat={true}
|
|
||||||
onChange={this.onMinSizeChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Preferred
|
|
||||||
|
|
||||||
<NumberInput
|
|
||||||
className={styles.sizeInput}
|
|
||||||
name={`${id}.min`}
|
|
||||||
value={preferredSize || MAX - 5}
|
|
||||||
min={MIN}
|
|
||||||
max={maxSize ? maxSize - 5 : MAX - 5}
|
|
||||||
step={0.1}
|
|
||||||
isFloat={true}
|
|
||||||
onChange={this.onPreferredSizeChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Max
|
|
||||||
|
|
||||||
<NumberInput
|
|
||||||
className={styles.sizeInput}
|
|
||||||
name={`${id}.min`}
|
|
||||||
value={maxSize || MAX}
|
|
||||||
min={minSize + 5}
|
|
||||||
max={MAX}
|
|
||||||
step={0.1}
|
|
||||||
isFloat={true}
|
|
||||||
onChange={this.onMaxSizeChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QualityDefinition.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
quality: PropTypes.object.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
minSize: PropTypes.number,
|
|
||||||
maxSize: PropTypes.number,
|
|
||||||
preferredSize: PropTypes.number,
|
|
||||||
advancedSettings: PropTypes.bool.isRequired,
|
|
||||||
onTitleChange: PropTypes.func.isRequired,
|
|
||||||
onSizeChange: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QualityDefinition;
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
|
||||||
import { setQualityDefinitionValue } from 'Store/Actions/settingsActions';
|
|
||||||
import QualityDefinition from './QualityDefinition';
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
setQualityDefinitionValue,
|
|
||||||
clearPendingChanges
|
|
||||||
};
|
|
||||||
|
|
||||||
class QualityDefinitionConnector extends Component {
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.props.clearPendingChanges({ section: 'settings.qualityDefinitions' });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onTitleChange = ({ value }) => {
|
|
||||||
this.props.setQualityDefinitionValue({ id: this.props.id, name: 'title', value });
|
|
||||||
}
|
|
||||||
|
|
||||||
onSizeChange = ({ minSize, maxSize, preferredSize }) => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
minSize: currentMinSize,
|
|
||||||
maxSize: currentMaxSize,
|
|
||||||
preferredSize: currentPreferredSize
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (minSize !== currentMinSize) {
|
|
||||||
this.props.setQualityDefinitionValue({ id, name: 'minSize', value: minSize });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxSize !== currentMaxSize) {
|
|
||||||
this.props.setQualityDefinitionValue({ id, name: 'maxSize', value: maxSize });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preferredSize !== currentPreferredSize) {
|
|
||||||
this.props.setQualityDefinitionValue({ id, name: 'preferredSize', value: preferredSize });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<QualityDefinition
|
|
||||||
{...this.props}
|
|
||||||
onTitleChange={this.onTitleChange}
|
|
||||||
onSizeChange={this.onSizeChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QualityDefinitionConnector.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
minSize: PropTypes.number,
|
|
||||||
maxSize: PropTypes.number,
|
|
||||||
preferredSize: PropTypes.number,
|
|
||||||
setQualityDefinitionValue: PropTypes.func.isRequired,
|
|
||||||
clearPendingChanges: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(QualityDefinitionConnector);
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
function QualityDefinitionLimits(props) {
|
|
||||||
const {
|
|
||||||
bytes,
|
|
||||||
message
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!bytes) {
|
|
||||||
return <div>{message}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sixty = formatBytes(bytes * 60);
|
|
||||||
const ninety = formatBytes(bytes * 90);
|
|
||||||
const hundredTwenty = formatBytes(bytes * 120);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
{translate('MinutesSixty', [sixty])}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{translate('MinutesNinety', [ninety])}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{translate('MinutesHundredTwenty', [hundredTwenty])}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
QualityDefinitionLimits.propTypes = {
|
|
||||||
bytes: PropTypes.number,
|
|
||||||
message: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QualityDefinitionLimits;
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality,
|
|
||||||
.title {
|
|
||||||
flex: 0 1 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sizeLimit {
|
|
||||||
flex: 0 1 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.megabytesPerMinute {
|
|
||||||
flex: 0 0 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sizeLimitHelpTextContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
margin-top: 20px;
|
|
||||||
max-width: 1000px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sizeLimitHelpText {
|
|
||||||
max-width: 500px;
|
|
||||||
color: $helpTextColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointSmall) {
|
|
||||||
.header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.definitions {
|
|
||||||
&:first-child {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import FieldSet from 'Components/FieldSet';
|
|
||||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import QualityDefinitionConnector from './QualityDefinitionConnector';
|
|
||||||
import styles from './QualityDefinitions.css';
|
|
||||||
|
|
||||||
class QualityDefinitions extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
items,
|
|
||||||
advancedSettings,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldSet legend={translate('QualityDefinitions')}>
|
|
||||||
<PageSectionContent
|
|
||||||
errorMessage={translate('UnableToLoadQualityDefinitions')}
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
<div className={styles.header}>
|
|
||||||
<div className={styles.quality}>Quality</div>
|
|
||||||
<div className={styles.title}>Title</div>
|
|
||||||
<div className={styles.sizeLimit}>Size Limit</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
advancedSettings ?
|
|
||||||
<div className={styles.megabytesPerMinute}>
|
|
||||||
Megabytes Per Minute
|
|
||||||
</div> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.definitions}>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<QualityDefinitionConnector
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
advancedSettings={advancedSettings}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.sizeLimitHelpTextContainer}>
|
|
||||||
<div className={styles.sizeLimitHelpText}>
|
|
||||||
Limits are automatically adjusted for the movie runtime.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PageSectionContent>
|
|
||||||
</FieldSet>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QualityDefinitions.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
defaultProfile: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
advancedSettings: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QualityDefinitions;
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { fetchQualityDefinitions, saveQualityDefinitions } from 'Store/Actions/settingsActions';
|
|
||||||
import QualityDefinitions from './QualityDefinitions';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.settings.qualityDefinitions,
|
|
||||||
(state) => state.settings.advancedSettings,
|
|
||||||
(qualityDefinitions, advancedSettings) => {
|
|
||||||
const items = qualityDefinitions.items.map((item) => {
|
|
||||||
const pendingChanges = qualityDefinitions.pendingChanges[item.id] || {};
|
|
||||||
|
|
||||||
return Object.assign({}, item, pendingChanges);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
...qualityDefinitions,
|
|
||||||
items,
|
|
||||||
hasPendingChanges: !_.isEmpty(qualityDefinitions.pendingChanges),
|
|
||||||
advancedSettings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
dispatchFetchQualityDefinitions: fetchQualityDefinitions,
|
|
||||||
dispatchSaveQualityDefinitions: saveQualityDefinitions
|
|
||||||
};
|
|
||||||
|
|
||||||
class QualityDefinitionsConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.dispatchFetchQualityDefinitions();
|
|
||||||
|
|
||||||
const {
|
|
||||||
dispatchFetchQualityDefinitions,
|
|
||||||
dispatchSaveQualityDefinitions,
|
|
||||||
onChildMounted
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
dispatchFetchQualityDefinitions();
|
|
||||||
onChildMounted(dispatchSaveQualityDefinitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
const {
|
|
||||||
hasPendingChanges,
|
|
||||||
isSaving,
|
|
||||||
onChildStateChange
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (
|
|
||||||
prevProps.isSaving !== isSaving ||
|
|
||||||
prevProps.hasPendingChanges !== hasPendingChanges
|
|
||||||
) {
|
|
||||||
onChildStateChange({
|
|
||||||
isSaving,
|
|
||||||
hasPendingChanges
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<QualityDefinitions
|
|
||||||
{...this.props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QualityDefinitionsConnector.propTypes = {
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
hasPendingChanges: PropTypes.bool.isRequired,
|
|
||||||
dispatchFetchQualityDefinitions: PropTypes.func.isRequired,
|
|
||||||
dispatchSaveQualityDefinitions: PropTypes.func.isRequired,
|
|
||||||
onChildMounted: PropTypes.func.isRequired,
|
|
||||||
onChildStateChange: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps, null)(QualityDefinitionsConnector);
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
import React, { Component } from 'react';
|
|
||||||
import PageContent from 'Components/Page/PageContent';
|
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
|
||||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import QualityDefinitionsConnector from './Definition/QualityDefinitionsConnector';
|
|
||||||
|
|
||||||
class Quality extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this._saveCallback = null;
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isSaving: false,
|
|
||||||
hasPendingChanges: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onChildMounted = (saveCallback) => {
|
|
||||||
this._saveCallback = saveCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
onChildStateChange = (payload) => {
|
|
||||||
this.setState(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
if (this._saveCallback) {
|
|
||||||
this._saveCallback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isSaving,
|
|
||||||
hasPendingChanges
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PageContent title={translate('QualitySettings')}>
|
|
||||||
<SettingsToolbarConnector
|
|
||||||
isSaving={isSaving}
|
|
||||||
hasPendingChanges={hasPendingChanges}
|
|
||||||
onSavePress={this.onSavePress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PageContentBody>
|
|
||||||
<QualityDefinitionsConnector
|
|
||||||
onChildMounted={this.onChildMounted}
|
|
||||||
onChildStateChange={this.onChildStateChange}
|
|
||||||
/>
|
|
||||||
</PageContentBody>
|
|
||||||
</PageContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Quality;
|
|
||||||
|
|
@ -7,7 +7,6 @@ import * as indexers from './indexerActions';
|
||||||
import * as indexerIndex from './indexerIndexActions';
|
import * as indexerIndex from './indexerIndexActions';
|
||||||
import * as indexerStats from './indexerStatsActions';
|
import * as indexerStats from './indexerStatsActions';
|
||||||
import * as indexerStatus from './indexerStatusActions';
|
import * as indexerStatus from './indexerStatusActions';
|
||||||
import * as movies from './movieActions';
|
|
||||||
import * as oAuth from './oAuthActions';
|
import * as oAuth from './oAuthActions';
|
||||||
import * as paths from './pathActions';
|
import * as paths from './pathActions';
|
||||||
import * as providerOptions from './providerOptionActions';
|
import * as providerOptions from './providerOptionActions';
|
||||||
|
|
@ -26,7 +25,6 @@ export default [
|
||||||
paths,
|
paths,
|
||||||
providerOptions,
|
providerOptions,
|
||||||
releases,
|
releases,
|
||||||
movies,
|
|
||||||
indexers,
|
indexers,
|
||||||
indexerIndex,
|
indexerIndex,
|
||||||
indexerStats,
|
indexerStats,
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,10 @@ import createTestProviderHandler, { createCancelTestProviderHandler } from 'Stor
|
||||||
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
|
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
|
||||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||||
import { createThunk, handleThunks } from 'Store/thunks';
|
import { createThunk, handleThunks } from 'Store/thunks';
|
||||||
|
import dateFilterPredicate from 'Utilities/Date/dateFilterPredicate';
|
||||||
import getSectionState from 'Utilities/State/getSectionState';
|
import getSectionState from 'Utilities/State/getSectionState';
|
||||||
import updateSectionState from 'Utilities/State/updateSectionState';
|
import updateSectionState from 'Utilities/State/updateSectionState';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
import createHandleActions from './Creators/createHandleActions';
|
import createHandleActions from './Creators/createHandleActions';
|
||||||
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
||||||
|
|
||||||
|
|
@ -45,6 +47,22 @@ export const defaultState = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const filters = [
|
||||||
|
{
|
||||||
|
key: 'all',
|
||||||
|
label: translate('All'),
|
||||||
|
filters: []
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const filterPredicates = {
|
||||||
|
added: function(item, filterValue, type) {
|
||||||
|
return dateFilterPredicate(item.added, filterValue, type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sortPredicates = {};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Actions Types
|
// Actions Types
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import createHandleActions from './Creators/createHandleActions';
|
||||||
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
|
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
|
||||||
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
||||||
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
|
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
|
||||||
import { filterPredicates, filters, sortPredicates } from './movieActions';
|
import { filterPredicates, filters, sortPredicates } from './indexerActions';
|
||||||
|
|
||||||
//
|
//
|
||||||
// Variables
|
// Variables
|
||||||
|
|
|
||||||
|
|
@ -1,193 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import { createAction } from 'redux-actions';
|
|
||||||
import { sortDirections } from 'Helpers/Props';
|
|
||||||
import { createThunk, handleThunks } from 'Store/thunks';
|
|
||||||
// import { batchActions } from 'redux-batched-actions';
|
|
||||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
|
||||||
import dateFilterPredicate from 'Utilities/Date/dateFilterPredicate';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import { updateItem } from './baseActions';
|
|
||||||
import createFetchHandler from './Creators/createFetchHandler';
|
|
||||||
import createHandleActions from './Creators/createHandleActions';
|
|
||||||
import createRemoveItemHandler from './Creators/createRemoveItemHandler';
|
|
||||||
import createSaveProviderHandler from './Creators/createSaveProviderHandler';
|
|
||||||
import createSetSettingValueReducer from './Creators/Reducers/createSetSettingValueReducer';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Variables
|
|
||||||
|
|
||||||
export const section = 'movies';
|
|
||||||
|
|
||||||
export const filters = [
|
|
||||||
{
|
|
||||||
key: 'all',
|
|
||||||
label: translate('All'),
|
|
||||||
filters: []
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const filterPredicates = {
|
|
||||||
added: function(item, filterValue, type) {
|
|
||||||
return dateFilterPredicate(item.added, filterValue, type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sortPredicates = {
|
|
||||||
status: function(item) {
|
|
||||||
let result = 0;
|
|
||||||
|
|
||||||
if (item.monitored) {
|
|
||||||
result += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.status === 'announced') {
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.status === 'inCinemas') {
|
|
||||||
result += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.status === 'released') {
|
|
||||||
result += 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// State
|
|
||||||
|
|
||||||
export const defaultState = {
|
|
||||||
isFetching: false,
|
|
||||||
isPopulated: false,
|
|
||||||
error: null,
|
|
||||||
isSaving: false,
|
|
||||||
saveError: null,
|
|
||||||
items: [],
|
|
||||||
sortKey: 'name',
|
|
||||||
sortDirection: sortDirections.ASCENDING,
|
|
||||||
pendingChanges: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Actions Types
|
|
||||||
|
|
||||||
export const FETCH_MOVIES = 'movies/fetchMovies';
|
|
||||||
export const SET_MOVIE_VALUE = 'movies/setMovieValue';
|
|
||||||
export const SAVE_MOVIE = 'movies/saveMovie';
|
|
||||||
export const DELETE_MOVIE = 'movies/deleteMovie';
|
|
||||||
|
|
||||||
export const TOGGLE_MOVIE_MONITORED = 'movies/toggleMovieMonitored';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Action Creators
|
|
||||||
|
|
||||||
export const fetchMovies = createThunk(FETCH_MOVIES);
|
|
||||||
export const saveMovie = createThunk(SAVE_MOVIE, (payload) => {
|
|
||||||
const newPayload = {
|
|
||||||
...payload
|
|
||||||
};
|
|
||||||
|
|
||||||
if (payload.moveFiles) {
|
|
||||||
newPayload.queryParams = {
|
|
||||||
moveFiles: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
delete newPayload.moveFiles;
|
|
||||||
|
|
||||||
return newPayload;
|
|
||||||
});
|
|
||||||
|
|
||||||
export const deleteMovie = createThunk(DELETE_MOVIE, (payload) => {
|
|
||||||
return {
|
|
||||||
...payload,
|
|
||||||
queryParams: {
|
|
||||||
deleteFiles: payload.deleteFiles
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const toggleMovieMonitored = createThunk(TOGGLE_MOVIE_MONITORED);
|
|
||||||
|
|
||||||
export const setMovieValue = createAction(SET_MOVIE_VALUE, (payload) => {
|
|
||||||
return {
|
|
||||||
section,
|
|
||||||
...payload
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// Helpers
|
|
||||||
|
|
||||||
function getSaveAjaxOptions({ ajaxOptions, payload }) {
|
|
||||||
if (payload.moveFolder) {
|
|
||||||
ajaxOptions.url = `${ajaxOptions.url}?moveFolder=true`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ajaxOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Action Handlers
|
|
||||||
|
|
||||||
export const actionHandlers = handleThunks({
|
|
||||||
|
|
||||||
[FETCH_MOVIES]: createFetchHandler(section, '/movie'),
|
|
||||||
[SAVE_MOVIE]: createSaveProviderHandler(section, '/movie', { getAjaxOptions: getSaveAjaxOptions }),
|
|
||||||
[DELETE_MOVIE]: createRemoveItemHandler(section, '/movie'),
|
|
||||||
|
|
||||||
[TOGGLE_MOVIE_MONITORED]: (getState, payload, dispatch) => {
|
|
||||||
const {
|
|
||||||
movieId: id,
|
|
||||||
monitored
|
|
||||||
} = payload;
|
|
||||||
|
|
||||||
const movie = _.find(getState().movies.items, { id });
|
|
||||||
|
|
||||||
dispatch(updateItem({
|
|
||||||
id,
|
|
||||||
section,
|
|
||||||
isSaving: true
|
|
||||||
}));
|
|
||||||
|
|
||||||
const promise = createAjaxRequest({
|
|
||||||
url: `/movie/${id}`,
|
|
||||||
method: 'PUT',
|
|
||||||
data: JSON.stringify({
|
|
||||||
...movie,
|
|
||||||
monitored
|
|
||||||
}),
|
|
||||||
dataType: 'json'
|
|
||||||
}).request;
|
|
||||||
|
|
||||||
promise.done((data) => {
|
|
||||||
dispatch(updateItem({
|
|
||||||
id,
|
|
||||||
section,
|
|
||||||
isSaving: false,
|
|
||||||
monitored
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.fail((xhr) => {
|
|
||||||
dispatch(updateItem({
|
|
||||||
id,
|
|
||||||
section,
|
|
||||||
isSaving: false
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// Reducers
|
|
||||||
|
|
||||||
export const reducers = createHandleActions({
|
|
||||||
|
|
||||||
[SET_MOVIE_VALUE]: createSetSettingValueReducer(section)
|
|
||||||
|
|
||||||
}, defaultState, section);
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createAllMoviesSelector from './createAllMoviesSelector';
|
|
||||||
|
|
||||||
function createExistingMovieSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state, { tmdbId }) => tmdbId,
|
|
||||||
createAllMoviesSelector(),
|
|
||||||
(tmdbId, movies) => {
|
|
||||||
return _.some(movies, { tmdbId });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createExistingMovieSelector;
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createAllMoviesSelector from './createAllMoviesSelector';
|
|
||||||
|
|
||||||
function createImportMovieItemSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state, { id }) => id,
|
|
||||||
(state) => state.addMovie,
|
|
||||||
(state) => state.importMovie,
|
|
||||||
createAllMoviesSelector(),
|
|
||||||
(id, addMovie, importMovie, movies) => {
|
|
||||||
const item = _.find(importMovie.items, { id }) || {};
|
|
||||||
const selectedMovie = item && item.selectedMovie;
|
|
||||||
const isExistingMovie = !!selectedMovie && _.some(movies, { tmdbId: selectedMovie.tmdbId });
|
|
||||||
|
|
||||||
return {
|
|
||||||
defaultMonitor: addMovie.defaults.monitor,
|
|
||||||
defaultQualityProfileId: addMovie.defaults.qualityProfileId,
|
|
||||||
...item,
|
|
||||||
isExistingMovie
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createImportMovieItemSelector;
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createAllMoviesSelector from './createAllMoviesSelector';
|
|
||||||
|
|
||||||
function createMovieCountSelector() {
|
|
||||||
return createSelector(
|
|
||||||
createAllMoviesSelector(),
|
|
||||||
(state) => state.movies.error,
|
|
||||||
(state) => state.movies.isFetching,
|
|
||||||
(state) => state.movies.isPopulated,
|
|
||||||
(movies, error, isFetching, isPopulated) => {
|
|
||||||
return {
|
|
||||||
count: movies.length,
|
|
||||||
error,
|
|
||||||
isFetching,
|
|
||||||
isPopulated
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createMovieCountSelector;
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
function formatRuntime(minutes, format) {
|
|
||||||
if (!minutes) {
|
|
||||||
return (format === 'hoursMinutes') ? '0m' : '0 mins';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format === 'minutes') {
|
|
||||||
return `${minutes} mins`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const movieHours = Math.floor(minutes / 60);
|
|
||||||
const movieMinutes = (minutes <= 59) ? minutes : minutes % 60;
|
|
||||||
return `${((movieHours > 0) ? `${movieHours}h ` : '') + movieMinutes}m`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default formatRuntime;
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import _ from 'lodash';
|
|
||||||
import { update } from 'Store/Actions/baseActions';
|
|
||||||
|
|
||||||
function updateEpisodes(section, episodes, episodeIds, options) {
|
|
||||||
const data = _.reduce(episodes, (result, item) => {
|
|
||||||
if (episodeIds.indexOf(item.id) > -1) {
|
|
||||||
result.push({
|
|
||||||
...item,
|
|
||||||
...options
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
result.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return update({ section, data });
|
|
||||||
}
|
|
||||||
|
|
||||||
export default updateEpisodes;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue