1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-12 15:30:39 -04:00

Compare commits

...

52 Commits

Author SHA1 Message Date
Qstick
17c6d11a47 fixup! New: Multiple Quality Profiles and Files Per Movie 2022-12-04 23:43:41 +00:00
ta264
a994502e59 Add api endpoint to generate the required login cookie
(cherry picked from commit 4180e2787a1ad5284873de4847f345b2c47df72a)
2022-12-04 23:43:41 +00:00
ta264
4154414adf Add OIDC and Plex authentication methods
(cherry picked from commit 3ff3de6b90704fba266833115cd9d03ace99aae9)
2022-12-04 23:43:41 +00:00
ta264
c5b12d074e Add explicit ApiKey requirement for ApiKey auth
(cherry picked from commit 8a3a998243e888e8f27c609f4bace5b42ad7ec50)
2022-12-04 13:06:25 -06:00
Qstick
578aa14770 Cleanup Translation Implementation in UI 2022-12-04 13:06:25 -06:00
Mark McDowall
da1bc0aa88 New: Calendar option for full color events
(cherry picked from commit 0210b5c5c1b5c56dce6f4c9f3f56366adba950d3)

Fixup Calendar for Full Color View, Final CSS fixups

Update localization
2022-12-04 13:06:25 -06:00
Qstick
a56d827744 Bump SQLite to 3.38.5 (1.0.116) 2022-12-04 13:06:25 -06:00
Qstick
a53234a107 Bump FFMpeg to 5.1.2 2022-12-04 13:06:24 -06:00
Marty Zalega
a015fa3255 Don't lowercase UrlBase in ConfigFileProvider
UrlBase should honour the case it is given.

(cherry picked from commit e1de523c89f7649e64f520b090bbdb2f56cc4b85)
2022-12-04 13:06:24 -06:00
Qstick
b2cb95829c New: Rework Movie Details view 2022-12-04 13:06:24 -06:00
Mark McDowall
64fd60a7d5 New: Migrate user passwords to Pbkdf2
(cherry picked from commit 269e72a2193b584476bec338ef41e6fb2e5cbea6)
2022-12-04 13:06:24 -06:00
Qstick
c3368d9e6c New: v4 API (DROP v3 AFTER TESTING PERIOD) 2022-12-04 13:06:20 -06:00
Qstick
583c5d501c Build Branch [REVERT] 2022-12-04 13:05:10 -06:00
Qstick
af6b16ec57 New: Multiple Quality Profiles and Files Per Movie 2022-12-04 13:05:05 -06:00
Qstick
7ac5a5843b New: Rework and Require Authentication
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-12-04 13:04:33 -06:00
Qstick
2465815890 Bump Version to 5 2022-12-04 13:04:33 -06:00
Weblate
c0caf65b69 Translated using Weblate (Dutch) [skip ci]
Currently translated at 95.2% (1100 of 1155 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Robin Flikkema <robin@robinflikkema.nl>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translation: Servarr/Radarr
2022-12-02 21:13:27 -06:00
Davo1624
cd889872de Properly parse H.265 4k as 4k quality (#7812)
* Update en.json

* Properly parse H265 for 4k quality

`.WEB-DL.4K.H265.AAC` parses properly for 4k quality
`.WEB-DL.4K.H.265.AAC` parses improperly as 480p
2022-11-29 22:00:36 -06:00
Servarr
6366e335bc Automated API Docs update 2022-11-29 21:57:01 -06:00
Qstick
41f10d098e Don't block task queue for queued update task when there are longer running tasks
Fixes #7538

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-11-29 21:44:03 -06:00
Qstick
b2c1698097 Fixed: False Positive HC in some cases
Fixes #7785
2022-11-29 20:52:23 -06:00
Mark McDowall
ed20487f30 Fixed: Handle Flood reporting errors for completed and stopped downloads
(cherry picked from commit f2b2eb69a3e8b271535bd92dc2f5cbfd45664daf)
2022-11-28 21:25:34 -06:00
Bruno Garcia
d1235adfc4 Sentry SDK v3.23.1
Co-authored-by: Bruno Garcia <bruno@Brunos-MacBook-Pro.local>
(cherry picked from commit de3cb07c57d762084c983336aa01b761a8e4b74a)
2022-11-28 21:30:06 +00:00
Qstick
561993e30c Fixed: Parse multiple languages for two letter cases
Fixes #7783
2022-11-25 19:16:29 -06:00
Weblate
14f8f89634 Translated using Weblate (Czech) [skip ci]
Currently translated at 90.9% (1051 of 1155 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1155 of 1155 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zalhera <tobias.bechen@gmail.com>
Co-authored-by: marapavelka <mara.pavelka@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translation: Servarr/Radarr
2022-11-24 18:40:38 -06:00
Qstick
874482dbce Webhook IndexerFlags to string list 2022-11-23 19:41:38 -06:00
Qstick
bae374c0c8 Additional Fields in Webhooks 2022-11-23 18:42:04 -06:00
Qstick
4f5ad899bb Fix Collection adding log 2022-11-23 18:31:48 -06:00
Qstick
adcd00d9fd Convert Notifiarr Payload to JSON, Standardize with Webhook 2022-11-22 21:17:25 -06:00
Servarr
d70d351ea2 Automated API Docs update 2022-11-22 19:25:54 -06:00
Qstick
ef90ac7041 Add theme property in API
Fixes #7772
2022-11-22 18:27:47 -06:00
Mark McDowall
aa8e886dab Added SECURITY.md
(cherry picked from commit 80af164385d9087a627142ca2281ae74ac0572af)

[common]
2022-11-21 19:23:43 -06:00
Mark McDowall
c7ee2c9166 Publish ApplicationStartingEvent during startup
(cherry picked from commit 5400bce1295bdc4198d2cfe0b9258bbb7ccf0852)
2022-11-21 18:47:59 -06:00
Robin Dadswell
5330815e1b fixup: some tests for log files 2022-11-21 18:42:38 -06:00
Qstick
296ec6c565 Fixup WindowsApp.cs 2022-11-20 15:08:26 -06:00
Qstick
bf89995984 Fixup SysTrayApp.cs 2022-11-20 13:09:42 -06:00
Qstick
44d7c54077 Enforce comment style in CS 2022-11-20 12:27:45 -06:00
Qstick
d37fac5343 Add PreSubstitutionRegex Capabilities
Fixes #7389
2022-11-20 12:20:50 -06:00
Qstick
ae8245c3c5 New: Reset Quality Definitions to default
(cherry picked from commit d5fff15f32fdb49768dcadd94c760678e650c884)
2022-11-20 12:20:50 -06:00
Qstick
850bfdcf82 New: Native Theme Engine
Co-Authored-By: Zak Saunders <1936903+thezak48@users.noreply.github.com>
2022-11-20 11:49:50 -06:00
Qstick
7d4865dea3 Fixed: Close all database connections on shutdown 2022-11-20 11:47:28 -06:00
Weblate
f9ef7e3578 Translated using Weblate (Dutch) [skip ci]
Currently translated at 94.7% (1088 of 1148 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1148 of 1148 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 99.9% (1147 of 1148 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1148 of 1148 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1148 of 1148 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 98.4% (1130 of 1148 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1148 of 1148 strings)

Translated using Weblate (Catalan) [skip ci]

Currently translated at 100.0% (1148 of 1148 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Sylvain Place <sylvain.place@etud.univ-jfc.fr>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: dtalens <databio@gmail.com>
Co-authored-by: reloxx <reloxx@interia.pl>
Co-authored-by: wauterr <wouter.rijken@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translation: Servarr/Radarr
2022-11-20 09:28:34 -06:00
Qstick
fb25422922 Fix test error due to DryIOC update 2022-11-19 16:59:53 -06:00
Mark McDowall
ac3d4bee35 Improve page scrollbar
New: Style scrollbar in Firefox
Fixed: Scrolling with click and drag

(cherry picked from commit 9bd783d49c91600d6575fc86e7bdd56858c213f1)
2022-11-19 16:31:37 -06:00
Mark McDowall
bb60510515 Added react-hooks lint rules
(cherry picked from commit 381d64259396582de8d63ada99333e42cf5e3189)
2022-11-19 16:30:16 -06:00
Qstick
5c9e11d7a0 Bump DryIoc to 5.3.0 2022-11-19 16:23:36 -06:00
Mark McDowall
6c01e8c91f Fixed: Testing SABnzbd when no categories are configured
(cherry picked from commit 0e31281828c737e3f6eecbb870960194888a970a)
2022-11-14 08:56:52 +00:00
Servarr
488a7d183e Automated API Docs update 2022-11-12 21:38:28 -06:00
Qstick
7fcb0d6e45 New: Base API info endpoint
(cherry picked from commit 5e57ffbcf9ac3a346d4bf2b692248393215bad89)
2022-11-12 19:56:41 -06:00
Jayson Reis
bd19c89f6e chore: Chain exceptions when trying to get rss's item size 2022-11-07 19:21:09 -06:00
Qstick
15e5ad5f84 Revert: Bump FFMpeg 2022-11-05 08:08:30 -05:00
Qstick
1732e23945 Fixed: Prevent Media Info error if no tags exist 2022-11-04 20:17:54 -05:00
775 changed files with 27996 additions and 3521 deletions

View File

@@ -42,7 +42,6 @@ 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

8
SECURITY.md Normal file
View File

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

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '4.3.1'
majorVersion: '5.0.0'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
@@ -27,6 +27,7 @@ trigger:
include:
- develop
- master
- zeus
pr:
branches:
@@ -1092,7 +1093,7 @@ stages:
projectVersion: '$(radarrVersion)'
extraProperties: |
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
sonar.coverage.exclusions=**/Radarr.Api.V3/**/*
sonar.coverage.exclusions=**/Radarr.Api.V*/**/*
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
- bash: |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -58,7 +58,7 @@ class AddNewMovieModalContentConnector extends Component {
tmdbId,
rootFolderPath,
monitor,
qualityProfileId,
qualityProfileIds,
minimumAvailability,
searchForMovie,
tags
@@ -68,7 +68,7 @@ class AddNewMovieModalContentConnector extends Component {
tmdbId,
rootFolderPath: rootFolderPath.value,
monitor: monitor.value,
qualityProfileId: qualityProfileId.value,
qualityProfileIds: qualityProfileIds.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,
qualityProfileId: PropTypes.object,
qualityProfileIds: PropTypes.arrayOf(PropTypes.object),
minimumAvailability: PropTypes.object.isRequired,
searchForMovie: PropTypes.object.isRequired,
tags: PropTypes.object.isRequired,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ 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';
@@ -242,7 +243,7 @@ class ImportMovieSelectMovie extends Component {
<FormInputButton
kind={kinds.DEFAULT}
spinnerIcon={icons.REFRESH}
canSpin={true}
ButtonComponent={SpinnerButton}
isSpinning={isFetching}
onPress={this.onRefreshPress}
>

View File

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

View File

@@ -4,6 +4,7 @@ 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 }) {
@@ -11,9 +12,11 @@ function App({ store, history }) {
<DocumentTitle title={window.Radarr.instanceName}>
<Provider store={store}>
<ConnectedRouter history={history}>
<PageConnector>
<AppRoutes app={App} />
</PageConnector>
<ApplyTheme>
<PageConnector>
<AppRoutes app={App} />
</PageConnector>
</ApplyTheme>
</ConnectedRouter>
</Provider>
</DocumentTitle>

View File

@@ -22,7 +22,7 @@ import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementCo
import MetadataSettings from 'Settings/Metadata/MetadataSettings';
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
import Profiles from 'Settings/Profiles/Profiles';
import Quality from 'Settings/Quality/Quality';
import QualityConnector from 'Settings/Quality/QualityConnector';
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={Quality}
component={QualityConnector}
/>
<Route

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,6 @@ 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) {
@@ -35,7 +34,7 @@ function CalendarEventQueueDetails(props) {
progress={progress}
size={20}
strokeWidth={2}
strokeColor={colors.purple}
strokeColor={'#7a43b6'}
/>
</div>
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,14 +26,16 @@ class CalendarOptionsModalContent extends Component {
firstDayOfWeek,
calendarWeekColumnHeader,
timeFormat,
enableColorImpairedMode
enableColorImpairedMode,
fullColorEvents
} = props;
this.state = {
firstDayOfWeek,
calendarWeekColumnHeader,
timeFormat,
enableColorImpairedMode
enableColorImpairedMode,
fullColorEvents
};
}
@@ -94,6 +96,7 @@ class CalendarOptionsModalContent extends Component {
const {
showMovieInformation,
showCutoffUnmetIcon,
fullColorEvents,
onModalClose
} = this.props;
@@ -136,6 +139,18 @@ 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>
@@ -176,7 +191,9 @@ class CalendarOptionsModalContent extends Component {
value={timeFormat}
onChange={this.onGlobalInputChange}
/>
</FormGroup><FormGroup>
</FormGroup>
<FormGroup>
<FormLabel>{translate('EnableColorImpairedMode')}</FormLabel>
<FormInputGroup
@@ -187,7 +204,6 @@ class CalendarOptionsModalContent extends Component {
onChange={this.onGlobalInputChange}
/>
</FormGroup>
</Form>
</FieldSet>
</ModalBody>
@@ -209,6 +225,7 @@ CalendarOptionsModalContent.propTypes = {
calendarWeekColumnHeader: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
enableColorImpairedMode: PropTypes.bool.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
dispatchSetCalendarOption: PropTypes.func.isRequired,
dispatchSaveUISettings: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ $hoverScale: 1.05;
&:hover {
z-index: 2;
box-shadow: 0 0 10px $black;
box-shadow: 0 0 10px var(--black);
transition: all 200ms ease-in;
.poster {
@@ -28,7 +28,7 @@ $hoverScale: 1.05;
.poster {
position: relative;
display: block;
background-color: $defaultColor;
background-color: var(--defaultColor);
}
.overlay {
@@ -44,7 +44,7 @@ $hoverScale: 1.05;
.overlayTitle {
padding: 5px;
color: $offWhite;
color: var(--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: $white;
color: var(--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: $radarrYellow;
color: var(--radarrYellow);
}
}
@@ -102,16 +102,16 @@ $hoverScale: 1.05;
position: relative;
display: block;
background-color: $defaultColor;
background-color: var(--defaultColor);
}
.monitorToggleButton {
composes: toggleButton from '~Components/MonitorToggleButton.css';
width: 25px;
color: $white;
color: var(--white);
&:hover {
color: $iconButtonHoverLightColor;
color: var(--iconButtonHoverLightColor);
}
}

View File

@@ -3,7 +3,7 @@
align-items: stretch;
overflow: hidden;
margin: 2px 4px;
border: 1px solid $borderColor;
border: 1px solid var(--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: $white;
color: $defaultColor;
background-color: var(--white);
color: var(--defaultColor);
}
.primary {
border-color: $primaryColor;
border-color: var(--primaryColor);
}
.danger {
border-color: $dangerColor;
border-color: var(--dangerColor);
}
.success {
border-color: $successColor;
border-color: var(--successColor);
}
.purple {
border-color: $purple;
border-color: var(--purple);
}
.warning {
border-color: $warningColor;
border-color: var(--warningColor);
}
.info {
border-color: $infoColor;
border-color: var(--infoColor);
}
.queue {
border-color: $queueColor;
border-color: var(--queueColor);
}

View File

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

View File

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

View File

@@ -96,7 +96,7 @@ class CollectionOverview extends Component {
render() {
const {
monitored,
qualityProfileId,
qualityProfileIds,
rootFolderPath,
genres,
id,
@@ -212,7 +212,7 @@ class CollectionOverview extends Component {
<span className={styles.qualityProfileName}>
{
<QualityProfileNameConnector
qualityProfileId={qualityProfileId}
qualityProfileIds={qualityProfileIds}
/>
}
</span>
@@ -325,7 +325,7 @@ class CollectionOverview extends Component {
CollectionOverview.propTypes = {
id: PropTypes.number.isRequired,
monitored: PropTypes.bool.isRequired,
qualityProfileId: PropTypes.number.isRequired,
qualityProfileIds: PropTypes.number.isRequired,
minimumAvailability: PropTypes.string.isRequired,
searchOnAdd: PropTypes.bool.isRequired,
rootFolderPath: PropTypes.string.isRequired,

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
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 {
@@ -132,7 +131,7 @@ CircularProgressBar.defaultProps = {
containerClassName: styles.circularProgressBarContainer,
size: 60,
strokeWidth: 5,
strokeColor: colors.radarrYellow,
strokeColor: '#ffc230',
showProgressText: false
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,33 +2,19 @@ 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,
canSpin,
ButtonComponent,
isLastButton,
...otherProps
} = props;
if (canSpin) {
return (
<SpinnerButton
className={classNames(
className,
!isLastButton && styles.middleButton
)}
kind={kinds.PRIMARY}
{...otherProps}
/>
);
}
return (
<Button
<ButtonComponent
className={classNames(
className,
!isLastButton && styles.middleButton
@@ -41,14 +27,14 @@ function FormInputButton(props) {
FormInputButton.propTypes = {
className: PropTypes.string.isRequired,
isLastButton: PropTypes.bool.isRequired,
canSpin: PropTypes.bool.isRequired
ButtonComponent: PropTypes.elementType.isRequired,
isLastButton: PropTypes.bool.isRequired
};
FormInputButton.defaultProps = {
className: styles.button,
isLastButton: true,
canSpin: false
ButtonComponent: Button,
isLastButton: true
};
export default FormInputButton;

View File

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

View File

@@ -20,6 +20,7 @@ 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';
@@ -62,6 +63,9 @@ function getComponent(type) {
case inputTypes.PATH:
return PathInputConnector;
case inputTypes.PLEX_MACHINE_SELECT:
return PlexMachineInputConnector;
case inputTypes.QUALITY_PROFILE_SELECT:
return QualityProfileSelectInputConnector;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import sortByName from 'Utilities/Array/sortByName';
import SelectInput from './SelectInput';
import EnhancedSelectInput from './EnhancedSelectInput';
function createMapStateToProps() {
return createSelector(
@@ -45,40 +45,14 @@ 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 (
<SelectInput
<EnhancedSelectInput
{...this.props}
onChange={this.onChange}
onChange={this.props.onChange}
/>
);
}
@@ -86,7 +60,7 @@ class QualityProfileSelectInputConnector extends Component {
QualityProfileSelectInputConnector.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.arrayOf(PropTypes.string)]),
values: PropTypes.arrayOf(PropTypes.object).isRequired,
includeNoChange: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired

View File

@@ -21,17 +21,17 @@
.movieFolder {
@add-mixin truncate;
color: $disabledColor;
color: var(--disabledColor);
}
.freeSpace {
margin-left: 15px;
color: $darkGray;
color: var(--darkGray);
font-size: $smallFontSize;
}
.isMissing {
margin-left: 15px;
color: $dangerColor;
color: var(--dangerColor);
font-size: $smallFontSize;
}

View File

@@ -20,13 +20,13 @@
.movieFolder {
@add-mixin truncate;
flex: 0 1 auto;
color: $disabledColor;
color: var(--disabledColor);
}
.freeSpace {
flex: 0 0 auto;
margin-left: 15px;
color: $gray;
color: var(--gray);
text-align: right;
font-size: $smallFontSize;
}

View File

@@ -12,6 +12,10 @@
composes: hasWarning from '~Components/Form/Input.css';
}
.hasButton {
composes: hasButton from '~Components/Form/Input.css';
}
.isDisabled {
opacity: 0.7;
cursor: not-allowed;

View File

@@ -28,6 +28,7 @@ class SelectInput extends Component {
isDisabled,
hasError,
hasWarning,
hasButton,
autoFocus,
onBlur
} = this.props;
@@ -38,6 +39,7 @@ class SelectInput extends Component {
className,
hasError && styles.hasError,
hasWarning && styles.hasWarning,
hasButton && styles.hasButton,
isDisabled && disabledClassName
)}
disabled={isDisabled}
@@ -80,6 +82,7 @@ SelectInput.propTypes = {
isDisabled: PropTypes.bool,
hasError: PropTypes.bool,
hasWarning: PropTypes.bool,
hasButton: PropTypes.bool,
autoFocus: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
onBlur: PropTypes.func

View File

@@ -7,8 +7,8 @@
&.isFocused {
outline: 0;
border-color: $inputFocusBorderColor;
box-shadow: inset 0 1px 1px $inputBoxShadowColor, 0 0 8px $inputFocusBoxShadowColor;
border-color: var(--inputFocusBorderColor);
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor), 0 0 8px var(--inputFocusBoxShadowColor);
}
}
@@ -20,4 +20,6 @@
width: 0%;
height: 31px;
border: none;
background-color: var(--inputBackground);
color: var(--textColor);
}

View File

@@ -3,7 +3,7 @@
}
.readOnly {
background-color: #eee;
background-color: var(--inputReadOnlyBackgroundColor);
}
.hasError {

View File

@@ -49,5 +49,5 @@
}
.readOnly {
background-color: #eee;
background-color: var(--inputReadOnlyBackgroundColor);
}

View File

@@ -1,5 +1,5 @@
.danger {
color: $dangerColor;
color: var(--dangerColor);
}
.default {
@@ -7,25 +7,33 @@
}
.disabled {
color: $disabledColor;
color: var(--disabledColor);
}
.info {
color: $infoColor;
color: var(--infoColor);
&:global(.darken) {
color: color(var(--infoColor) shade(30%));
}
}
.pink {
color: $pink;
color: var(--pink);
&:global(.darken) {
color: color(var(--pink) shade(30%));
}
}
.success {
color: $successColor;
color: var(--successColor);
}
.warning {
color: $warningColor;
color: var(--warningColor);
}
.purple {
color: $purple;
color: var(--purple);
}

View File

@@ -18,6 +18,7 @@ class Icon extends PureComponent {
kind,
size,
title,
darken,
isSpinning,
...otherProps
} = this.props;
@@ -26,7 +27,8 @@ class Icon extends PureComponent {
<FontAwesomeIcon
className={classNames(
className,
styles[kind]
styles[kind],
darken && 'darken'
)}
icon={name}
spin={isSpinning}
@@ -59,6 +61,7 @@ Icon.propTypes = {
kind: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
title: PropTypes.string,
darken: PropTypes.bool.isRequired,
isSpinning: PropTypes.bool.isRequired,
fixedWidth: PropTypes.bool.isRequired
};
@@ -66,6 +69,7 @@ Icon.propTypes = {
Icon.defaultProps = {
kind: kinds.DEFAULT,
size: 14,
darken: false,
isSpinning: false,
fixedWidth: false
};

View File

@@ -1,7 +1,7 @@
.label {
display: inline-block;
margin: 2px;
color: $white;
color: var(--white);
/** text-align: center; **/
white-space: nowrap;
line-height: 1;
@@ -10,7 +10,7 @@
.title {
margin-bottom: 2px;
color: $helpTextColor;
color: var(--helpTextColor);
font-size: 10px;
}
@@ -36,5 +36,5 @@
/** Outline **/
.outline {
background-color: $white;
background-color: var(--white);
}

View File

@@ -3,7 +3,7 @@
margin: 2px;
border: 1px solid;
border-radius: 2px;
color: $white;
color: var(--white);
text-align: center;
white-space: nowrap;
line-height: 1;
@@ -13,94 +13,95 @@
/** Kinds **/
.danger {
border-color: $dangerColor;
background-color: $dangerColor;
border-color: var(--dangerColor);
background-color: var(--dangerColor);
&.outline {
color: $dangerColor;
color: var(--dangerColor);
}
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, color($dangerColor shade(5%)), color($dangerColor shade(5%)) 5px, color($dangerColor shade(15%)) 5px, color($dangerColor shade(15%)) 10px);
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);
}
}
.default {
border-color: $themeLightColor;
background-color: $themeLightColor;
border-color: var(--themeLightColor);
background-color: var(--themeLightColor);
&.outline {
color: $themeLightColor;
color: var(--themeLightColor);
}
}
.disabled {
border-color: $disabledColor;
background-color: $disabledColor;
border-color: var(--disabledColor);
background-color: var(--disabledColor);
&.outline {
color: $disabledColor;
color: var(--offWhite);
}
}
.info {
border-color: $infoColor;
background-color: $infoColor;
border-color: var(--infoColor);
background-color: var(--infoColor);
color: var(--infoTextColor);
&.outline {
color: $infoColor;
color: var(--infoColor);
}
}
.inverse {
border-color: $lightGray;
background-color: $lightGray;
color: $defaultColor;
border-color: var(--inverseLabelColor);
background-color: var(--inverseLabelColor);
color: var(--inverseLabelTextColor);
&.outline {
background-color: $defaultColor !important;
color: $lightGray;
background-color: var(--inverseLabelTextColor) !important;
color: var(--inverseLabelColor);
}
}
.primary {
border-color: $primaryColor;
background-color: $primaryColor;
border-color: var(--primaryColor);
background-color: var(--primaryColor);
&.outline {
color: $primaryColor;
color: var(--primaryColor);
}
}
.success {
border-color: $successColor;
background-color: $successColor;
border-color: var(--successColor);
background-color: var(--successColor);
color: #eee;
&.outline {
color: $successColor;
color: var(--successColor);
}
}
.warning {
border-color: $warningColor;
background-color: $warningColor;
border-color: var(--warningColor);
background-color: var(--warningColor);
&.outline {
color: $warningColor;
color: var(--warningColor);
}
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, $warningColor, $warningColor 5px, color($warningColor tint(15%)) 5px, color($warningColor tint(15%)) 10px);
background: repeating-linear-gradient(45deg, var(--warningColor), var(--warningColor) 5px, color(var(--warningColor) tint(15%)) 5px, color(var(--warningColor) tint(15%)) 10px);
}
}
.queue {
border-color: $queueColor;
background-color: $queueColor;
border-color: var(--queueColor);
background-color: var(--queueColor);
&.outline {
color: $queueColor;
color: var(--queueColor);
}
}
@@ -125,5 +126,5 @@
/** Outline **/
.outline {
background-color: $white;
background-color: var(--disabledLabelColor);
}

View File

@@ -19,62 +19,62 @@
}
.danger {
border-color: $dangerBorderColor;
background-color: $dangerBackgroundColor;
color: $white;
border-color: var(--dangerBorderColor);
background-color: var(--dangerBackgroundColor);
color: var(--white);
&:hover {
border-color: $dangerHoverBorderColor;
background-color: $dangerHoverBackgroundColor;
color: $white;
border-color: var(--dangerHoverBorderColor);
background-color: var(--dangerHoverBackgroundColor);
color: var(--white);
}
}
.default {
border-color: $defaultBorderColor;
background-color: $defaultBackgroundColor;
color: $defaultColor;
border-color: var(--defaultBorderColor);
background-color: var(--defaultBackgroundColor);
color: var(--defaultColor);
&:hover {
border-color: $defaultHoverBorderColor;
background-color: $defaultHoverBackgroundColor;
color: $defaultColor;
border-color: var(--defaultHoverBorderColor);
background-color: var(--defaultHoverBackgroundColor);
color: var(--defaultColor);
}
}
.primary {
border-color: $primaryBorderColor;
background-color: $primaryBackgroundColor;
color: $white;
border-color: var(--primaryBorderColor);
background-color: var(--primaryBackgroundColor);
color: var(--white);
&:hover {
border-color: $primaryHoverBorderColor;
background-color: $primaryHoverBackgroundColor;
color: $white;
border-color: var(--primaryHoverBorderColor);
background-color: var(--primaryHoverBackgroundColor);
color: var(--white);
}
}
.success {
border-color: $successBorderColor;
background-color: $successBackgroundColor;
color: $white;
border-color: var(--successBorderColor);
background-color: var(--successBackgroundColor);
color: var(--white);
&:hover {
border-color: $successHoverBorderColor;
background-color: $successHoverBackgroundColor;
color: $white;
border-color: var(--successHoverBorderColor);
background-color: var(--successHoverBackgroundColor);
color: var(--white);
}
}
.warning {
border-color: $warningBorderColor;
background-color: $warningBackgroundColor;
color: $white;
border-color: var(--warningBorderColor);
background-color: var(--warningBackgroundColor);
color: var(--white);
&:hover {
border-color: $warningHoverBorderColor;
background-color: $warningHoverBackgroundColor;
color: $white;
border-color: var(--warningHoverBorderColor);
background-color: var(--warningHoverBackgroundColor);
color: var(--white);
}
}

View File

@@ -12,10 +12,10 @@
&:hover {
border: none;
background-color: inherit;
color: $iconButtonHoverColor;
color: var(--iconButtonHoverColor);
}
&.isDisabled {
color: $iconButtonDisabledColor;
color: var(--iconButtonDisabledColor);
}
}

View File

@@ -15,10 +15,10 @@
}
.to {
color: $linkColor;
color: var(--linkColor);
&:hover {
color: $linkHoverColor;
color: var(--linkHoverColor);
text-decoration: underline;
}
}

View File

@@ -26,7 +26,7 @@
.ripple {
position: absolute;
border: 2px solid #3a3f51;
border: 2px solid var(--themeDarkColor);
border-radius: 100%;
animation: rippleContainer 1.25s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8);
animation-fill-mode: both;

View File

@@ -10,12 +10,12 @@
}
&:hover {
color: $toobarButtonHoverColor;
color: var(--toobarButtonHoverColor);
}
}
.isDisabled {
color: $disabledColor;
color: var(--disabledColor);
pointer-events: none;
}

View File

@@ -2,7 +2,7 @@
z-index: $popperZIndex;
display: flex;
flex-direction: column;
background-color: $toolbarMenuItemBackgroundColor;
background-color: var(--toolbarMenuItemBackgroundColor);
line-height: 20px;
}

View File

@@ -5,19 +5,19 @@
padding: 10px 20px;
min-width: 150px;
max-width: 250px;
background-color: $toolbarMenuItemBackgroundColor;
color: $menuItemColor;
background-color: var(--toolbarMenuItemBackgroundColor);
color: var(--menuItemColor);
line-height: 20px;
&:hover,
&:focus {
background-color: $toolbarMenuItemHoverBackgroundColor;
color: $menuItemHoverColor;
background-color: var(--toolbarMenuItemHoverBackgroundColor);
color: var(--menuItemHoverColor);
text-decoration: none;
}
}
.isDisabled {
color: $disabledColor;
color: var(--disabledColor);
pointer-events: none;
}

View File

@@ -2,5 +2,5 @@
overflow: hidden;
min-height: 1px;
height: 1px;
background-color: $themeDarkColor;
background-color: var(--themeDarkColor);
}

View File

@@ -33,7 +33,7 @@ function ConfirmModal(props) {
return () => unbindShortcut('enter', onConfirm);
}
}, [isOpen, onConfirm]);
}, [bindShortcut, unbindShortcut, isOpen, onConfirm]);
return (
<Modal

View File

@@ -12,7 +12,7 @@
justify-content: center;
width: 100%;
height: 100%;
background-color: $modalBackdropBackgroundColor;
background-color: var(--modalBackdropBackgroundColor);
opacity: 1;
}

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