mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-06 13:31:28 -05:00
Compare commits
2 Commits
zeus-old
...
list-exclu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74382d7250 | ||
|
|
6659bc034c |
@@ -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
10
.esprintrc
Normal 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
2
.github/FUNDING.yml
vendored
@@ -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
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
14
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -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
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -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
41
.github/workflows/azuresync.yml
vendored
Normal 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
|
||||
5
.github/workflows/lock.yml
vendored
5
.github/workflows/lock.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/support.yml
vendored
4
.github/workflows/support.yml
vendored
@@ -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
23
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
|
||||
|
||||
2
docs.sh
2
docs.sh
@@ -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
|
||||
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
@@ -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]'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line filenames/match-exported
|
||||
const loaderUtils = require('loader-utils');
|
||||
|
||||
module.exports = function cssVariablesLoader(source) {
|
||||
|
||||
@@ -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'
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
.year {
|
||||
margin-left: 5px;
|
||||
color: var(--disabledColor);
|
||||
color: $disabledColor;
|
||||
}
|
||||
|
||||
.poster {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
.year {
|
||||
margin-left: 5px;
|
||||
color: var(--disabledColor);
|
||||
color: $disabledColor;
|
||||
}
|
||||
|
||||
.existing {
|
||||
|
||||
@@ -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> :
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.days {
|
||||
display: flex;
|
||||
border-right: 1px solid var(--calendarBorderColor);
|
||||
border-right: 1px solid $calendarBorderColor;
|
||||
}
|
||||
|
||||
.day,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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&';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
.year {
|
||||
margin-left: 5px;
|
||||
color: var(--disabledColor);
|
||||
color: $disabledColor;
|
||||
}
|
||||
|
||||
.poster {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
.container {
|
||||
&:hover {
|
||||
.content {
|
||||
background-color: var(--tableRowHoverBackgroundColor);
|
||||
background-color: $tableRowHoverBackgroundColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
color: var(--textColor);
|
||||
color: #3a3f51;
|
||||
font-size: 21px;
|
||||
line-height: inherit;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
|
||||
.faqLink {
|
||||
color: var(--alertWarningColor);
|
||||
color: $alertWarningColor;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
margin-bottom: 5px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tableRowHoverBackgroundColor);
|
||||
background-color: $tableRowHoverBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
|
||||
.or {
|
||||
margin: 0 3px;
|
||||
color: var(--themeDarkColor);
|
||||
color: $themeDarkColor;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
padding: 5px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tableRowHoverBackgroundColor);
|
||||
background-color: $tableRowHoverBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
}
|
||||
|
||||
.isDisabled {
|
||||
color: var(--disabledInputColor);
|
||||
color: $disabledInputColor;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
}
|
||||
|
||||
.hasError {
|
||||
color: var(--dangerColor);
|
||||
color: $dangerColor;
|
||||
}
|
||||
|
||||
.isAdvanced {
|
||||
color: var(--advancedFormLabelColor);
|
||||
color: $advancedFormLabelColor;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointLarge) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
flex: 1 10 0;
|
||||
margin-left: 15px;
|
||||
color: var(--gray);
|
||||
color: $gray;
|
||||
text-align: right;
|
||||
font-size: $smallFontSize;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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
Reference in New Issue
Block a user