1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-06 13:31:28 -05:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Qstick
74382d7250 New: Rework List Exclusion UI 2022-07-05 23:10:47 -05:00
Qstick
6659bc034c Remove general yarn restore key to avoid cross OS conflict 2022-07-05 17:15:52 -05:00
1127 changed files with 9322 additions and 40662 deletions

View File

@@ -19,10 +19,10 @@ indent_size = 4
dotnet_sort_system_directives_first = true
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:refactoring
dotnet_style_qualification_for_property = false:refactoring
dotnet_style_qualification_for_method = false:refactoring
dotnet_style_qualification_for_event = false:refactoring
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
# Indentation preferences
csharp_indent_block_contents = true
@@ -32,10 +32,6 @@ csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _
@@ -46,6 +42,7 @@ csharp_style_var_elsewhere = true:suggestion
# Stylecop Rules
dotnet_diagnostic.SA0001.severity = none
dotnet_diagnostic.SA1005.severity = none
dotnet_diagnostic.SA1025.severity = none
dotnet_diagnostic.SA1101.severity = none
dotnet_diagnostic.SA1116.severity = none
@@ -68,7 +65,6 @@ dotnet_diagnostic.SA1406.severity = suggestion
dotnet_diagnostic.SA1410.severity = suggestion
dotnet_diagnostic.SA1411.severity = suggestion
dotnet_diagnostic.SA1413.severity = none
dotnet_diagnostic.SA1512.severity = none
dotnet_diagnostic.SA1516.severity = none
dotnet_diagnostic.SA1600.severity = none
dotnet_diagnostic.SA1601.severity = none
@@ -167,7 +163,6 @@ dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = suggestion
dotnet_diagnostic.CA1401.severity = suggestion
dotnet_diagnostic.CA1416.severity = suggestion
dotnet_diagnostic.CA1419.severity = suggestion
dotnet_diagnostic.CA1507.severity = suggestion
dotnet_diagnostic.CA1508.severity = suggestion
dotnet_diagnostic.CA1707.severity = suggestion
@@ -183,6 +178,9 @@ dotnet_diagnostic.CA1720.severity = suggestion
dotnet_diagnostic.CA1721.severity = suggestion
dotnet_diagnostic.CA1724.severity = suggestion
dotnet_diagnostic.CA1725.severity = suggestion
dotnet_diagnostic.CA1801.severity = suggestion
dotnet_diagnostic.CA1802.severity = suggestion
dotnet_diagnostic.CA1805.severity = suggestion
dotnet_diagnostic.CA1806.severity = suggestion
dotnet_diagnostic.CA1810.severity = suggestion
dotnet_diagnostic.CA1812.severity = suggestion
@@ -194,11 +192,13 @@ dotnet_diagnostic.CA1819.severity = suggestion
dotnet_diagnostic.CA1822.severity = suggestion
dotnet_diagnostic.CA1823.severity = suggestion
dotnet_diagnostic.CA1824.severity = suggestion
dotnet_diagnostic.CA1848.severity = suggestion
dotnet_diagnostic.CA2000.severity = suggestion
dotnet_diagnostic.CA2002.severity = suggestion
dotnet_diagnostic.CA2007.severity = suggestion
dotnet_diagnostic.CA2008.severity = suggestion
dotnet_diagnostic.CA2009.severity = suggestion
dotnet_diagnostic.CA2010.severity = suggestion
dotnet_diagnostic.CA2011.severity = suggestion
dotnet_diagnostic.CA2012.severity = suggestion
dotnet_diagnostic.CA2013.severity = suggestion
dotnet_diagnostic.CA2100.severity = suggestion
@@ -229,9 +229,6 @@ dotnet_diagnostic.CA2243.severity = suggestion
dotnet_diagnostic.CA2244.severity = suggestion
dotnet_diagnostic.CA2245.severity = suggestion
dotnet_diagnostic.CA2246.severity = suggestion
dotnet_diagnostic.CA2249.severity = suggestion
dotnet_diagnostic.CA2251.severity = suggestion
dotnet_diagnostic.CA2254.severity = suggestion
dotnet_diagnostic.CA3061.severity = suggestion
dotnet_diagnostic.CA3075.severity = suggestion
dotnet_diagnostic.CA3076.severity = suggestion
@@ -259,7 +256,7 @@ dotnet_diagnostic.CA5392.severity = suggestion
dotnet_diagnostic.CA5394.severity = suggestion
dotnet_diagnostic.CA5397.severity = suggestion
dotnet_diagnostic.SYSLIB0006.severity = none
[*.{js,html,js,hbs,less,css}]
charset = utf-8

10
.esprintrc Normal file
View File

@@ -0,0 +1,10 @@
{
"paths": [
"frontend/src/**/*.js",
"src/NzbDrone.Core/Localization/Core/*.json"
],
"ignored": [
"**/node_modules/**/*"
],
"port": 5004
}

2
.github/FUNDING.yml vendored
View File

@@ -1,6 +1,6 @@
# These are supported funding model platforms
github: radarr
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: radarr
ko_fi: # Replace with a single Ko-fi username

View File

@@ -5,9 +5,9 @@ body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the bug you encountered. If a bug exists and is closed note that it may only be fixed in an unstable branch.
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing open and closed issues
- label: I have searched the existing issues
required: true
- type: textarea
attributes:
@@ -42,14 +42,12 @@ body:
- **Docker Install**: Yes
- **Using Reverse Proxy**: No
- **Browser**: Firefox 90 (If UI related)
- **Database**: Sqlite 3.36.0
value: |
- OS:
- Radarr:
- Docker Install:
- Using Reverse Proxy:
- Browser:
- Database:
- Radarr:
- Docker Install:
- Using Reverse Proxy:
- Browser:
render: markdown
validations:
required: true

View File

@@ -5,9 +5,9 @@ body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the feature you are requesting. If a request exists and is closed note that it may only be fixed in an unstable branch.
description: Please search to see if an issue already exists for the feature you are requesting.
options:
- label: I have searched the existing open and closed issues
- label: I have searched the existing issues
required: true
- type: textarea
attributes:

41
.github/workflows/azuresync.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Sync issue to Azure DevOps work item
on:
issues:
types:
[opened, edited, deleted, closed, reopened, labeled, unlabeled, assigned]
concurrency: azuresync-${{ github.event.issue.number }}
jobs:
alert:
runs-on: ubuntu-latest
steps:
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == true }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Radarr"
ado_wit: "Bug"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == false }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Radarr"
ado_wit: "User Story"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100

View File

@@ -5,13 +5,8 @@ on:
schedule:
- cron: '0 0 * * *'
permissions: {}
jobs:
lock:
permissions:
issues: write # to lock issues (dessant/lock-threads)
pull-requests: write # to lock PRs (dessant/lock-threads)
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2

View File

@@ -4,12 +4,8 @@ on:
issues:
types: [labeled, unlabeled, reopened]
permissions: {}
jobs:
support:
permissions:
issues: write # to modify issues
runs-on: ubuntu-latest
steps:
- uses: dessant/support-requests@v2

23
.gitignore vendored
View File

@@ -166,8 +166,27 @@ packages.config.md5sum
# Common IntelliJ Platform excludes
# Ignore Rider projects completely for now
.idea/
# User specific
**/.idea/**/workspace.xml
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries
**/.idea/.idea.Radarr.Posix
**/.idea/.idea.Radarr.Windows
# Sensitive or high-churn files
**/.idea/**/dataSources/
**/.idea/**/dataSources.ids
**/.idea/**/dataSources.xml
**/.idea/**/dataSources.local.xml
**/.idea/**/sqlDataSources.xml
**/.idea/**/dynamic.xml
# Rider
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
# ignore node_modules symlink
node_modules

View File

@@ -1,132 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<development@radarr.video>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View File

@@ -76,15 +76,6 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrai
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
* [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
## DigitalOcean
This project is also supported by DigitalOcean
<p>
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
### License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)

View File

@@ -1,8 +0,0 @@
# Security Policy
## Reporting a Vulnerability
Please report (suspected) security vulnerabilities on Discord (preferred) to
any of the Servarr Dev role holders (red names) or via email: development@servarr.com. You will receive a response from
us within 72 hours. If the issue is confirmed, we will release a patch as soon
as possible depending on complexity/severity.

View File

@@ -9,13 +9,13 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '5.0.0'
majorVersion: '4.2.0'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.400'
dotnetVersion: '6.0.300'
nodeVersion: '16.X'
innoVersion: '6.2.0'
windowsImage: 'windows-2022'
@@ -27,7 +27,6 @@ trigger:
include:
- develop
- master
- zeus
pr:
branches:
@@ -550,7 +549,7 @@ stages:
Radarr__Postgres__Password: 'radarr'
pool:
vmImage: ${{ variables.linuxImage }}
vmImage: 'ubuntu-18.04'
timeoutInMinutes: 10
@@ -577,7 +576,6 @@ stages:
-e POSTGRES_PASSWORD=radarr \
-e POSTGRES_USER=radarr \
-p 5432:5432/tcp \
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
postgres:14
displayName: Start postgres
- bash: |
@@ -688,7 +686,7 @@ stages:
Radarr__Postgres__Password: 'radarr'
pool:
vmImage: ${{ variables.linuxImage }}
vmImage: 'ubuntu-18.04'
steps:
- task: UseDotNet@2
@@ -723,7 +721,6 @@ stages:
-e POSTGRES_PASSWORD=radarr \
-e POSTGRES_USER=radarr \
-p 5432:5432/tcp \
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
postgres:14
displayName: Start postgres
- bash: |
@@ -762,7 +759,7 @@ stages:
inputs:
buildType: 'current'
artifactName: Packages
itemPattern: '**/$(pattern)'
itemPattern: '/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory)
- bash: |
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
@@ -1093,7 +1090,7 @@ stages:
projectVersion: '$(radarrVersion)'
extraProperties: |
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
sonar.coverage.exclusions=**/Radarr.Api.V*/**/*
sonar.coverage.exclusions=**/Radarr.Api.V3/**/*
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
- bash: |
@@ -1144,5 +1141,4 @@ stages:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
DISCORDCHANNELID: $(discordChannelId)
DISCORDWEBHOOKKEY: $(discordWebhookKey)
DISCORDTHREADID: $(discordThreadId)

View File

@@ -29,7 +29,7 @@ dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p
dotnet new tool-manifest
dotnet tool install --version 6.3.0 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Radarr.Api.V4/openapi.json "$outputFolder/net6.0/$RUNTIME/radarr.console.dll" v4 &
dotnet tool run swagger tofile --output ./src/Radarr.Api.V3/openapi.json "$outputFolder/net6.0/$RUNTIME/radarr.console.dll" v3 &
sleep 45

View File

@@ -39,7 +39,6 @@ module.exports = {
plugins: [
'filenames',
'react',
'react-hooks',
'simple-import-sort',
'import'
],
@@ -309,9 +308,7 @@ module.exports = {
'react/react-in-jsx-scope': 2,
'react/self-closing-comp': 2,
'react/sort-comp': 2,
'react/jsx-wrap-multilines': 2,
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error'
'react/jsx-wrap-multilines': 2
},
overrides: [
{

View File

@@ -223,6 +223,7 @@ module.exports = (env) => {
{
loader: 'url-loader',
options: {
limit: 24096,
mimetype: 'application/font-woff',
emitFile: false,
name: 'Content/Fonts/[name].[ext]'
@@ -232,11 +233,12 @@ module.exports = (env) => {
},
{
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
test: /\.(ttf|eot|eot?#iefix|gif|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'file-loader',
options: {
limit: 24096,
emitFile: false,
name: 'Content/Fonts/[name].[ext]'
}

View File

@@ -1,4 +1,3 @@
// eslint-disable-next-line filenames/match-exported
const loaderUtils = require('loader-utils');
module.exports = function cssVariablesLoader(source) {

View File

@@ -1,6 +1,7 @@
const reload = require('require-nocache')(module);
const cssVarsFiles = [
'./src/Styles/Variables/colors',
'./src/Styles/Variables/dimensions',
'./src/Styles/Variables/fonts',
'./src/Styles/Variables/animations',
@@ -28,4 +29,4 @@ module.exports = {
'postcss-color-function',
'postcss-nested'
]
};
};

View File

@@ -181,12 +181,13 @@ class Blocklist extends Component {
>
<TableBody>
{
items.map((item) => {
items.map((item, index) => {
return (
<BlocklistRowConnector
key={item.id}
isSelected={selectedState[item.id] || false}
columns={columns}
index={index}
{...item}
onSelectedChange={this.onSelectedChange}
/>

View File

@@ -10,12 +10,6 @@
width: 80px;
}
.customFormatScore {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 55px;
}
.releaseGroup {
composes: cell from '~Components/Table/Cells/TableRowCell.css';

View File

@@ -9,7 +9,6 @@ import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality';
import MovieTitleLink from 'Movie/MovieTitleLink';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import HistoryDetailsModal from './Details/HistoryDetailsModal';
import HistoryEventTypeCell from './HistoryEventTypeCell';
import styles from './HistoryRow.css';
@@ -56,7 +55,6 @@ class HistoryRow extends Component {
movie,
quality,
customFormats,
customFormatScore,
languages,
qualityCutoffNotMet,
eventType,
@@ -170,17 +168,6 @@ class HistoryRow extends Component {
);
}
if (name === 'customFormatScore') {
return (
<TableRowCell
key={name}
className={styles.customFormatScore}
>
{formatCustomFormatScore(customFormatScore)}
</TableRowCell>
);
}
if (name === 'releaseGroup') {
return (
<TableRowCell
@@ -242,9 +229,8 @@ HistoryRow.propTypes = {
movie: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
customFormatScore: PropTypes.number.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,

View File

@@ -1,13 +1,13 @@
.torrent {
composes: label from '~Components/Label.css';
border-color: var(--torrentColor);
background-color: var(--torrentColor);
border-color: $torrentColor;
background-color: $torrentColor;
}
.usenet {
composes: label from '~Components/Label.css';
border-color: var(--usenetColor);
background-color: var(--usenetColor);
border-color: $usenetColor;
background-color: $usenetColor;
}

View File

@@ -75,23 +75,13 @@ class Queue extends Component {
return;
}
const nextState = {};
if (prevProps.items !== items) {
nextState.items = items;
}
const selectedIds = this.getSelectedIds();
const isPendingSelected = _.some(this.props.items, (item) => {
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
});
if (isPendingSelected !== this.state.isPendingSelected) {
nextState.isPendingSelected = isPendingSelected;
}
if (!_.isEmpty(nextState)) {
this.setState(nextState);
this.setState({ isPendingSelected });
}
}
@@ -224,29 +214,26 @@ class Queue extends Component {
<PageContentBody>
{
isRefreshing && !isAllPopulated ?
<LoadingIndicator /> :
null
isRefreshing && !isAllPopulated &&
<LoadingIndicator />
}
{
!isRefreshing && hasError ?
!isRefreshing && hasError &&
<div>
{translate('FailedToLoadQueue')}
</div> :
null
</div>
}
{
isAllPopulated && !hasError && !items.length ?
isAllPopulated && !hasError && !items.length &&
<div>
{translate('QueueIsEmpty')}
</div> :
null
</div>
}
{
isAllPopulated && !hasError && !!items.length ?
isAllPopulated && !hasError && !!items.length &&
<div>
<Table
columns={columns}
@@ -281,8 +268,7 @@ class Queue extends Component {
isFetching={isRefreshing}
{...otherProps}
/>
</div> :
null
</div>
}
</PageContentBody>

View File

@@ -128,7 +128,6 @@ class QueueRow extends Component {
{
columns.map((column) => {
const {
name,
isVisible
@@ -235,16 +234,6 @@ class QueueRow extends Component {
);
}
if (name === 'year') {
return (
<TableRowCell key={name}>
{
movie ? movie.year : ''
}
</TableRowCell>
);
}
if (name === 'title') {
return (
<TableRowCell key={name}>
@@ -373,7 +362,6 @@ QueueRow.propTypes = {
estimatedCompletionTime: PropTypes.string,
timeleft: PropTypes.string,
size: PropTypes.number,
year: PropTypes.number,
sizeleft: PropTypes.number,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,

View File

@@ -6,12 +6,12 @@
.searchIconContainer {
width: 58px;
height: 46px;
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-right: none;
border-radius: 4px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background-color: var(--searchIconContainerBackgroundColor);
background-color: #edf1f2;
text-align: center;
line-height: 46px;
}
@@ -25,7 +25,7 @@
}
.clearLookupButton {
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-left: none;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;

View File

@@ -4,7 +4,7 @@
.year {
margin-left: 5px;
color: var(--disabledColor);
color: $disabledColor;
}
.poster {

View File

@@ -20,6 +20,10 @@ class AddNewMovieModalContent extends Component {
//
// Listeners
onQualityProfileIdChange = ({ value }) => {
this.props.onInputChange({ name: 'qualityProfileId', value: parseInt(value) });
};
onAddMoviePress = () => {
this.props.onAddMoviePress();
};
@@ -36,7 +40,7 @@ class AddNewMovieModalContent extends Component {
isAdding,
rootFolderPath,
monitor,
qualityProfileIds,
qualityProfileId,
minimumAvailability,
searchForMovie,
folder,
@@ -126,9 +130,9 @@ class AddNewMovieModalContent extends Component {
<FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT}
name="qualityProfileIds"
onChange={onInputChange}
{...qualityProfileIds}
name="qualityProfileId"
onChange={this.onQualityProfileIdChange}
{...qualityProfileId}
/>
</FormGroup>
@@ -185,7 +189,7 @@ AddNewMovieModalContent.propTypes = {
addError: PropTypes.object,
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
qualityProfileIds: PropTypes.arrayOf(PropTypes.object),
qualityProfileId: PropTypes.object,
minimumAvailability: PropTypes.object.isRequired,
searchForMovie: PropTypes.object.isRequired,
folder: PropTypes.string.isRequired,

View File

@@ -58,7 +58,7 @@ class AddNewMovieModalContentConnector extends Component {
tmdbId,
rootFolderPath,
monitor,
qualityProfileIds,
qualityProfileId,
minimumAvailability,
searchForMovie,
tags
@@ -68,7 +68,7 @@ class AddNewMovieModalContentConnector extends Component {
tmdbId,
rootFolderPath: rootFolderPath.value,
monitor: monitor.value,
qualityProfileIds: qualityProfileIds.value,
qualityProfileId: qualityProfileId.value,
minimumAvailability: minimumAvailability.value,
searchForMovie: searchForMovie.value,
tags: tags.value
@@ -93,7 +93,7 @@ AddNewMovieModalContentConnector.propTypes = {
tmdbId: PropTypes.number.isRequired,
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
qualityProfileIds: PropTypes.arrayOf(PropTypes.object),
qualityProfileId: PropTypes.object,
minimumAvailability: PropTypes.object.isRequired,
searchForMovie: PropTypes.object.isRequired,
tags: PropTypes.object.isRequired,

View File

@@ -9,15 +9,13 @@
.underlay {
@add-mixin cover;
background-color: var(--addMovieBackgroundColor);
background-color: $white;
transition: background 500ms;
&:hover {
background-color: var(--pageBackground);
box-shadow: 0 0 12px var(--black);
background-color: #eaf2ff;
color: inherit;
text-decoration: none;
transition: all 200ms ease-in;
}
}
@@ -33,7 +31,7 @@
display: block;
margin-right: 20px;
height: 250px;
background-color: var(--defaultColor);
background-color: $defaultColor;
}
.content {
@@ -58,7 +56,7 @@
.year {
margin-left: 10px;
color: var(--disabledColor);
color: $disabledColor;
}
.icons {
@@ -77,7 +75,7 @@
.exclusionIcon {
margin-left: 10px;
color: var(--dangerColor);
color: $dangerColor;
pointer-events: all;
}

View File

@@ -72,19 +72,15 @@ class AddNewMovieSearchResult extends Component {
colorImpairedMode,
id,
monitored,
hasFile,
isAvailable,
queueStatus,
queueState,
runtime,
movieRuntimeFormat,
certification,
statistics
certification
} = this.props;
const {
movieFileCount
} = statistics;
const {
isNewAddMovieModalOpen
} = this.state;
@@ -124,7 +120,7 @@ class AddNewMovieSearchResult extends Component {
isExistingMovie &&
<MovieIndexProgressBar
monitored={monitored}
hasFile={movieFileCount > 0}
hasFile={hasFile}
status={status}
posterWidth={posterWidth}
detailedProgressBar={true}
@@ -237,7 +233,7 @@ class AddNewMovieSearchResult extends Component {
{
isExistingMovie && isSmallScreen &&
<MovieStatusLabel
hasMovieFiles={movieFileCount > 0}
hasMovieFiles={hasFile}
monitored={monitored}
isAvailable={isAvailable}
id={id}
@@ -294,14 +290,7 @@ AddNewMovieSearchResult.propTypes = {
queueState: PropTypes.string,
runtime: PropTypes.number.isRequired,
movieRuntimeFormat: PropTypes.string.isRequired,
certification: PropTypes.string,
statistics: PropTypes.object
};
AddNewMovieSearchResult.defaultProps = {
statistics: {
movieFileCount: 0
}
certification: PropTypes.string
};
export default AddNewMovieSearchResult;

View File

@@ -25,13 +25,13 @@ class ImportMovieFooter extends Component {
const {
defaultMonitor,
defaultQualityProfileIds,
defaultQualityProfileId,
defaultMinimumAvailability
} = props;
this.state = {
monitor: defaultMonitor,
qualityProfileIds: defaultQualityProfileIds,
qualityProfileId: defaultQualityProfileId,
minimumAvailability: defaultMinimumAvailability
};
}
@@ -39,16 +39,16 @@ class ImportMovieFooter extends Component {
componentDidUpdate(prevProps, prevState) {
const {
defaultMonitor,
defaultQualityProfileIds,
defaultQualityProfileId,
defaultMinimumAvailability,
isMonitorMixed,
isQualityProfileIdsMixed,
isQualityProfileIdMixed,
isMinimumAvailabilityMixed
} = this.props;
const {
monitor,
qualityProfileIds,
qualityProfileId,
minimumAvailability
} = this.state;
@@ -60,10 +60,10 @@ class ImportMovieFooter extends Component {
newState.monitor = defaultMonitor;
}
if (isQualityProfileIdsMixed && qualityProfileIds !== MIXED) {
newState.qualityProfileIds = MIXED;
} else if (!isQualityProfileIdsMixed && qualityProfileIds !== defaultQualityProfileIds) {
newState.qualityProfileIds = defaultQualityProfileIds;
if (isQualityProfileIdMixed && qualityProfileId !== MIXED) {
newState.qualityProfileId = MIXED;
} else if (!isQualityProfileIdMixed && qualityProfileId !== defaultQualityProfileId) {
newState.qualityProfileId = defaultQualityProfileId;
}
if (isMinimumAvailabilityMixed && minimumAvailability !== MIXED) {
@@ -94,7 +94,7 @@ class ImportMovieFooter extends Component {
isImporting,
isLookingUpMovie,
isMonitorMixed,
isQualityProfileIdsMixed,
isQualityProfileIdMixed,
isMinimumAvailabilityMixed,
hasUnsearchedItems,
importError,
@@ -105,7 +105,7 @@ class ImportMovieFooter extends Component {
const {
monitor,
qualityProfileIds,
qualityProfileId,
minimumAvailability
} = this.state;
@@ -148,10 +148,10 @@ class ImportMovieFooter extends Component {
<FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT}
name="qualityProfileIds"
value={qualityProfileIds}
name="qualityProfileId"
value={qualityProfileId}
isDisabled={!selectedCount}
includeMixed={isQualityProfileIdsMixed}
includeMixed={isQualityProfileIdMixed}
onChange={this.onInputChange}
/>
</div>
@@ -225,19 +225,13 @@ class ImportMovieFooter extends Component {
body={
<ul>
{
Array.isArray(importError.responseJSON) ?
importError.responseJSON.map((error, index) => {
return (
<li key={index}>
{error.errorMessage}
</li>
);
}) :
<li>
{
JSON.stringify(importError.responseJSON)
}
</li>
importError.responseJSON.map((error, index) => {
return (
<li key={index}>
{error.errorMessage}
</li>
);
})
}
</ul>
}
@@ -257,10 +251,10 @@ ImportMovieFooter.propTypes = {
isImporting: PropTypes.bool.isRequired,
isLookingUpMovie: PropTypes.bool.isRequired,
defaultMonitor: PropTypes.string.isRequired,
defaultQualityProfileIds: PropTypes.arrayOf(PropTypes.number),
defaultQualityProfileId: PropTypes.number,
defaultMinimumAvailability: PropTypes.string,
isMonitorMixed: PropTypes.bool.isRequired,
isQualityProfileIdsMixed: PropTypes.bool.isRequired,
isQualityProfileIdMixed: PropTypes.bool.isRequired,
isMinimumAvailabilityMixed: PropTypes.bool.isRequired,
hasUnsearchedItems: PropTypes.bool.isRequired,
importError: PropTypes.object,

View File

@@ -18,7 +18,7 @@ function createMapStateToProps() {
(addMovie, importMovie, selectedIds) => {
const {
monitor: defaultMonitor,
qualityProfileIds: defaultQualityProfileIds,
qualityProfileId: defaultQualityProfileId,
minimumAvailability: defaultMinimumAvailability
} = addMovie.defaults;
@@ -30,7 +30,7 @@ function createMapStateToProps() {
} = importMovie;
const isMonitorMixed = isMixed(items, selectedIds, defaultMonitor, 'monitor');
const isQualityProfileIdsMixed = isMixed(items, selectedIds, defaultQualityProfileIds, 'qualityProfileIds');
const isQualityProfileIdMixed = isMixed(items, selectedIds, defaultQualityProfileId, 'qualityProfileId');
const isMinimumAvailabilityMixed = isMixed(items, selectedIds, defaultMinimumAvailability, 'minimumAvailability');
const hasUnsearchedItems = !isLookingUpMovie && items.some((item) => !item.isPopulated);
@@ -39,10 +39,10 @@ function createMapStateToProps() {
isLookingUpMovie,
isImporting,
defaultMonitor,
defaultQualityProfileIds,
defaultQualityProfileId,
defaultMinimumAvailability,
isMonitorMixed,
isQualityProfileIdsMixed,
isQualityProfileIdMixed,
isMinimumAvailabilityMixed,
importError,
hasUnsearchedItems

View File

@@ -11,7 +11,7 @@ function ImportMovieRow(props) {
const {
id,
monitor,
qualityProfileIds,
qualityProfileId,
minimumAvailability,
selectedMovie,
isExistingMovie,
@@ -62,8 +62,8 @@ function ImportMovieRow(props) {
<VirtualTableRowCell className={styles.qualityProfile}>
<FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT}
name="qualityProfileIds"
value={qualityProfileIds}
name="qualityProfileId"
value={qualityProfileId}
onChange={onInputChange}
/>
</VirtualTableRowCell>
@@ -74,7 +74,7 @@ function ImportMovieRow(props) {
ImportMovieRow.propTypes = {
id: PropTypes.string.isRequired,
monitor: PropTypes.string.isRequired,
qualityProfileIds: PropTypes.arrayOf(PropTypes.number).isRequired,
qualityProfileId: PropTypes.number.isRequired,
minimumAvailability: PropTypes.string.isRequired,
selectedMovie: PropTypes.object,
isExistingMovie: PropTypes.bool.isRequired,

View File

@@ -15,7 +15,7 @@ class ImportMovieTable extends Component {
const {
unmappedFolders,
defaultMonitor,
defaultQualityProfileIds,
defaultQualityProfileId,
defaultMinimumAvailability,
onMovieLookup,
onSetImportMovieValue
@@ -23,7 +23,7 @@ class ImportMovieTable extends Component {
const values = {
monitor: defaultMonitor,
qualityProfileIds: defaultQualityProfileIds,
qualityProfileId: defaultQualityProfileId,
minimumAvailability: defaultMinimumAvailability
};
@@ -167,7 +167,7 @@ ImportMovieTable.propTypes = {
items: PropTypes.arrayOf(PropTypes.object),
unmappedFolders: PropTypes.arrayOf(PropTypes.object),
defaultMonitor: PropTypes.string.isRequired,
defaultQualityProfileIds: PropTypes.arrayOf(PropTypes.number),
defaultQualityProfileId: PropTypes.number,
defaultMinimumAvailability: PropTypes.string,
allSelected: PropTypes.bool.isRequired,
allUnselected: PropTypes.bool.isRequired,

View File

@@ -13,7 +13,7 @@ function createMapStateToProps() {
(addMovie, importMovie, dimensions, allMovies) => {
return {
defaultMonitor: addMovie.defaults.monitor,
defaultQualityProfileIds: addMovie.defaults.qualityProfileIds,
defaultQualityProfileId: addMovie.defaults.qualityProfileId,
defaultMinimumAvailability: addMovie.defaults.minimumAvailability,
items: importMovie.items,
isSmallScreen: dimensions.isSmallScreen,

View File

@@ -4,7 +4,7 @@
width: 100%;
&:hover {
background-color: var(--menuItemHoverBackgroundColor);
background-color: $menuItemHoverBackgroundColor;
}
}
@@ -17,7 +17,7 @@
composes: link from '~Components/Link/Link.css';
margin-left: auto;
color: var(--textColor);
color: $textColor;
}
.tmdbLinkIcon {

View File

@@ -7,10 +7,10 @@
padding: 6px 16px;
width: 100%;
height: 35px;
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-radius: 4px;
background-color: var(--inputBackgroundColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor);
background-color: $white;
box-shadow: inset 0 1px 1px $inputBoxShadowColor;
}
.loading {
@@ -38,9 +38,9 @@
.content {
padding: 4px;
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-radius: 4px;
background-color: var(--inputBackgroundColor);
background-color: $white;
}
.searchContainer {
@@ -49,12 +49,12 @@
.searchIconContainer {
width: 58px;
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-right: none;
border-radius: 4px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background-color: var(--searchIconContainerBackgroundColor);
background-color: #edf1f2;
text-align: center;
line-height: 33px;
}

View File

@@ -5,7 +5,6 @@ import FormInputButton from 'Components/Form/FormInputButton';
import TextInput from 'Components/Form/TextInput';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import SpinnerButton from 'Components/Link/SpinnerButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Portal from 'Components/Portal';
import { icons, kinds } from 'Helpers/Props';
@@ -243,7 +242,7 @@ class ImportMovieSelectMovie extends Component {
<FormInputButton
kind={kinds.DEFAULT}
spinnerIcon={icons.REFRESH}
ButtonComponent={SpinnerButton}
canSpin={true}
isSpinning={isFetching}
onPress={this.onRefreshPress}
>

View File

@@ -9,7 +9,7 @@
.year {
margin-left: 5px;
color: var(--disabledColor);
color: $disabledColor;
}
.existing {

View File

@@ -152,19 +152,13 @@ class ImportMovieSelectFolder extends Component {
<ul>
{
Array.isArray(saveError.responseJSON) ?
saveError.responseJSON.map((e, index) => {
return (
<li key={index}>
{e.errorMessage}
</li>
);
}) :
<li>
{
JSON.stringify(saveError.responseJSON)
}
</li>
saveError.responseJSON.map((e, index) => {
return (
<li key={index}>
{e.errorMessage}
</li>
);
})
}
</ul>
</Alert> :

View File

@@ -4,7 +4,6 @@ import React from 'react';
import DocumentTitle from 'react-document-title';
import { Provider } from 'react-redux';
import PageConnector from 'Components/Page/PageConnector';
import ApplyTheme from './ApplyTheme';
import AppRoutes from './AppRoutes';
function App({ store, history }) {
@@ -12,11 +11,9 @@ function App({ store, history }) {
<DocumentTitle title={window.Radarr.instanceName}>
<Provider store={store}>
<ConnectedRouter history={history}>
<ApplyTheme>
<PageConnector>
<AppRoutes app={App} />
</PageConnector>
</ApplyTheme>
<PageConnector>
<AppRoutes app={App} />
</PageConnector>
</ConnectedRouter>
</Provider>
</DocumentTitle>

View File

@@ -22,7 +22,7 @@ import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementCo
import MetadataSettings from 'Settings/Metadata/MetadataSettings';
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
import Profiles from 'Settings/Profiles/Profiles';
import QualityConnector from 'Settings/Quality/QualityConnector';
import Quality from 'Settings/Quality/Quality';
import Settings from 'Settings/Settings';
import TagSettings from 'Settings/Tags/TagSettings';
import UISettingsConnector from 'Settings/UI/UISettingsConnector';
@@ -143,7 +143,7 @@ function AppRoutes(props) {
<Route
path="/settings/quality"
component={QualityConnector}
component={Quality}
/>
<Route

View File

@@ -1,49 +0,0 @@
import PropTypes from 'prop-types';
import React, { Fragment, useCallback, useEffect } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import themes from 'Styles/Themes';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.ui.item.theme || window.Radarr.theme,
(
theme
) => {
return {
theme
};
}
);
}
function ApplyTheme({ theme, children }) {
// Update the CSS Variables
const updateCSSVariables = useCallback(() => {
const arrayOfVariableKeys = Object.keys(themes[theme]);
const arrayOfVariableValues = Object.values(themes[theme]);
// Loop through each array key and set the CSS Variables
arrayOfVariableKeys.forEach((cssVariableKey, index) => {
// Based on our snippet from MDN
document.documentElement.style.setProperty(
`--${cssVariableKey}`,
arrayOfVariableValues[index]
);
});
}, [theme]);
// On Component Mount and Component Update
useEffect(() => {
updateCSSVariables(theme);
}, [updateCSSVariables, theme]);
return <Fragment>{children}</Fragment>;
}
ApplyTheme.propTypes = {
theme: PropTypes.string.isRequired,
children: PropTypes.object.isRequired
};
export default connect(createMapStateToProps)(ApplyTheme);

View File

@@ -2,11 +2,11 @@
display: flex;
overflow-x: hidden;
padding: 5px;
border-bottom: 1px solid var(--borderColor);
border-bottom: 1px solid $borderColor;
font-size: $defaultFontSize;
&:hover {
background-color: var(--tableRowHoverBackgroundColor);
background-color: $tableRowHoverBackgroundColor;
}
}

View File

@@ -24,7 +24,7 @@ function createMissingMovieIdsSelector() {
const inCinemas = movie.inCinemas;
if (
(!movie.statistics || movie.statistics.movieFileCount === 0) &&
!movie.hasFile &&
moment(inCinemas).isAfter(start) &&
moment(inCinemas).isBefore(end) &&
isBefore(movie.inCinemas) &&

View File

@@ -2,8 +2,8 @@
flex: 1 0 14.28%;
overflow: hidden;
min-height: 70px;
border-bottom: 1px solid var(--calendarBorderColor);
border-left: 1px solid var(--calendarBorderColor);
border-bottom: 1px solid $calendarBorderColor;
border-left: 1px solid $calendarBorderColor;
}
.isSingleDay {
@@ -12,14 +12,14 @@
.dayOfMonth {
padding-right: 5px;
border-bottom: 1px solid var(--calendarBorderColor);
border-bottom: 1px solid $calendarBorderColor;
text-align: right;
}
.isToday {
background-color: var(--calendarTodayBackgroundColor);
background-color: $calendarTodayBackgroundColor;
}
.isDifferentMonth {
color: var(--disabledColor);
color: $disabledColor;
}

View File

@@ -1,6 +1,6 @@
.days {
display: flex;
border-right: 1px solid var(--calendarBorderColor);
border-right: 1px solid $calendarBorderColor;
}
.day,

View File

@@ -1,6 +1,6 @@
.dayOfWeek {
flex: 1 0 14.28%;
background-color: var(--calendarBackgroudColor);
background-color: #e4eaec;
text-align: center;
}
@@ -9,5 +9,5 @@
}
.isToday {
background-color: var(--calendarTodayBackgroundColor);
background-color: $calendarTodayBackgroundColor;
}

View File

@@ -1,11 +1,9 @@
$fullColorGradient: rgba(244, 245, 246, 0.2);
.event {
overflow-x: hidden;
margin: 4px 2px;
padding: 5px;
border-bottom: 1px solid var(--calendarBorderColor);
border-left: 4px solid var(--calendarBorderColor);
border-bottom: 1px solid $borderColor;
border-left: 4px solid $borderColor;
font-size: 12px;
&:global(.colorImpaired) {
@@ -17,10 +15,10 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
composes: link from '~Components/Link/Link.css';
display: block;
color: var(--defaultColor);
color: $defaultColor;
&:hover {
color: var(--defaultColor);
color: $defaultColor;
text-decoration: none;
}
}
@@ -31,7 +29,7 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
}
.movieInfo {
color: var(--calendarTextDim);
color: $calendarTextDim;
}
.movieTitle,
@@ -42,7 +40,7 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
}
.movieTitle {
color: var(--calendarTextDimAlternate);
color: #3a3f51;
font-size: $defaultFontSize;
}
@@ -55,85 +53,37 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
*/
.downloaded {
border-left-color: var(--successColor) !important;
&:global(.fullColor) {
background-color: rgba(39, 194, 76, 0.4) !important;
}
border-left-color: $successColor !important;
&:global(.colorImpaired) {
border-left-color: color(var(--successColor), saturation(+15%)) !important;
border-left-color: color($successColor, saturation(+15%)) !important;
}
}
.queue {
border-left-color: var(--purple) !important;
&:global(.fullColor) {
background-color: rgba(122, 67, 182, 0.4) !important;
}
border-left-color: $purple !important;
}
.unmonitored {
border-left-color: var(--gray) !important;
&:global(.fullColor) {
background-color: rgba(173, 173, 173, 0.5) !important;
}
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
&:global(.fullColor.colorImpaired) {
background: repeating-linear-gradient(45deg, $fullColorGradient, $fullColorGradient 5px, transparent 5px, transparent 10px);
}
border-left-color: $gray !important;
}
.missingUnmonitored {
border-left-color: var(--warningColor) !important;
&:global(.fullColor) {
background-color: rgba(255, 165, 0, 0.6) !important;
}
border-left-color: $warningColor !important;
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
&:global(.fullColor.colorImpaired) {
background: repeating-linear-gradient(45deg, $fullColorGradient, $fullColorGradient 5px, transparent 5px, transparent 10px);
background: repeating-linear-gradient(45deg, $colorImpairedGradientDark, $colorImpairedGradientDark 5px, $colorImpairedGradient 5px, $colorImpairedGradient 10px);
}
}
.missingMonitored {
border-left-color: var(--dangerColor) !important;
&:global(.fullColor) {
background-color: rgba(240, 80, 80, 0.6) !important;
}
border-left-color: $dangerColor !important;
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
&:global(.fullColor.colorImpaired) {
background: repeating-linear-gradient(90deg, $fullColorGradient, $fullColorGradient 5px, transparent 5px, transparent 10px);
background: repeating-linear-gradient(90deg, $colorImpairedGradientDark, $colorImpairedGradientDark 5px, $colorImpairedGradient 5px, $colorImpairedGradient 10px);
}
}
.unaired {
border-left-color: var(--primaryColor) !important;
&:global(.fullColor) {
background-color: rgba(93, 156, 236, 0.4) !important;
}
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
&:global(.fullColor.colorImpaired) {
background: repeating-linear-gradient(90deg, $fullColorGradient, $fullColorGradient 5px, transparent 5px, transparent 10px);
}
.continuing {
border-left-color: $primaryColor !important;
}

View File

@@ -32,7 +32,6 @@ class CalendarEvent extends Component {
queueItem,
showMovieInformation,
showCutoffUnmetIcon,
fullColorEvents,
colorImpairedMode,
date
} = this.props;
@@ -63,8 +62,7 @@ class CalendarEvent extends Component {
styles.event,
styles.link,
styles[statusStyle],
colorImpairedMode && 'colorImpaired',
fullColorEvents && 'fullColor'
colorImpairedMode && 'colorImpaired'
)}
// component="div"
to={link}
@@ -99,7 +97,7 @@ class CalendarEvent extends Component {
<Icon
className={styles.statusIcon}
name={icons.MOVIE_FILE}
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
kind={kinds.WARNING}
title={translate('QualityCutoffHasNotBeenMet')}
/>
}
@@ -144,12 +142,11 @@ CalendarEvent.propTypes = {
digitalRelease: PropTypes.string,
monitored: PropTypes.bool.isRequired,
certification: PropTypes.string,
hasFile: PropTypes.bool,
hasFile: PropTypes.bool.isRequired,
grabbed: PropTypes.bool,
queueItem: PropTypes.object,
showMovieInformation: PropTypes.bool.isRequired,
showCutoffUnmetIcon: PropTypes.bool.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
timeFormat: PropTypes.string.isRequired,
colorImpairedMode: PropTypes.bool.isRequired,
date: PropTypes.string.isRequired

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import QueueDetails from 'Activity/Queue/QueueDetails';
import CircularProgressBar from 'Components/CircularProgressBar';
import colors from 'Styles/Variables/colors';
import translate from 'Utilities/String/translate';
function CalendarEventQueueDetails(props) {
@@ -34,7 +35,7 @@ function CalendarEventQueueDetails(props) {
progress={progress}
size={20}
strokeWidth={2}
strokeColor={'#7a43b6'}
strokeColor={colors.purple}
/>
</div>
}

View File

@@ -9,7 +9,6 @@ import styles from './Legend.css';
function Legend(props) {
const {
showCutoffUnmetIcon,
fullColorEvents,
colorImpairedMode
} = props;
@@ -20,7 +19,7 @@ function Legend(props) {
<LegendIconItem
name={translate('CutoffUnmet')}
icon={icons.MOVIE_FILE}
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
kind={kinds.WARNING}
tooltip={translate('QualityOrLangCutoffHasNotBeenMet')}
/>
);
@@ -32,14 +31,12 @@ function Legend(props) {
<LegendItem
style='ended'
name={translate('DownloadedAndMonitored')}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
style='availNotMonitored'
name={translate('DownloadedButNotMonitored')}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
</div>
@@ -48,14 +45,12 @@ function Legend(props) {
<LegendItem
style='missingMonitored'
name={translate('MissingMonitoredAndConsideredAvailable')}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
style='missingUnmonitored'
name={translate('MissingNotMonitored')}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
</div>
@@ -64,14 +59,12 @@ function Legend(props) {
<LegendItem
style='queue'
name={translate('Queued')}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
style='continuing'
name={translate('Unreleased')}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
</div>
@@ -86,9 +79,7 @@ function Legend(props) {
}
Legend.propTypes = {
view: PropTypes.string.isRequired,
showCutoffUnmetIcon: PropTypes.bool.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
colorImpairedMode: PropTypes.bool.isRequired
};

View File

@@ -6,12 +6,10 @@ import Legend from './Legend';
function createMapStateToProps() {
return createSelector(
(state) => state.calendar.options,
(state) => state.calendar.view,
createUISettingsSelector(),
(calendarOptions, view, uiSettings) => {
(calendarOptions, uiSettings) => {
return {
...calendarOptions,
view,
colorImpairedMode: uiSettings.enableColorImpairedMode
};
}

View File

@@ -8,7 +8,6 @@ function LegendIconItem(props) {
name,
icon,
kind,
darken,
tooltip
} = props;
@@ -20,7 +19,6 @@ function LegendIconItem(props) {
<Icon
className={styles.icon}
name={icon}
darken={darken}
kind={kind}
/>
@@ -33,12 +31,7 @@ LegendIconItem.propTypes = {
name: PropTypes.string.isRequired,
icon: PropTypes.object.isRequired,
kind: PropTypes.string.isRequired,
darken: PropTypes.bool.isRequired,
tooltip: PropTypes.string.isRequired
};
LegendIconItem.defaultProps = {
darken: false
};
export default LegendIconItem;

View File

@@ -1,5 +1,3 @@
$fullColorGradient: rgba(244, 245, 246, 0.2);
.legendItemContainer {
margin-right: 5px;
width: 220px;
@@ -22,55 +20,53 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
.queue {
composes: legendItemColor;
background-color: var(--queueColor);
background-color: $queueColor;
}
.continuing {
composes: legendItemColor;
background-color: var(--primaryColor);
background-color: $primaryColor;
}
.availNotMonitored {
composes: legendItemColor;
background-color: var(--darkGray);
background-color: $darkGray;
}
.ended {
composes: legendItemColor;
background-color: var(--successColor);
background-color: $successColor;
}
.missingMonitored {
composes: legendItemColor;
background-color: var(--dangerColor);
background-color: $dangerColor;
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, color(var(--dangerColor) shade(5%)), color(var(--dangerColor) shade(5%)) 5px, color(var(--dangerColor) shade(15%)) 5px, color(var(--dangerColor) shade(15%)) 10px);
background: repeating-linear-gradient(90deg, color($dangerColor shade(5%)), color($dangerColor shade(5%)) 5px, color($dangerColor shade(15%)) 5px, color($dangerColor shade(15%)) 10px);
}
}
.missingUnmonitored {
composes: legendItemColor;
background-color: var(--warningColor);
background-color: $warningColor;
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, var(--warningColor), var(--warningColor) 5px, color(var(--warningColor) tint(15%)) 5px, color(var(--warningColor) tint(15%)) 10px);
background: repeating-linear-gradient(45deg, $warningColor, $warningColor 5px, color($warningColor tint(15%)) 5px, color($warningColor tint(15%)) 10px);
}
}
.missingMonitoredColorImpaired {
background: repeating-linear-gradient(90deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
color: var(--white);
background: repeating-linear-gradient(90deg, $colorImpairedGradientDark, $colorImpairedGradientDark 5px, $colorImpairedGradient 5px, $colorImpairedGradient 10px);
}
.missingUnmonitoredColorImpaired {
background: repeating-linear-gradient(45deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
color: var(--white);
background: repeating-linear-gradient(45deg, $colorImpairedGradientDark, $colorImpairedGradientDark 5px, $colorImpairedGradient 5px, $colorImpairedGradient 10px);
}
.legendItemText {

View File

@@ -7,7 +7,6 @@ function LegendItem(props) {
const {
name,
style,
fullColorEvents,
colorImpairedMode
} = props;
@@ -17,8 +16,7 @@ function LegendItem(props) {
className={classNames(
styles.legendItem,
styles[style],
colorImpairedMode && 'colorImpaired',
fullColorEvents && 'fullColor'
colorImpairedMode && 'colorImpaired'
)}
/>
<div className={classNames(styles.legendItemText, colorImpairedMode && styles[`${style}ColorImpaired`])}>
@@ -31,7 +29,6 @@ function LegendItem(props) {
LegendItem.propTypes = {
name: PropTypes.string.isRequired,
style: PropTypes.string.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
colorImpairedMode: PropTypes.bool.isRequired
};

View File

@@ -26,16 +26,14 @@ class CalendarOptionsModalContent extends Component {
firstDayOfWeek,
calendarWeekColumnHeader,
timeFormat,
enableColorImpairedMode,
fullColorEvents
enableColorImpairedMode
} = props;
this.state = {
firstDayOfWeek,
calendarWeekColumnHeader,
timeFormat,
enableColorImpairedMode,
fullColorEvents
enableColorImpairedMode
};
}
@@ -96,7 +94,6 @@ class CalendarOptionsModalContent extends Component {
const {
showMovieInformation,
showCutoffUnmetIcon,
fullColorEvents,
onModalClose
} = this.props;
@@ -139,18 +136,6 @@ class CalendarOptionsModalContent extends Component {
onChange={this.onOptionInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('FullColorEvents')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="fullColorEvents"
value={fullColorEvents}
helpText={translate('FullColorEventsHelpText')}
onChange={this.onOptionInputChange}
/>
</FormGroup>
</Form>
</FieldSet>
@@ -177,7 +162,7 @@ class CalendarOptionsModalContent extends Component {
values={weekColumnOptions}
value={calendarWeekColumnHeader}
onChange={this.onGlobalInputChange}
helpText={translate('SettingsWeekColumnHeaderHelpText')}
helpText={translate('HelpText')}
/>
</FormGroup>
@@ -191,9 +176,7 @@ class CalendarOptionsModalContent extends Component {
value={timeFormat}
onChange={this.onGlobalInputChange}
/>
</FormGroup>
<FormGroup>
</FormGroup><FormGroup>
<FormLabel>{translate('EnableColorImpairedMode')}</FormLabel>
<FormInputGroup
@@ -204,6 +187,7 @@ class CalendarOptionsModalContent extends Component {
onChange={this.onGlobalInputChange}
/>
</FormGroup>
</Form>
</FieldSet>
</ModalBody>
@@ -225,7 +209,6 @@ CalendarOptionsModalContent.propTypes = {
calendarWeekColumnHeader: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
enableColorImpairedMode: PropTypes.bool.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
dispatchSetCalendarOption: PropTypes.func.isRequired,
dispatchSaveUISettings: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired

View File

@@ -22,7 +22,7 @@ function getUrls(state) {
tags
} = state;
let icalUrl = `${window.location.host}${window.Radarr.urlBase}/feed/v4/calendar/Radarr.ics?`;
let icalUrl = `${window.location.host}${window.Radarr.urlBase}/feed/v3/calendar/Radarr.ics?`;
if (unmonitored) {
icalUrl += 'unmonitored=true&';

View File

@@ -4,7 +4,7 @@
.year {
margin-left: 5px;
color: var(--disabledColor);
color: $disabledColor;
}
.poster {

View File

@@ -46,7 +46,7 @@ class AddNewCollectionMovieModalContent extends Component {
onInputChange,
rootFolderPath,
monitor,
qualityProfileIds,
qualityProfileId,
minimumAvailability,
searchForMovie
} = this.props;
@@ -126,13 +126,13 @@ class AddNewCollectionMovieModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>{translate('QualityProfiles')}</FormLabel>
<FormLabel>{translate('QualityProfile')}</FormLabel>
<FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT}
name="qualityProfileIds"
name="qualityProfileId"
onChange={this.onQualityProfileIdChange}
{...qualityProfileIds}
{...qualityProfileId}
/>
</FormGroup>
@@ -189,7 +189,7 @@ AddNewCollectionMovieModalContent.propTypes = {
addError: PropTypes.object,
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
qualityProfileIds: PropTypes.object,
qualityProfileId: PropTypes.object,
minimumAvailability: PropTypes.object.isRequired,
searchForMovie: PropTypes.object.isRequired,
folder: PropTypes.string.isRequired,

View File

@@ -25,7 +25,7 @@ function createMapStateToProps() {
const collectionDefaults = {
rootFolderPath: collection.rootFolderPath,
monitor: 'movieOnly',
qualityProfileIds: collection.qualityProfileIds,
qualityProfileId: collection.qualityProfileId,
minimumAvailability: collection.minimumAvailability,
searchForMovie: collection.searchOnAdd,
tags: []
@@ -70,7 +70,7 @@ class AddNewCollectionMovieModalContentConnector extends Component {
title,
rootFolderPath,
monitor,
qualityProfileIds,
qualityProfileId,
minimumAvailability,
searchForMovie,
tags
@@ -81,7 +81,7 @@ class AddNewCollectionMovieModalContentConnector extends Component {
title,
rootFolderPath: rootFolderPath.value,
monitor: monitor.value,
qualityProfileIds: qualityProfileIds.value,
qualityProfileId: qualityProfileId.value,
minimumAvailability: minimumAvailability.value,
searchForMovie: searchForMovie.value,
tags: tags.value
@@ -109,7 +109,7 @@ AddNewCollectionMovieModalContentConnector.propTypes = {
title: PropTypes.string.isRequired,
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
qualityProfileIds: PropTypes.object,
qualityProfileId: PropTypes.object,
minimumAvailability: PropTypes.object.isRequired,
searchForMovie: PropTypes.object.isRequired,
tags: PropTypes.object.isRequired,

View File

@@ -66,8 +66,7 @@ class CollectionFooter extends Component {
monitor,
monitored,
qualityProfileId,
minimumAvailability,
rootFolderPath
minimumAvailability
} = this.state;
const changes = {};
@@ -88,10 +87,6 @@ class CollectionFooter extends Component {
changes.minimumAvailability = minimumAvailability;
}
if (rootFolderPath !== NO_CHANGE) {
changes.rootFolderPath = rootFolderPath;
}
this.props.onUpdateSelectedPress(changes);
};

View File

@@ -46,7 +46,7 @@ class EditCollectionModalContent extends Component {
const {
monitored,
qualityProfileIds,
qualityProfileId,
minimumAvailability,
// Id,
rootFolderPath,
@@ -104,18 +104,18 @@ class EditCollectionModalContent extends Component {
</FormGroup>
<FormGroup>
<FormLabel>{translate('QualityProfiles')}</FormLabel>
<FormLabel>{translate('QualityProfile')}</FormLabel>
<FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT}
name="qualityProfileIds"
{...qualityProfileIds}
name="qualityProfileId"
{...qualityProfileId}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('RootFolder')}</FormLabel>
<FormLabel>{translate('Folder')}</FormLabel>
<FormInputGroup
type={inputTypes.ROOT_FOLDER_SELECT}

View File

@@ -39,7 +39,7 @@ function createMapStateToProps() {
const movieSettings = {
monitored: collection.monitored,
qualityProfileIds: collection.qualityProfileIds,
qualityProfileId: collection.qualityProfileId,
minimumAvailability: collection.minimumAvailability,
rootFolderPath: collection.rootFolderPath,
searchOnAdd: collection.searchOnAdd

View File

@@ -6,7 +6,7 @@ $hoverScale: 1.05;
&:hover {
z-index: 2;
box-shadow: 0 0 10px var(--black);
box-shadow: 0 0 10px $black;
transition: all 200ms ease-in;
.poster {
@@ -28,7 +28,7 @@ $hoverScale: 1.05;
.poster {
position: relative;
display: block;
background-color: var(--defaultColor);
background-color: $defaultColor;
}
.overlay {
@@ -44,7 +44,7 @@ $hoverScale: 1.05;
.overlayTitle {
padding: 5px;
color: var(--offWhite);
color: $offWhite;
text-align: left;
font-weight: bold;
font-size: 15px;
@@ -67,7 +67,7 @@ $hoverScale: 1.05;
z-index: 3;
border-radius: 4px;
background-color: #707070;
color: var(--white);
color: $white;
font-size: $smallFontSize;
opacity: 0;
transition: opacity 0;
@@ -77,7 +77,7 @@ $hoverScale: 1.05;
composes: button from '~Components/Link/IconButton.css';
&:hover {
color: var(--radarrYellow);
color: $radarrYellow;
}
}
@@ -102,16 +102,16 @@ $hoverScale: 1.05;
position: relative;
display: block;
background-color: var(--defaultColor);
background-color: $defaultColor;
}
.monitorToggleButton {
composes: toggleButton from '~Components/MonitorToggleButton.css';
width: 25px;
color: var(--white);
color: $white;
&:hover {
color: var(--iconButtonHoverLightColor);
color: $iconButtonHoverLightColor;
}
}

View File

@@ -3,7 +3,7 @@
align-items: stretch;
overflow: hidden;
margin: 2px 4px;
border: 1px solid var(--borderColor);
border: 1px solid $borderColor;
border-radius: 4px;
background-color: #eee;
cursor: default;
@@ -17,34 +17,34 @@
padding: 0 4px;
border-left: 4px;
border-left-style: solid;
background-color: var(--white);
color: var(--defaultColor);
background-color: $white;
color: $defaultColor;
}
.primary {
border-color: var(--primaryColor);
border-color: $primaryColor;
}
.danger {
border-color: var(--dangerColor);
border-color: $dangerColor;
}
.success {
border-color: var(--successColor);
border-color: $successColor;
}
.purple {
border-color: var(--purple);
border-color: $purple;
}
.warning {
border-color: var(--warningColor);
border-color: $warningColor;
}
.info {
border-color: var(--infoColor);
border-color: $infoColor;
}
.queue {
border-color: var(--queueColor);
border-color: $queueColor;
}

View File

@@ -17,13 +17,11 @@ class CollectionMovieLabel extends Component {
status,
monitored,
isAvailable,
hasFile,
onMonitorTogglePress,
isSaving,
statistics
isSaving
} = this.props;
const { movieFileCount } = statistics;
return (
<div className={styles.movie}>
<div className={styles.movieTitle}>
@@ -48,11 +46,11 @@ class CollectionMovieLabel extends Component {
<div
className={classNames(
styles.movieStatus,
styles[getStatusStyle(status, monitored, movieFileCount > 0, isAvailable, 'kinds')]
styles[getStatusStyle(status, monitored, hasFile, isAvailable, 'kinds')]
)}
>
{
movieFileCount > 0 ? translate('Downloaded') : translate('Missing')
hasFile ? translate('Downloaded') : translate('Missing')
}
</div>
}
@@ -65,9 +63,9 @@ CollectionMovieLabel.propTypes = {
id: PropTypes.number,
title: PropTypes.string.isRequired,
status: PropTypes.string,
statistics: PropTypes.object.isRequired,
isAvailable: PropTypes.bool,
monitored: PropTypes.bool,
hasFile: PropTypes.bool,
isSaving: PropTypes.bool.isRequired,
movieFile: PropTypes.object,
movieFileId: PropTypes.number,
@@ -77,7 +75,9 @@ CollectionMovieLabel.propTypes = {
CollectionMovieLabel.defaultProps = {
isSaving: false,
statistics: {
movieFileCount: 0
episodeFileCount: 0,
totalEpisodeCount: 0,
percentOfEpisodes: 0
}
};

View File

@@ -104,7 +104,7 @@ $hoverScale: 1.05;
width: 25px;
&:hover {
color: var(--iconButtonHoverLightColor);
color: $iconButtonHoverLightColor;
}
}
@@ -131,7 +131,7 @@ $hoverScale: 1.05;
width: 20px;
&:hover {
color: var(--iconButtonHoverLightColor);
color: $iconButtonHoverLightColor;
}
}
}

View File

@@ -1,8 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Slider from 'react-slick';
import TextTruncate from 'react-text-truncate';
import { Navigation } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
import EditCollectionModalConnector from 'Collection/Edit/EditCollectionModalConnector';
import CheckInput from 'Components/Form/CheckInput';
import Icon from 'Components/Icon';
@@ -18,9 +17,8 @@ import CollectionMovieConnector from './CollectionMovieConnector';
import CollectionMovieLabelConnector from './CollectionMovieLabelConnector';
import styles from './CollectionOverview.css';
// Import Swiper styles
import 'swiper/css';
import 'swiper/css/navigation';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
@@ -54,12 +52,8 @@ class CollectionOverview extends Component {
//
// Control
setSliderPrevRef = (ref) => {
this._swiperPrevRef = ref;
};
setSliderNextRef = (ref) => {
this._swiperNextRef = ref;
setSliderRef = (ref) => {
this.setState({ slider: ref });
};
//
@@ -96,7 +90,7 @@ class CollectionOverview extends Component {
render() {
const {
monitored,
qualityProfileIds,
qualityProfileId,
rootFolderPath,
genres,
id,
@@ -126,6 +120,15 @@ class CollectionOverview extends Component {
const contentHeight = getContentHeight(rowHeight, isSmallScreen);
const overviewHeight = contentHeight - titleRowHeight - posterHeight;
const sliderSettings = {
arrows: false,
dots: false,
infinite: false,
slidesToShow: 1,
slidesToScroll: 1,
variableWidth: true
};
return (
<div className={styles.container}>
<div className={styles.content}>
@@ -163,21 +166,19 @@ class CollectionOverview extends Component {
{
showPosters &&
<div className={styles.navigationButtons}>
<span ref={this.setSliderPrevRef}>
<IconButton
name={icons.ARROW_LEFT}
title={translate('ScrollMovies')}
size={20}
/>
</span>
<IconButton
name={icons.ARROW_LEFT}
title={translate('ScrollMovies')}
onPress={this.state.slider?.slickPrev}
size={20}
/>
<span ref={this.setSliderNextRef}>
<IconButton
name={icons.ARROW_RIGHT}
title={translate('ScrollMovies')}
size={20}
/>
</span>
<IconButton
name={icons.ARROW_RIGHT}
title={translate('ScrollMovies')}
onPress={this.state.slider?.slickNext}
size={20}
/>
</div>
}
@@ -212,7 +213,7 @@ class CollectionOverview extends Component {
<span className={styles.qualityProfileName}>
{
<QualityProfileNameConnector
qualityProfileIds={qualityProfileIds}
qualityProfileId={qualityProfileId}
/>
}
</span>
@@ -269,23 +270,9 @@ class CollectionOverview extends Component {
{
showPosters ?
<div className={styles.sliderContainer}>
<Swiper
slidesPerView='auto'
spaceBetween={10}
slidesPerGroup={3}
loop={false}
loopFillGroupWithBlank={true}
className="mySwiper"
modules={[Navigation]}
onInit={(swiper) => {
swiper.params.navigation.prevEl = this._swiperPrevRef;
swiper.params.navigation.nextEl = this._swiperNextRef;
swiper.navigation.init();
swiper.navigation.update();
}}
>
<Slider ref={this.setSliderRef} {...sliderSettings}>
{movies.map((movie) => (
<SwiperSlide key={movie.tmdbId} style={{ width: posterWidth }}>
<div className={styles.movie} key={movie.tmdbId}>
<CollectionMovieConnector
key={movie.tmdbId}
posterWidth={posterWidth}
@@ -294,9 +281,9 @@ class CollectionOverview extends Component {
collectionId={id}
{...movie}
/>
</SwiperSlide>
</div>
))}
</Swiper>
</Slider>
</div> :
<div className={styles.labelsContainer}>
{movies.map((movie) => (
@@ -325,7 +312,7 @@ class CollectionOverview extends Component {
CollectionOverview.propTypes = {
id: PropTypes.number.isRequired,
monitored: PropTypes.bool.isRequired,
qualityProfileIds: PropTypes.number.isRequired,
qualityProfileId: PropTypes.number.isRequired,
minimumAvailability: PropTypes.string.isRequired,
searchOnAdd: PropTypes.bool.isRequired,
rootFolderPath: PropTypes.string.isRequired,

View File

@@ -5,7 +5,7 @@
.container {
&:hover {
.content {
background-color: var(--tableRowHoverBackgroundColor);
background-color: $tableRowHoverBackgroundColor;
}
}
}

View File

@@ -15,7 +15,6 @@ export const REFRESH_MOVIE = 'RefreshMovie';
export const RENAME_FILES = 'RenameFiles';
export const RENAME_MOVIE = 'RenameMovie';
export const RESET_API_KEY = 'ResetApiKey';
export const RESET_QUALITY_DEFINITIONS = 'ResetQualityDefinitions';
export const RSS_SYNC = 'RssSync';
export const MOVIE_SEARCH = 'MoviesSearch';
export const IMPORT_LIST_SYNC = 'ImportListSync';

View File

@@ -7,25 +7,25 @@
}
.danger {
border-color: var(--alertDangerBorderColor);
background-color: var(--alertDangerBackgroundColor);
color: var(--alertDangerColor);
border-color: $alertDangerBorderColor;
background-color: $alertDangerBackgroundColor;
color: $alertDangerColor;
}
.info {
border-color: var(--alertInfoBorderColor);
background-color: var(--alertInfoBackgroundColor);
color: var(--alertInfoColor);
border-color: $alertInfoBorderColor;
background-color: $alertInfoBackgroundColor;
color: $alertInfoColor;
}
.success {
border-color: var(--alertSuccessBorderColor);
background-color: var(--alertSuccessBackgroundColor);
color: var(--alertSuccessColor);
border-color: $alertSuccessBorderColor;
background-color: $alertSuccessBackgroundColor;
color: $alertSuccessColor;
}
.warning {
border-color: var(--alertWarningBorderColor);
background-color: var(--alertWarningBackgroundColor);
color: var(--alertWarningColor);
border-color: $alertWarningBorderColor;
background-color: $alertWarningBackgroundColor;
color: $alertWarningColor;
}

View File

@@ -3,9 +3,9 @@
margin: 10px;
padding: 10px;
border-radius: 3px;
background-color: var(--cardBackgroundColor);
box-shadow: 0 0 10px 1px var(--cardShadowColor);
color: var(--defaultColor);
background-color: $white;
box-shadow: 0 0 10px 1px $cardShadowColor;
color: $defaultColor;
}
.underlay {

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import colors from 'Styles/Variables/colors';
import styles from './CircularProgressBar.css';
class CircularProgressBar extends Component {
@@ -131,7 +132,7 @@ CircularProgressBar.defaultProps = {
containerClassName: styles.circularProgressBarContainer,
size: 60,
strokeWidth: 5,
strokeColor: '#ffc230',
strokeColor: colors.radarrYellow,
showProgressText: false
};

View File

@@ -13,7 +13,7 @@
width: 100%;
border: 0;
border-bottom: 1px solid #e5e5e5;
color: var(--textColor);
color: #3a3f51;
font-size: 21px;
line-height: inherit;

View File

@@ -13,7 +13,7 @@
}
.faqLink {
color: var(--alertWarningColor);
color: $alertWarningColor;
font-weight: bold;
}

View File

@@ -3,7 +3,7 @@
margin-bottom: 5px;
&:hover {
background-color: var(--tableRowHoverBackgroundColor);
background-color: $tableRowHoverBackgroundColor;
}
}

View File

@@ -17,5 +17,5 @@
.or {
margin: 0 3px;
color: var(--themeDarkColor);
color: $themeDarkColor;
}

View File

@@ -4,7 +4,7 @@
padding: 5px;
&:hover {
background-color: var(--tableRowHoverBackgroundColor);
background-color: $tableRowHoverBackgroundColor;
}
}

View File

@@ -27,10 +27,10 @@
overflow-y: auto;
max-height: 200px;
width: 100%;
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-radius: 4px;
background-color: var(--inputBackgroundColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor);
background-color: $white;
box-shadow: inset 0 1px 1px $inputBoxShadowColor;
}
}
@@ -46,5 +46,5 @@
}
.suggestionHighlighted {
background-color: var(--menuItemHoverBackgroundColor);
background-color: $menuItemHoverBackgroundColor;
}

View File

@@ -32,21 +32,21 @@
height: 20px;
border: 1px solid #ccc;
border-radius: 2px;
background-color: var(--white);
color: var(--white);
background-color: $white;
color: $white;
text-align: center;
line-height: 20px;
}
.checkbox:focus + .input {
outline: 0;
border-color: var(--inputFocusBorderColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor), 0 0 8px var(--inputFocusBoxShadowColor);
border-color: $inputFocusBorderColor;
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputFocusBoxShadowColor;
}
.dangerIsChecked {
border-color: var(--dangerColor);
background-color: var(--dangerColor);
border-color: $dangerColor;
background-color: $dangerColor;
&.isDisabled {
opacity: 0.7;
@@ -54,8 +54,8 @@
}
.primaryIsChecked {
border-color: var(--primaryColor);
background-color: var(--primaryColor);
border-color: $primaryColor;
background-color: $primaryColor;
&.isDisabled {
opacity: 0.7;
@@ -63,8 +63,8 @@
}
.successIsChecked {
border-color: var(--successColor);
background-color: var(--successColor);
border-color: $successColor;
background-color: $successColor;
&.isDisabled {
opacity: 0.7;
@@ -72,8 +72,8 @@
}
.warningIsChecked {
border-color: var(--warningColor);
background-color: var(--warningColor);
border-color: $warningColor;
background-color: $warningColor;
&.isDisabled {
opacity: 0.7;
@@ -82,15 +82,15 @@
.isNotChecked {
&.isDisabled {
border-color: var(--disabledCheckInputColor);
background-color: var(--disabledCheckInputColor);
border-color: $disabledCheckInputColor;
background-color: $disabledCheckInputColor;
opacity: 0.7;
}
}
.isIndeterminate {
border-color: var(--gray);
background-color: var(--gray);
border-color: $gray;
background-color: $gray;
}
.helpText {

View File

@@ -39,7 +39,7 @@
.dropdownArrowContainerDisabled {
composes: dropdownArrowContainer;
color: var(--disabledInputColor);
color: $disabledInputColor;
}
.optionsContainer {
@@ -50,9 +50,9 @@
.options {
composes: scroller from '~Components/Scroller/Scroller.css';
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-radius: 4px;
background-color: var(--inputBackgroundColor);
background-color: $white;
}
.optionsModal {
@@ -76,9 +76,9 @@
.optionsModalScroller {
composes: scroller from '~Components/Scroller/Scroller.css';
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-radius: 4px;
background-color: var(--inputBackgroundColor);
background-color: $white;
}
.loading {
@@ -90,7 +90,7 @@
display: flex;
justify-content: flex-end;
height: 40px;
border-bottom: 1px solid var(--borderColor);
border-bottom: 1px solid $borderColor;
}
.mobileCloseButton {
@@ -100,6 +100,6 @@
line-height: 40px;
&:hover {
color: var(--modalCloseButtonHoverColor);
color: $modalCloseButtonHoverColor;
}
}

View File

@@ -113,12 +113,10 @@ class EnhancedSelectInput extends Component {
this._scheduleUpdate();
}
if (!Array.isArray(this.props.value)) {
if (prevProps.value !== this.props.value || prevProps.values !== this.props.values) {
this.setState({
selectedIndex: getSelectedIndex(this.props)
});
}
if (!Array.isArray(this.props.value) && prevProps.value !== this.props.value) {
this.setState({
selectedIndex: getSelectedIndex(this.props)
});
}
}
@@ -334,11 +332,6 @@ class EnhancedSelectInput extends Component {
const isMultiSelect = Array.isArray(value);
const selectedOption = getSelectedOption(selectedIndex, values);
let selectedValue = value;
if (!values.length) {
selectedValue = isMultiSelect ? [] : '';
}
return (
<div>
@@ -379,17 +372,15 @@ class EnhancedSelectInput extends Component {
onPress={this.onPress}
>
{
isFetching ?
isFetching &&
<LoadingIndicator
className={styles.loading}
size={20}
/> :
null
/>
}
{
isFetching ?
null :
!isFetching &&
<Icon
name={icons.CARET_DOWN}
/>
@@ -409,7 +400,7 @@ class EnhancedSelectInput extends Component {
onPress={this.onPress}
>
<SelectedValueComponent
value={selectedValue}
value={value}
values={values}
{...selectedValueOptions}
{...selectedOption}
@@ -427,17 +418,15 @@ class EnhancedSelectInput extends Component {
>
{
isFetching ?
isFetching &&
<LoadingIndicator
className={styles.loading}
size={20}
/> :
null
/>
}
{
isFetching ?
null :
!isFetching &&
<Icon
name={icons.CARET_DOWN}
/>
@@ -517,7 +506,7 @@ class EnhancedSelectInput extends Component {
</Manager>
{
isMobile ?
isMobile &&
<Modal
className={styles.optionsModal}
size={sizes.EXTRA_SMALL}
@@ -568,8 +557,7 @@ class EnhancedSelectInput extends Component {
}
</Scroller>
</ModalBody>
</Modal> :
null
</Modal>
}
</div>
);

View File

@@ -7,7 +7,7 @@
cursor: default;
&:hover {
background-color: var(--inputHoverBackgroundColor);
background-color: #f8f8f8;
}
}
@@ -24,17 +24,17 @@
}
.isSelected {
background-color: var(--inputSelectedBackgroundColor);
background-color: #e2e2e2;
&:hover {
background-color: var(--inputSelectedBackgroundColor);
background-color: #e2e2e2;
}
&.isMobile {
background-color: inherit;
.iconContainer {
color: var(--primaryColor);
color: $primaryColor;
}
}
}
@@ -49,7 +49,7 @@
.isMobile {
height: 50px;
border-bottom: 1px solid var(--borderColor);
border-bottom: 1px solid $borderColor;
&:last-child {
border: none;

View File

@@ -3,5 +3,5 @@
}
.isDisabled {
color: var(--disabledInputColor);
color: $disabledInputColor;
}

View File

@@ -2,19 +2,33 @@ import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton';
import { kinds } from 'Helpers/Props';
import styles from './FormInputButton.css';
function FormInputButton(props) {
const {
className,
ButtonComponent,
canSpin,
isLastButton,
...otherProps
} = props;
if (canSpin) {
return (
<SpinnerButton
className={classNames(
className,
!isLastButton && styles.middleButton
)}
kind={kinds.PRIMARY}
{...otherProps}
/>
);
}
return (
<ButtonComponent
<Button
className={classNames(
className,
!isLastButton && styles.middleButton
@@ -27,14 +41,14 @@ function FormInputButton(props) {
FormInputButton.propTypes = {
className: PropTypes.string.isRequired,
ButtonComponent: PropTypes.elementType.isRequired,
isLastButton: PropTypes.bool.isRequired
isLastButton: PropTypes.bool.isRequired,
canSpin: PropTypes.bool.isRequired
};
FormInputButton.defaultProps = {
className: styles.button,
ButtonComponent: Button,
isLastButton: true
isLastButton: true,
canSpin: false
};
export default FormInputButton;

View File

@@ -6,6 +6,7 @@
.inputGroup {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
}
.inputContainer {
@@ -39,7 +40,7 @@
}
.pendingChangesIcon {
color: var(--warningColor);
color: $warningColor;
font-size: 20px;
line-height: 35px;
}

View File

@@ -20,7 +20,6 @@ import NumberInput from './NumberInput';
import OAuthInputConnector from './OAuthInputConnector';
import PasswordInput from './PasswordInput';
import PathInputConnector from './PathInputConnector';
import PlexMachineInputConnector from './PlexMachineInputConnector';
import QualityProfileSelectInputConnector from './QualityProfileSelectInputConnector';
import RootFolderSelectInputConnector from './RootFolderSelectInputConnector';
import TagInputConnector from './TagInputConnector';
@@ -63,9 +62,6 @@ function getComponent(type) {
case inputTypes.PATH:
return PathInputConnector;
case inputTypes.PLEX_MACHINE_SELECT:
return PlexMachineInputConnector;
case inputTypes.QUALITY_PROFILE_SELECT:
return QualityProfileSelectInputConnector;

View File

@@ -1,14 +1,14 @@
.helpText {
margin-top: 5px;
color: var(--helpTextColor);
color: $helpTextColor;
line-height: 20px;
}
.isError {
color: var(--dangerColor);
color: $dangerColor;
.link {
color: var(--dangerColor);
color: $dangerColor;
&:hover {
color: #e01313;
@@ -17,10 +17,10 @@
}
.isWarning {
color: var(--warningColor);
color: $warningColor;
.link {
color: var(--warningColor);
color: $warningColor;
&:hover {
color: #e36c00;

View File

@@ -7,11 +7,11 @@
}
.hasError {
color: var(--dangerColor);
color: $dangerColor;
}
.isAdvanced {
color: var(--advancedFormLabelColor);
color: $advancedFormLabelColor;
}
@media only screen and (max-width: $breakpointLarge) {

View File

@@ -18,11 +18,11 @@
@add-mixin truncate;
margin-left: 15px;
color: var(--darkGray);
color: $darkGray;
font-size: $smallFontSize;
}
.divider {
border: none;
border-bottom: 1px solid var(--lightGray);
border-bottom: 1px solid $lightGray;
}

View File

@@ -18,7 +18,7 @@
flex: 1 10 0;
margin-left: 15px;
color: var(--gray);
color: $gray;
text-align: right;
font-size: $smallFontSize;
}

View File

@@ -24,7 +24,7 @@ function HintedSelectInputSelectedValue(props) {
>
<div className={styles.valueText}>
{
isMultiSelect ?
isMultiSelect &&
value.map((key, index) => {
const v = valuesMap[key];
return (
@@ -32,28 +32,26 @@ function HintedSelectInputSelectedValue(props) {
{v ? v.value : key}
</Label>
);
}) :
null
})
}
{
isMultiSelect ? null : value
!isMultiSelect && value
}
</div>
{
hint != null && includeHint ?
hint != null && includeHint &&
<div className={styles.hintText}>
{hint}
</div> :
null
</div>
}
</EnhancedSelectInputSelectedValue>
);
}
HintedSelectInputSelectedValue.propTypes = {
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]).isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
hint: PropTypes.string,
isMultiSelect: PropTypes.bool.isRequired,

View File

@@ -2,27 +2,26 @@
padding: 6px 16px;
width: 100%;
height: 35px;
border: 1px solid var(--inputBorderColor);
border: 1px solid $inputBorderColor;
border-radius: 4px;
background-color: var(--inputBackgroundColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor);
color: var(--textColor);
background-color: $white;
box-shadow: inset 0 1px 1px $inputBoxShadowColor;
&:focus {
outline: 0;
border-color: var(--inputFocusBorderColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor), 0 0 8px var(--inputFocusBoxShadowColor);
border-color: $inputFocusBorderColor;
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputFocusBoxShadowColor;
}
}
.hasError {
border-color: var(--inputErrorBorderColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor), 0 0 8px var(--inputErrorBoxShadowColor);
border-color: $inputErrorBorderColor;
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputErrorBoxShadowColor;
}
.hasWarning {
border-color: var(--inputWarningBorderColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor), 0 0 8px var(--inputWarningBoxShadowColor);
border-color: $inputWarningBorderColor;
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputWarningBoxShadowColor;
}
.hasButton {

View File

@@ -7,8 +7,8 @@
&.isFocused {
outline: 0;
border-color: var(--inputFocusBorderColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor), 0 0 8px var(--inputFocusBoxShadowColor);
border-color: $inputFocusBorderColor;
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputFocusBoxShadowColor;
}
}

View File

@@ -1,7 +1,7 @@
.itemContainer {
display: flex;
margin-bottom: 3px;
border-bottom: 1px solid var(--inputBorderColor);
border-bottom: 1px solid $inputBorderColor;
&:last-child {
margin-bottom: 0;

View File

@@ -5,7 +5,6 @@ import { kinds } from 'Helpers/Props';
function OAuthInput(props) {
const {
className,
label,
authorizing,
error,
@@ -13,21 +12,21 @@ function OAuthInput(props) {
} = props;
return (
<SpinnerErrorButton
className={className}
kind={kinds.PRIMARY}
isSpinning={authorizing}
error={error}
onPress={onPress}
>
{label}
</SpinnerErrorButton>
<div>
<SpinnerErrorButton
kind={kinds.PRIMARY}
isSpinning={authorizing}
error={error}
onPress={onPress}
>
{label}
</SpinnerErrorButton>
</div>
);
}
OAuthInput.propTypes = {
className: PropTypes.string,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
label: PropTypes.string.isRequired,
authorizing: PropTypes.bool.isRequired,
error: PropTypes.object,
onPress: PropTypes.func.isRequired

View File

@@ -1,44 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import SelectInput from './SelectInput';
function PlexMachineInput(props) {
const {
isFetching,
isDisabled,
value,
values,
onChange,
...otherProps
} = props;
const helpText = 'Authenticate with plex.tv to show servers to use for authentication';
return (
<>
{
isFetching ?
<LoadingIndicator /> :
<SelectInput
value={value}
values={values}
isDisabled={isDisabled}
onChange={onChange}
helpText={helpText}
{...otherProps}
/>
}
</>
);
}
PlexMachineInput.propTypes = {
isFetching: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool.isRequired,
value: PropTypes.string,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
onChange: PropTypes.func.isRequired
};
export default PlexMachineInput;

View File

@@ -1,115 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchPlexResources } from 'Store/Actions/settingsActions';
import PlexMachineInput from './PlexMachineInput';
function createMapStateToProps() {
return createSelector(
(state, { value }) => value,
(state) => state.oAuth,
(state) => state.settings.plex,
(value, oAuth, plex) => {
let values = [{ key: value, value }];
let isDisabled = true;
if (plex.isPopulated) {
const serverValues = plex.items.filter((item) => item.provides.includes('server')).map((item) => {
return ({
key: item.clientIdentifier,
value: `${item.name} / ${item.owned ? 'Owner' : 'User'} / ${item.clientIdentifier}`
});
});
if (serverValues.find((item) => item.key === value)) {
values = serverValues;
} else {
values = values.concat(serverValues);
}
isDisabled = false;
}
return ({
accessToken: oAuth.result?.accessToken,
values,
isDisabled,
...plex
});
}
);
}
const mapDispatchToProps = {
dispatchFetchPlexResources: fetchPlexResources
};
class PlexMachineInputConnector extends Component {
//
// Lifecycle
componentDidMount = () => {
const {
accessToken,
dispatchFetchPlexResources
} = this.props;
if (accessToken) {
dispatchFetchPlexResources({ accessToken });
}
};
componentDidUpdate(prevProps) {
const {
accessToken,
dispatchFetchPlexResources
} = this.props;
const oldToken = prevProps.accessToken;
if (accessToken && accessToken !== oldToken) {
dispatchFetchPlexResources({ accessToken });
}
}
render() {
const {
isFetching,
isPopulated,
isDisabled,
value,
values,
onChange
} = this.props;
return (
<PlexMachineInput
isFetching={isFetching}
isPopulated={isPopulated}
isDisabled={isDisabled}
value={value}
values={values}
onChange={onChange}
{...this.props}
/>
);
}
}
PlexMachineInputConnector.propTypes = {
dispatchFetchPlexResources: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool.isRequired,
error: PropTypes.object,
oAuth: PropTypes.object,
accessToken: PropTypes.string,
onChange: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(PlexMachineInputConnector);

View File

@@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import sortByName from 'Utilities/Array/sortByName';
import EnhancedSelectInput from './EnhancedSelectInput';
import SelectInput from './SelectInput';
function createMapStateToProps() {
return createSelector(
@@ -45,14 +45,40 @@ function createMapStateToProps() {
class QualityProfileSelectInputConnector extends Component {
//
// Lifecycle
componentDidMount() {
const {
name,
value,
values
} = this.props;
if (!value || !values.some((v) => v.key === value) ) {
const firstValue = _.find(values, (option) => !isNaN(parseInt(option.key)));
if (firstValue) {
this.onChange({ name, value: firstValue.key });
}
}
}
//
// Listeners
onChange = ({ name, value }) => {
this.props.onChange({ name, value: parseInt(value) });
};
//
// Render
render() {
return (
<EnhancedSelectInput
<SelectInput
{...this.props}
onChange={this.props.onChange}
onChange={this.onChange}
/>
);
}
@@ -60,7 +86,7 @@ class QualityProfileSelectInputConnector extends Component {
QualityProfileSelectInputConnector.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.arrayOf(PropTypes.string)]),
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
values: PropTypes.arrayOf(PropTypes.object).isRequired,
includeNoChange: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired

Some files were not shown because too many files have changed in this diff Show More