Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66aeb19d88 | |||
| 455aca85bd | |||
| a29d794cbe | |||
| bb3123772f | |||
| 46a20e1dcd | |||
| 993144b67a | |||
| 1f209848dc | |||
| 3dafe44fed | |||
| 767e75ca45 | |||
| 1d4db26f17 | |||
| 757cb9a956 | |||
| 5dc3726023 | |||
| 5b2f30227b | |||
| ed94eee859 | |||
| 6eb271eee4 | |||
| 9fe205727c |
@@ -40,18 +40,9 @@ dotnet_naming_style.instance_field_style.capitalization = camel_case
|
|||||||
dotnet_naming_style.instance_field_style.required_prefix = _
|
dotnet_naming_style.instance_field_style.required_prefix = _
|
||||||
|
|
||||||
# Prefer "var" everywhere
|
# Prefer "var" everywhere
|
||||||
csharp_style_var_for_built_in_types = true
|
csharp_style_var_for_built_in_types = true:suggestion
|
||||||
csharp_style_var_when_type_is_apparent = true
|
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||||
csharp_style_var_elsewhere = true
|
csharp_style_var_elsewhere = true:suggestion
|
||||||
# Prefer "out" variables to be declared inline
|
|
||||||
csharp_style_inlined_variable_declaration = true
|
|
||||||
|
|
||||||
# Using directive is unnecessary.
|
|
||||||
dotnet_diagnostic.IDE0005.severity = error
|
|
||||||
# Use var instead of explicit type
|
|
||||||
dotnet_diagnostic.IDE0007.severity = error
|
|
||||||
# Inline variable declaration
|
|
||||||
dotnet_diagnostic.IDE0018.severity = error
|
|
||||||
|
|
||||||
# Stylecop Rules
|
# Stylecop Rules
|
||||||
dotnet_diagnostic.SA0001.severity = none
|
dotnet_diagnostic.SA0001.severity = none
|
||||||
@@ -270,7 +261,7 @@ dotnet_diagnostic.CA5397.severity = suggestion
|
|||||||
|
|
||||||
dotnet_diagnostic.SYSLIB0006.severity = none
|
dotnet_diagnostic.SYSLIB0006.severity = none
|
||||||
|
|
||||||
[*.{js,html,hbs,less,css,ts,tsx}]
|
[*.{js,html,js,hbs,less,css}]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
# Explicitly set bash scripts to have unix endings
|
# Explicitly set bash scripts to have unix endings
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
distribution/osx/Radarr text eol=lf
|
macOS/Radarr text eol=lf
|
||||||
|
|
||||||
# Custom for Visual Studio
|
# Custom for Visual Studio
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Discord first'
|
description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Reddit or Discord first'
|
||||||
labels: ['Type: Bug', 'Status: Needs Triage']
|
labels: ['Type: Bug', 'Status: Needs Triage']
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
@@ -65,18 +65,11 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Trace Logs? **Not Optional**
|
label: Trace Logs?
|
||||||
description: |
|
description: |
|
||||||
Trace Logs (https://wiki.servarr.com/radarr/troubleshooting#logging-and-log-files)
|
Trace Logs (https://wiki.servarr.com/radarr/troubleshooting#logging-and-log-files)
|
||||||
***Generally speaking, all bug reports MUST have trace logs provided.***
|
***Generally speaking, all bug reports must have trace logs provided.***
|
||||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||||
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Trace Logs have been provided as applicable. Reports will be closed if the required logs are not provided.
|
|
||||||
description: Trace logs are **generally required** and are not optional for all bug reports and contain `trace`. Info logs are invalid for bug reports and do not contain `debug` nor `trace`
|
|
||||||
options:
|
|
||||||
- label: I have read and followed the steps in the wiki link above and provided the required trace logs - the logs contain `trace` - that are relevant and show this issue.
|
|
||||||
required: true
|
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ contact_links:
|
|||||||
- name: Support via Discord
|
- name: Support via Discord
|
||||||
url: https://radarr.video/discord
|
url: https://radarr.video/discord
|
||||||
about: Chat with users and devs on support and setup related topics.
|
about: Chat with users and devs on support and setup related topics.
|
||||||
|
- name: Support via Reddit
|
||||||
|
url: https://reddit.com/r/radarr
|
||||||
|
about: Discuss and search thru support topics.
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
# Configuration for Label Actions - https://github.com/dessant/label-actions
|
|
||||||
|
|
||||||
'Type: Support':
|
|
||||||
comment: >
|
|
||||||
:wave: @{issue-author}, we use the issue tracker exclusively
|
|
||||||
for bug reports and feature requests. However, this issue appears
|
|
||||||
to be a support request. Please hop over onto our [Discord](https://radarr.video/discord).
|
|
||||||
close: true
|
|
||||||
close-reason: 'not planned'
|
|
||||||
|
|
||||||
'Status: Logs Needed':
|
|
||||||
comment: >
|
|
||||||
:wave: @{issue-author}, In order to help you further we'll need to see logs.
|
|
||||||
You'll need to enable trace logging and replicate the problem that you encountered.
|
|
||||||
Guidance on how to enable trace logging can be found in
|
|
||||||
our [troubleshooting guide](https://wiki.servarr.com/radarr/troubleshooting#logging-and-log-files).
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
'Area: API':
|
|
||||||
- src/Radarr.Api.V3/**/*
|
|
||||||
|
|
||||||
'Area: Db-migration':
|
|
||||||
- src/NzbDrone.Core/Datastore/Migration/*
|
|
||||||
|
|
||||||
'Area: Download Clients':
|
|
||||||
- src/NzbDrone.Core/Download/Clients/**/*
|
|
||||||
|
|
||||||
'Area: Import Lists':
|
|
||||||
- src/NzbDrone.Core/ImportLists/**/*
|
|
||||||
|
|
||||||
'Area: Indexer':
|
|
||||||
- src/NzbDrone.Core/Indexers/**/*
|
|
||||||
|
|
||||||
'Area: Notifications':
|
|
||||||
- src/NzbDrone.Core/Notifications/**/*
|
|
||||||
|
|
||||||
'Area: Organizer':
|
|
||||||
- src/NzbDrone.Core/Organizer/**/*
|
|
||||||
|
|
||||||
'Area: Parser':
|
|
||||||
- src/NzbDrone.Core/Parser/**/*
|
|
||||||
|
|
||||||
'Area: UI':
|
|
||||||
- frontend/**/*
|
|
||||||
- package.json
|
|
||||||
- yarn.lock
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
name: 'Label Actions'
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [labeled, unlabeled]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
action:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: dessant/label-actions@v3
|
|
||||||
with:
|
|
||||||
process-only: 'issues'
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
name: "Pull Request Labeler"
|
|
||||||
on:
|
|
||||||
- pull_request_target
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
triage:
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/labeler@v4
|
|
||||||
@@ -14,13 +14,13 @@ jobs:
|
|||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v4
|
- uses: dessant/lock-threads@v2
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
issue-inactive-days: '90'
|
issue-lock-inactive-days: '90'
|
||||||
exclude-issue-created-before: ''
|
issue-exclude-created-before: ''
|
||||||
exclude-any-issue-labels: ''
|
issue-exclude-labels: ''
|
||||||
add-issue-labels: ''
|
issue-lock-labels: ''
|
||||||
issue-comment: ''
|
issue-lock-comment: ''
|
||||||
issue-lock-reason: 'resolved'
|
issue-lock-reason: 'resolved'
|
||||||
process-only: ''
|
process-only: ''
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: 'Support requests'
|
||||||
|
|
||||||
|
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
|
||||||
|
with:
|
||||||
|
github-token: ${{ github.token }}
|
||||||
|
support-label: 'Type: Support'
|
||||||
|
issue-comment: >
|
||||||
|
:wave: @{issue-author}, we use the issue tracker exclusively
|
||||||
|
for bug reports and feature requests. However, this issue appears
|
||||||
|
to be a support request. Please hop over onto our [Discord](https://radarr.video/discord)
|
||||||
|
or [Subreddit](https://reddit.com/r/radarr)
|
||||||
|
close-issue: true
|
||||||
|
lock-issue: false
|
||||||
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 577 B |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 987 B After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 32 KiB |
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
|
[](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
|
||||||
[](https://translate.servarr.com/engage/radarr/?utm_source=widget)
|
[](https://translate.servarr.com/engage/radarr/?utm_source=widget)
|
||||||
[](https://wiki.servarr.com/radarr/installation/docker)
|
[](https://wiki.servarr.com/radarr/installation#docker)
|
||||||

|

|
||||||
[](#backers)
|
[](#backers)
|
||||||
[](#sponsors)
|
[](#sponsors)
|
||||||
@@ -35,6 +35,7 @@ Note that only one type of a given movie is supported. If you want both an 4k ve
|
|||||||
|
|
||||||
[](https://wiki.servarr.com/radarr)
|
[](https://wiki.servarr.com/radarr)
|
||||||
[](https://radarr.video/discord)
|
[](https://radarr.video/discord)
|
||||||
|
[](https://www.reddit.com/r/Radarr)
|
||||||
|
|
||||||
Note: GitHub Issues are for Bugs and Feature Requests Only
|
Note: GitHub Issues are for Bugs and Feature Requests Only
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ variables:
|
|||||||
testsFolder: './_tests'
|
testsFolder: './_tests'
|
||||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||||
majorVersion: '5.3.6'
|
majorVersion: '5.0.0'
|
||||||
minorVersion: $[counter('minorVersion', 2000)]
|
minorVersion: $[counter('minorVersion', 2000)]
|
||||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||||
sentryOrg: 'servarr'
|
sentryOrg: 'servarr'
|
||||||
sentryUrl: 'https://sentry.servarr.com'
|
sentryUrl: 'https://sentry.servarr.com'
|
||||||
dotnetVersion: '6.0.417'
|
dotnetVersion: '6.0.400'
|
||||||
nodeVersion: '16.X'
|
nodeVersion: '16.X'
|
||||||
innoVersion: '6.2.2'
|
innoVersion: '6.2.0'
|
||||||
windowsImage: 'windows-2022'
|
windowsImage: 'windows-2022'
|
||||||
linuxImage: 'ubuntu-20.04'
|
linuxImage: 'ubuntu-20.04'
|
||||||
macImage: 'macOS-11'
|
macImage: 'macOS-11'
|
||||||
@@ -27,10 +27,7 @@ trigger:
|
|||||||
include:
|
include:
|
||||||
- develop
|
- develop
|
||||||
- master
|
- master
|
||||||
paths:
|
- zeus
|
||||||
exclude:
|
|
||||||
- .github
|
|
||||||
- src/Radarr.Api.*/openapi.json
|
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
branches:
|
branches:
|
||||||
@@ -38,7 +35,6 @@ pr:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
exclude:
|
exclude:
|
||||||
- .github
|
|
||||||
- src/NzbDrone.Core/Localization/Core
|
- src/NzbDrone.Core/Localization/Core
|
||||||
- src/Radarr.Api.*/openapi.json
|
- src/Radarr.Api.*/openapi.json
|
||||||
|
|
||||||
@@ -216,8 +212,8 @@ stages:
|
|||||||
displayName: Fetch Frontend
|
displayName: Fetch Frontend
|
||||||
- bash: |
|
- bash: |
|
||||||
./build.sh --packages --installer
|
./build.sh --packages --installer
|
||||||
cp distribution/windows/setup/output/Radarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
|
cp setup/output/Radarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||||
cp distribution/windows/setup/output/Radarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x86-installer.exe
|
cp setup/output/Radarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x86-installer.exe
|
||||||
displayName: Create Installers
|
displayName: Create Installers
|
||||||
- publish: $(Build.ArtifactStagingDirectory)
|
- publish: $(Build.ArtifactStagingDirectory)
|
||||||
artifact: 'WindowsInstaller'
|
artifact: 'WindowsInstaller'
|
||||||
@@ -368,7 +364,7 @@ stages:
|
|||||||
- bash: |
|
- bash: |
|
||||||
echo "Uploading source maps to sentry"
|
echo "Uploading source maps to sentry"
|
||||||
curl -sL https://sentry.io/get-cli/ | bash
|
curl -sL https://sentry.io/get-cli/ | bash
|
||||||
RELEASENAME="Radarr@${RADARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
RELEASENAME="${RADARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
||||||
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
|
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
|
||||||
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
||||||
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
||||||
@@ -541,8 +537,8 @@ stages:
|
|||||||
testRunTitle: '$(testName) Unit Tests'
|
testRunTitle: '$(testName) Unit Tests'
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
|
|
||||||
- job: Unit_LinuxCore_Postgres14
|
- job: Unit_LinuxCore_Postgres
|
||||||
displayName: Unit Native LinuxCore with Postgres14 Database
|
displayName: Unit Native LinuxCore with Postgres Database
|
||||||
dependsOn: Prepare
|
dependsOn: Prepare
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
variables:
|
variables:
|
||||||
@@ -594,63 +590,7 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: 'LinuxCore Postgres14 Unit Tests'
|
testRunTitle: 'LinuxCore Postgres Unit Tests'
|
||||||
failTaskOnFailedTests: true
|
|
||||||
|
|
||||||
- job: Unit_LinuxCore_Postgres15
|
|
||||||
displayName: Unit Native LinuxCore with Postgres15 Database
|
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
variables:
|
|
||||||
pattern: 'Radarr.*.linux-core-x64.tar.gz'
|
|
||||||
artifactName: linux-x64-tests
|
|
||||||
Radarr__Postgres__Host: 'localhost'
|
|
||||||
Radarr__Postgres__Port: '5432'
|
|
||||||
Radarr__Postgres__User: 'radarr'
|
|
||||||
Radarr__Postgres__Password: 'radarr'
|
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: ${{ variables.linuxImage }}
|
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .net core'
|
|
||||||
inputs:
|
|
||||||
version: $(dotnetVersion)
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download Test Artifact
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: $(artifactName)
|
|
||||||
targetPath: $(testsFolder)
|
|
||||||
- bash: |
|
|
||||||
chmod a+x _tests/ffprobe
|
|
||||||
displayName: Make ffprobe Executable
|
|
||||||
- bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
|
|
||||||
displayName: Make Test Dummy Executable
|
|
||||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
|
||||||
- bash: |
|
|
||||||
docker run -d --name=postgres15 \
|
|
||||||
-e POSTGRES_PASSWORD=radarr \
|
|
||||||
-e POSTGRES_USER=radarr \
|
|
||||||
-p 5432:5432/tcp \
|
|
||||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
|
||||||
postgres:15
|
|
||||||
displayName: Start postgres
|
|
||||||
- bash: |
|
|
||||||
chmod a+x ${TESTSFOLDER}/test.sh
|
|
||||||
ls -lR ${TESTSFOLDER}
|
|
||||||
${TESTSFOLDER}/test.sh Linux Unit Test
|
|
||||||
displayName: Run Tests
|
|
||||||
- task: PublishTestResults@2
|
|
||||||
displayName: Publish Test Results
|
|
||||||
inputs:
|
|
||||||
testResultsFormat: 'NUnit'
|
|
||||||
testResultsFiles: '**/TestResult.xml'
|
|
||||||
testRunTitle: 'LinuxCore Postgres15 Unit Tests'
|
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
|
|
||||||
- stage: Integration
|
- stage: Integration
|
||||||
@@ -736,8 +676,8 @@ stages:
|
|||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
- job: Integration_LinuxCore_Postgres14
|
- job: Integration_LinuxCore_Postgres
|
||||||
displayName: Integration Native LinuxCore with Postgres14 Database
|
displayName: Integration Native LinuxCore with Postgres Database
|
||||||
dependsOn: Prepare
|
dependsOn: Prepare
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
variables:
|
variables:
|
||||||
@@ -794,70 +734,7 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
|
testRunTitle: 'Integration LinuxCore Postgres Database Integration Tests'
|
||||||
failTaskOnFailedTests: true
|
|
||||||
displayName: Publish Test Results
|
|
||||||
|
|
||||||
|
|
||||||
- job: Integration_LinuxCore_Postgres15
|
|
||||||
displayName: Integration Native LinuxCore with Postgres Database
|
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
variables:
|
|
||||||
pattern: 'Radarr.*.linux-core-x64.tar.gz'
|
|
||||||
Radarr__Postgres__Host: 'localhost'
|
|
||||||
Radarr__Postgres__Port: '5432'
|
|
||||||
Radarr__Postgres__User: 'radarr'
|
|
||||||
Radarr__Postgres__Password: 'radarr'
|
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: ${{ variables.linuxImage }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .net core'
|
|
||||||
inputs:
|
|
||||||
version: $(dotnetVersion)
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download Test Artifact
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: 'linux-x64-tests'
|
|
||||||
targetPath: $(testsFolder)
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download Build Artifact
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: Packages
|
|
||||||
itemPattern: '**/$(pattern)'
|
|
||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
|
||||||
- task: ExtractFiles@1
|
|
||||||
inputs:
|
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
|
||||||
displayName: Extract Package
|
|
||||||
- bash: |
|
|
||||||
mkdir -p ./bin/
|
|
||||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
|
||||||
displayName: Move Package Contents
|
|
||||||
- bash: |
|
|
||||||
docker run -d --name=postgres15 \
|
|
||||||
-e POSTGRES_PASSWORD=radarr \
|
|
||||||
-e POSTGRES_USER=radarr \
|
|
||||||
-p 5432:5432/tcp \
|
|
||||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
|
||||||
postgres:15
|
|
||||||
displayName: Start postgres
|
|
||||||
- bash: |
|
|
||||||
chmod a+x ${TESTSFOLDER}/test.sh
|
|
||||||
${TESTSFOLDER}/test.sh Linux Integration Test
|
|
||||||
displayName: Run Integration Tests
|
|
||||||
- task: PublishTestResults@2
|
|
||||||
inputs:
|
|
||||||
testResultsFormat: 'NUnit'
|
|
||||||
testResultsFiles: '**/TestResult.xml'
|
|
||||||
testRunTitle: 'Integration LinuxCore Postgres15 Database Integration Tests'
|
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
@@ -1216,7 +1093,7 @@ stages:
|
|||||||
projectVersion: '$(radarrVersion)'
|
projectVersion: '$(radarrVersion)'
|
||||||
extraProperties: |
|
extraProperties: |
|
||||||
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
|
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.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
|
||||||
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
|
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -1242,7 +1119,6 @@ stages:
|
|||||||
- stage: Report_Out
|
- stage: Report_Out
|
||||||
dependsOn:
|
dependsOn:
|
||||||
- Analyze
|
- Analyze
|
||||||
- Installer
|
|
||||||
- Unit_Test
|
- Unit_Test
|
||||||
- Integration
|
- Integration
|
||||||
- Automation
|
- Automation
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ UpdateVersionNumber()
|
|||||||
echo "Updating Version Info"
|
echo "Updating Version Info"
|
||||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$RADARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$RADARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
||||||
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
||||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$RADARRVERSION<\/string>/g" distribution/osx/Radarr.app/Contents/Info.plist
|
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$RADARRVERSION<\/string>/g" macOS/Radarr.app/Contents/Info.plist
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ PackageMacOSApp()
|
|||||||
|
|
||||||
rm -rf $folder
|
rm -rf $folder
|
||||||
mkdir -p $folder
|
mkdir -p $folder
|
||||||
cp -r distribution/osx/Radarr.app $folder
|
cp -r macOS/Radarr.app $folder
|
||||||
mkdir -p $folder/Radarr.app/Contents/MacOS
|
mkdir -p $folder/Radarr.app/Contents/MacOS
|
||||||
|
|
||||||
echo "Copying Binaries"
|
echo "Copying Binaries"
|
||||||
@@ -246,7 +246,7 @@ BuildInstaller()
|
|||||||
local framework="$1"
|
local framework="$1"
|
||||||
local runtime="$2"
|
local runtime="$2"
|
||||||
|
|
||||||
./_inno/ISCC.exe distribution/windows/setup/radarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
./_inno/ISCC.exe setup/radarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallInno()
|
InstallInno()
|
||||||
@@ -254,7 +254,7 @@ InstallInno()
|
|||||||
ProgressStart "Installing portable Inno Setup"
|
ProgressStart "Installing portable Inno Setup"
|
||||||
|
|
||||||
rm -rf _inno
|
rm -rf _inno
|
||||||
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.2}.exe"
|
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.0}.exe"
|
||||||
mkdir _inno
|
mkdir _inno
|
||||||
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
|
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
|
||||||
rm innosetup.exe
|
rm innosetup.exe
|
||||||
@@ -392,21 +392,22 @@ then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$LINT" = "YES" || "$FRONTEND" = "YES" ]];
|
if [ "$FRONTEND" = "YES" ];
|
||||||
then
|
then
|
||||||
YarnInstall
|
YarnInstall
|
||||||
|
RunWebpack
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$LINT" = "YES" ];
|
if [ "$LINT" = "YES" ];
|
||||||
then
|
then
|
||||||
|
if [ -z "$FRONTEND" ];
|
||||||
|
then
|
||||||
|
YarnInstall
|
||||||
|
fi
|
||||||
|
|
||||||
LintUI
|
LintUI
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$FRONTEND" = "YES" ];
|
|
||||||
then
|
|
||||||
RunWebpack
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$PACKAGES" = "YES" ];
|
if [ "$PACKAGES" = "YES" ];
|
||||||
then
|
then
|
||||||
UpdateVersionNumber
|
UpdateVersionNumber
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
nzbdrone {version} {branch}; urgency=low
|
||||||
|
|
||||||
|
* Automatic Release.
|
||||||
|
|
||||||
|
-- NzbDrone <contact@nzbdrone.com> Mon, 26 Aug 2013 00:00:00 -0700
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
8
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Section: web
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Sonarr <contact@nzbdrone.com>
|
||||||
|
Source: nzbdrone
|
||||||
|
Homepage: https://sonarr.tv
|
||||||
|
Vcs-Git: git@github.com:Sonarr/Sonarr.git
|
||||||
|
Vcs-Browser: https://github.com/Sonarr/Sonarr
|
||||||
|
|
||||||
|
Package: nzbdrone
|
||||||
|
Architecture: all
|
||||||
|
Depends: libmono-cil-dev (>= 3.2), sqlite3 (>= 3.7), mediainfo (>= 0.7.52)
|
||||||
|
Description: Sonarr is an internet PVR
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: nzbdrone
|
||||||
|
Source: https://github.com/Sonarr/Sonarr
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2010-2016 Sonarr <hello@sonarr.tv>
|
||||||
|
|
||||||
|
License: GPL-3.0+
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
.
|
||||||
|
This package is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the GNU General
|
||||||
|
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
nzbdrone_bin/* opt/NzbDrone
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
# -*- makefile -*-
|
||||||
|
# Sample debian/rules that uses debhelper.
|
||||||
|
# This file was originally written by Joey Hess and Craig Small.
|
||||||
|
# As a special exception, when this file is copied by dh-make into a
|
||||||
|
# dh-make output file, you may use that output file without restriction.
|
||||||
|
# This special exception was added by Craig Small in version 0.37 of dh-make.
|
||||||
|
|
||||||
|
# Uncomment this to turn on verbose mode.
|
||||||
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
@@ -3,9 +3,9 @@ PLATFORM=$1
|
|||||||
if [ "$PLATFORM" = "Windows" ]; then
|
if [ "$PLATFORM" = "Windows" ]; then
|
||||||
RUNTIME="win-x64"
|
RUNTIME="win-x64"
|
||||||
elif [ "$PLATFORM" = "Linux" ]; then
|
elif [ "$PLATFORM" = "Linux" ]; then
|
||||||
RUNTIME="linux-x64"
|
WHERE="linux-x64"
|
||||||
elif [ "$PLATFORM" = "Mac" ]; then
|
elif [ "$PLATFORM" = "Mac" ]; then
|
||||||
RUNTIME="osx-x64"
|
WHERE="osx-x64"
|
||||||
else
|
else
|
||||||
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -27,12 +27,12 @@ dotnet clean $slnFile -c Release
|
|||||||
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
||||||
|
|
||||||
dotnet new tool-manifest
|
dotnet new tool-manifest
|
||||||
dotnet tool install --version 6.5.0 Swashbuckle.AspNetCore.Cli
|
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
|
sleep 45
|
||||||
|
|
||||||
kill %1
|
kill %1
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"remove-empty-rulesets": true,
|
||||||
|
"always-semicolon": true,
|
||||||
|
"color-case": "lower",
|
||||||
|
"block-indent": " ",
|
||||||
|
"color-shorthand": false,
|
||||||
|
"element-case": "lower",
|
||||||
|
"eof-newline": true,
|
||||||
|
"leading-zero": true,
|
||||||
|
"quotes": "double",
|
||||||
|
"sort-order-fallback": "abc",
|
||||||
|
"space-before-colon": "",
|
||||||
|
"space-after-colon": " ",
|
||||||
|
"space-before-combinator": " ",
|
||||||
|
"space-after-combinator": " ",
|
||||||
|
"space-between-declarations": "\n",
|
||||||
|
"space-before-opening-brace": " ",
|
||||||
|
"space-after-opening-brace": "\n",
|
||||||
|
"space-after-selector-delimiter": " ",
|
||||||
|
"space-before-selector-delimiter": "",
|
||||||
|
"space-before-closing-brace": "\n",
|
||||||
|
"strip-spaces": true,
|
||||||
|
"tab-size": true,
|
||||||
|
"unitless-zero": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
{
|
||||||
|
"indent": {
|
||||||
|
"value": " ",
|
||||||
|
"FunctionExpression": 1,
|
||||||
|
"ArrayExpression": 1,
|
||||||
|
"ObjectExpression": 1
|
||||||
|
},
|
||||||
|
"lineBreak": {
|
||||||
|
"value": "\n",
|
||||||
|
|
||||||
|
"before": {
|
||||||
|
"ArrayPatternClosing": 0,
|
||||||
|
"ArrayPatternComma": 0,
|
||||||
|
"ArrayPatternOpening": 0,
|
||||||
|
"ArrowFunctionExpressionArrow": 0,
|
||||||
|
"ArrowFunctionExpressionClosingBrace": ">=1",
|
||||||
|
"ArrowFunctionExpressionOpeningBrace": 0,
|
||||||
|
"AssignmentExpression": ">=1",
|
||||||
|
"AssignmentOperator": 0,
|
||||||
|
"BlockStatement": 0,
|
||||||
|
"BreakKeyword": ">=1",
|
||||||
|
"CallExpression": -1,
|
||||||
|
"CallExpressionClosingParentheses": -1,
|
||||||
|
"CallExpressionOpeningParentheses": 0,
|
||||||
|
"CatchClosingBrace": ">=1",
|
||||||
|
"CatchKeyword": 0,
|
||||||
|
"CatchOpeningBrace": 0,
|
||||||
|
"ClassDeclaration": ">=1",
|
||||||
|
"ClassDeclarationClosingBrace": ">=1",
|
||||||
|
"ClassDeclarationOpeningBrace": 0,
|
||||||
|
"ConditionalExpression": ">=1",
|
||||||
|
"DeleteOperator": ">=1",
|
||||||
|
"DoWhileStatement": ">=1",
|
||||||
|
"DoWhileStatementClosingBrace": ">=1",
|
||||||
|
"DoWhileStatementOpeningBrace": 0,
|
||||||
|
"ElseIfStatement": 0,
|
||||||
|
"ElseIfStatementClosingBrace": ">=1",
|
||||||
|
"ElseIfStatementOpeningBrace": 0,
|
||||||
|
"ElseStatement": 0,
|
||||||
|
"ElseStatementClosingBrace": ">=1",
|
||||||
|
"ElseStatementOpeningBrace": 0,
|
||||||
|
"EmptyStatement": -1,
|
||||||
|
"EndOfFile": -1,
|
||||||
|
"FinallyClosingBrace": ">=1",
|
||||||
|
"FinallyKeyword": -1,
|
||||||
|
"FinallyOpeningBrace": 0,
|
||||||
|
"ForInStatement": ">=1",
|
||||||
|
"ForInStatementClosingBrace": ">=1",
|
||||||
|
"ForInStatementExpressionClosing": 0,
|
||||||
|
"ForInStatementExpressionOpening": 0,
|
||||||
|
"ForInStatementOpeningBrace": 0,
|
||||||
|
"ForStatement": ">=1",
|
||||||
|
"ForStatementClosingBrace": ">=1",
|
||||||
|
"ForStatementExpressionClosing": "<2",
|
||||||
|
"ForStatementExpressionOpening": 0,
|
||||||
|
"ForStatementOpeningBrace": 0,
|
||||||
|
"FunctionDeclaration": ">=1",
|
||||||
|
"FunctionDeclarationClosingBrace": ">=1",
|
||||||
|
"FunctionDeclarationOpeningBrace": 0,
|
||||||
|
"FunctionExpression": 0,
|
||||||
|
"FunctionExpressionClosingBrace": 1,
|
||||||
|
"FunctionExpressionOpeningBrace":0,
|
||||||
|
"IIFEClosingParentheses": 0,
|
||||||
|
"IfStatement": ">=1",
|
||||||
|
"IfStatementClosingBrace": ">=1",
|
||||||
|
"IfStatementOpeningBrace": 0,
|
||||||
|
"LogicalExpression": -1,
|
||||||
|
"MemberExpressionClosing": 0,
|
||||||
|
"MemberExpressionOpening": 0,
|
||||||
|
"MemberExpressionPeriod": -1,
|
||||||
|
"MethodDefinition": ">=1",
|
||||||
|
"ObjectExpressionClosingBrace": "<=1",
|
||||||
|
"ObjectPatternClosingBrace": 0,
|
||||||
|
"ObjectPatternComma": 0,
|
||||||
|
"ObjectPatternOpeningBrace": 0,
|
||||||
|
"ParameterDefault": 0,
|
||||||
|
"Property": "<=2",
|
||||||
|
"PropertyValue": 0,
|
||||||
|
"ReturnStatement": -1,
|
||||||
|
"SwitchClosingBrace": ">=1",
|
||||||
|
"SwitchOpeningBrace": 0,
|
||||||
|
"ThisExpression": -1,
|
||||||
|
"ThrowStatement": ">=1",
|
||||||
|
"TryClosingBrace": ">=1",
|
||||||
|
"TryKeyword": -1,
|
||||||
|
"TryOpeningBrace": 0,
|
||||||
|
"VariableDeclaration": ">=1",
|
||||||
|
"VariableDeclarationSemiColon": 0,
|
||||||
|
"VariableDeclarationWithoutInit": ">=1",
|
||||||
|
"VariableName": ">=1",
|
||||||
|
"VariableValue": 0,
|
||||||
|
"WhileStatement": ">=1",
|
||||||
|
"WhileStatementClosingBrace": ">=1",
|
||||||
|
"WhileStatementOpeningBrace": 0
|
||||||
|
},
|
||||||
|
|
||||||
|
"after": {
|
||||||
|
"ArrayPatternClosing": 0,
|
||||||
|
"ArrayPatternComma": 0,
|
||||||
|
"ArrayPatternOpening": 0,
|
||||||
|
"ArrowFunctionExpressionArrow": 0,
|
||||||
|
"ArrowFunctionExpressionClosingBrace": -1,
|
||||||
|
"ArrowFunctionExpressionOpeningBrace": ">=1",
|
||||||
|
"AssignmentExpression": ">=1",
|
||||||
|
"AssignmentOperator": 0,
|
||||||
|
"BlockStatement": 0,
|
||||||
|
"BreakKeyword": -1,
|
||||||
|
"CallExpression": -1,
|
||||||
|
"CallExpressionClosingParentheses": -1,
|
||||||
|
"CallExpressionOpeningParentheses": -1,
|
||||||
|
"CatchClosingBrace": ">=0",
|
||||||
|
"CatchKeyword": 0,
|
||||||
|
"CatchOpeningBrace": ">=1",
|
||||||
|
"ClassDeclaration": ">=1",
|
||||||
|
"ClassDeclarationClosingBrace": ">=1",
|
||||||
|
"ClassDeclarationOpeningBrace": ">=1",
|
||||||
|
"ConditionalExpression": ">=1",
|
||||||
|
"DeleteOperator": ">=1",
|
||||||
|
"DoWhileStatement": ">=1",
|
||||||
|
"DoWhileStatementClosingBrace": 0,
|
||||||
|
"DoWhileStatementOpeningBrace": ">=1",
|
||||||
|
"ElseIfStatement": ">=1",
|
||||||
|
"ElseIfStatementClosingBrace": ">=1",
|
||||||
|
"ElseIfStatementOpeningBrace": ">=1",
|
||||||
|
"ElseStatement": ">=1",
|
||||||
|
"ElseStatementClosingBrace": ">=1",
|
||||||
|
"ElseStatementOpeningBrace": ">=1",
|
||||||
|
"EmptyStatement": -1,
|
||||||
|
"FinallyClosingBrace": ">=1",
|
||||||
|
"FinallyKeyword": -1,
|
||||||
|
"FinallyOpeningBrace": ">=1",
|
||||||
|
"ForInStatement": ">=1",
|
||||||
|
"ForInStatementClosingBrace": ">=1",
|
||||||
|
"ForInStatementExpressionClosing": -1,
|
||||||
|
"ForInStatementExpressionOpening": "<2",
|
||||||
|
"ForInStatementOpeningBrace": ">=1",
|
||||||
|
"ForStatement": ">=1",
|
||||||
|
"ForStatementClosingBrace": ">=1",
|
||||||
|
"ForStatementExpressionClosing": -1,
|
||||||
|
"ForStatementExpressionOpening": "<2",
|
||||||
|
"ForStatementOpeningBrace": ">=1",
|
||||||
|
"FunctionDeclaration": ">=1",
|
||||||
|
"FunctionDeclarationClosingBrace": ">=1",
|
||||||
|
"FunctionDeclarationOpeningBrace": ">=1",
|
||||||
|
"FunctionExpression": 0,
|
||||||
|
"FunctionExpressionClosingBrace": -1,
|
||||||
|
"FunctionExpressionOpeningBrace": 1,
|
||||||
|
"IIFEOpeningParentheses": 0,
|
||||||
|
"IfStatement": ">=1",
|
||||||
|
"IfStatementClosingBrace": ">=1",
|
||||||
|
"IfStatementOpeningBrace": ">=1",
|
||||||
|
"LogicalExpression": -1,
|
||||||
|
"MemberExpressionClosing": 0,
|
||||||
|
"MemberExpressionOpening": 0,
|
||||||
|
"MemberExpressionPeriod": 0,
|
||||||
|
"MethodDefinition": ">=1",
|
||||||
|
"ObjectExpressionOpeningBrace": "<=1",
|
||||||
|
"ObjectPatternClosingBrace": 0,
|
||||||
|
"ObjectPatternComma": 0,
|
||||||
|
"ObjectPatternOpeningBrace": 0,
|
||||||
|
"ParameterDefault": 0,
|
||||||
|
"Property": -1,
|
||||||
|
"PropertyName": 0,
|
||||||
|
"ReturnStatement": -1,
|
||||||
|
"SwitchCaseColon": ">=1",
|
||||||
|
"SwitchClosingBrace": ">=1",
|
||||||
|
"SwitchOpeningBrace": ">=1",
|
||||||
|
"ThisExpression": 0,
|
||||||
|
"ThrowStatement": ">=1",
|
||||||
|
"TryClosingBrace": 0,
|
||||||
|
"TryKeyword": -1,
|
||||||
|
"TryOpeningBrace": ">=1",
|
||||||
|
"VariableDeclaration": ">=1",
|
||||||
|
"VariableDeclarationSemiColon": ">=1",
|
||||||
|
"VariableValue": -1,
|
||||||
|
"WhileStatement": ">=1",
|
||||||
|
"WhileStatementClosingBrace": ">=1",
|
||||||
|
"WhileStatementOpeningBrace": ">=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"whiteSpace": {
|
||||||
|
"value": " ",
|
||||||
|
"removeTrailing": 1,
|
||||||
|
"before": {
|
||||||
|
"ArgumentComma": 0,
|
||||||
|
"ArgumentList": 0,
|
||||||
|
"ArgumentListArrayExpression": 0,
|
||||||
|
"ArgumentListFunctionExpression": 1,
|
||||||
|
"ArgumentListObjectExpression": 0,
|
||||||
|
"ArrayExpressionClosing": 0,
|
||||||
|
"ArrayExpressionComma": 0,
|
||||||
|
"ArrayExpressionOpening": 1,
|
||||||
|
"AssignmentOperator": 1,
|
||||||
|
"BinaryExpression": 0,
|
||||||
|
"BinaryExpressionOperator": 1,
|
||||||
|
"BlockComment": 1,
|
||||||
|
"CallExpression": 1,
|
||||||
|
"CatchClosingBrace": 1,
|
||||||
|
"CatchKeyword": 1,
|
||||||
|
"CatchOpeningBrace": 1,
|
||||||
|
"CatchParameterList": 0,
|
||||||
|
"CommaOperator": 0,
|
||||||
|
"ConditionalExpressionAlternate": 1,
|
||||||
|
"ConditionalExpressionConsequent": 1,
|
||||||
|
"DoWhileStatementClosingBrace": 1,
|
||||||
|
"DoWhileStatementConditional": 1,
|
||||||
|
"DoWhileStatementOpeningBrace": 1,
|
||||||
|
"ElseIfStatementClosingBrace": 1,
|
||||||
|
"ElseIfStatementOpeningBrace": 1,
|
||||||
|
"ElseStatementClosingBrace": 1,
|
||||||
|
"ElseStatementOpeningBrace": 1,
|
||||||
|
"EmptyStatement": 0,
|
||||||
|
"ExpressionClosingParentheses": 0,
|
||||||
|
"FinallyClosingBrace": 1,
|
||||||
|
"FinallyKeyword": -1,
|
||||||
|
"FinallyOpeningBrace": 1,
|
||||||
|
"ForInStatement": 1,
|
||||||
|
"ForInStatementClosingBrace": 1,
|
||||||
|
"ForInStatementExpressionClosing": 0,
|
||||||
|
"ForInStatementExpressionOpening": 1,
|
||||||
|
"ForInStatementOpeningBrace": 1,
|
||||||
|
"ForStatement": 1,
|
||||||
|
"ForStatementClosingBrace": 1,
|
||||||
|
"ForStatementExpressionClosing": 0,
|
||||||
|
"ForStatementExpressionOpening": 1,
|
||||||
|
"ForStatementOpeningBrace": 1,
|
||||||
|
"ForStatementSemicolon": 0,
|
||||||
|
"FunctionDeclarationClosingBrace": 1,
|
||||||
|
"FunctionDeclarationOpeningBrace": 1,
|
||||||
|
"FunctionExpressionClosingBrace": 1,
|
||||||
|
"FunctionExpressionOpeningBrace": 1,
|
||||||
|
"IfStatementClosingBrace": 1,
|
||||||
|
"IfStatementConditionalClosing": 0,
|
||||||
|
"IfStatementConditionalOpening": 1,
|
||||||
|
"IfStatementOpeningBrace": 1,
|
||||||
|
"LineComment": 1,
|
||||||
|
"LogicalExpressionOperator": 1,
|
||||||
|
"MemberExpressionClosing": 0,
|
||||||
|
"ObjectExpressionClosingBrace": 1,
|
||||||
|
"ParameterComma": 0,
|
||||||
|
"ParameterList": 0,
|
||||||
|
"Property": 1,
|
||||||
|
"PropertyName": 1,
|
||||||
|
"PropertyValue": 1,
|
||||||
|
"SwitchDiscriminantClosing": 0,
|
||||||
|
"SwitchDiscriminantOpening": 1,
|
||||||
|
"ThrowKeyword": 1,
|
||||||
|
"TryClosingBrace": 1,
|
||||||
|
"TryKeyword": -1,
|
||||||
|
"TryOpeningBrace": 1,
|
||||||
|
"UnaryExpressionOperator": 0,
|
||||||
|
"VariableName": 1,
|
||||||
|
"VariableValue": 1,
|
||||||
|
"WhileStatementClosingBrace": 1,
|
||||||
|
"WhileStatementConditionalClosing": 0,
|
||||||
|
"WhileStatementConditionalOpening": 1,
|
||||||
|
"WhileStatementOpeningBrace": 1
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"ArgumentComma": 1,
|
||||||
|
"ArgumentList": 0,
|
||||||
|
"ArgumentListArrayExpression": 1,
|
||||||
|
"ArgumentListFunctionExpression": 1,
|
||||||
|
"ArgumentListObjectExpression": 0,
|
||||||
|
"ArrayExpressionClosing": 0,
|
||||||
|
"ArrayExpressionComma": 1,
|
||||||
|
"ArrayExpressionOpening": 0,
|
||||||
|
"AssignmentOperator": 1,
|
||||||
|
"BinaryExpression": 0,
|
||||||
|
"BinaryExpressionOperator": 1,
|
||||||
|
"BlockComment": 1,
|
||||||
|
"CallExpression": 0,
|
||||||
|
"CatchClosingBrace": 1,
|
||||||
|
"CatchKeyword": 1,
|
||||||
|
"CatchOpeningBrace": 1,
|
||||||
|
"CatchParameterList": 0,
|
||||||
|
"CommaOperator": 1,
|
||||||
|
"ConditionalExpressionConsequent": 1,
|
||||||
|
"ConditionalExpressionTest": 1,
|
||||||
|
"DoWhileStatementBody": 1,
|
||||||
|
"DoWhileStatementClosingBrace": 1,
|
||||||
|
"DoWhileStatementOpeningBrace": 1,
|
||||||
|
"ElseIfStatementClosingBrace": 1,
|
||||||
|
"ElseIfStatementOpeningBrace": 1,
|
||||||
|
"ElseStatementClosingBrace": 1,
|
||||||
|
"ElseStatementOpeningBrace": 1,
|
||||||
|
"EmptyStatement": 0,
|
||||||
|
"ExpressionOpeningParentheses": 0,
|
||||||
|
"FinallyClosingBrace": 1,
|
||||||
|
"FinallyKeyword": -1,
|
||||||
|
"FinallyOpeningBrace": 1,
|
||||||
|
"ForInStatement": 1,
|
||||||
|
"ForInStatementClosingBrace": 1,
|
||||||
|
"ForInStatementExpressionClosing": 1,
|
||||||
|
"ForInStatementExpressionOpening": 0,
|
||||||
|
"ForInStatementOpeningBrace": 1,
|
||||||
|
"ForStatement": 1,
|
||||||
|
"ForStatementClosingBrace": 1,
|
||||||
|
"ForStatementExpressionClosing": 1,
|
||||||
|
"ForStatementExpressionOpening": 0,
|
||||||
|
"ForStatementOpeningBrace": 1,
|
||||||
|
"ForStatementSemicolon": 1,
|
||||||
|
"FunctionDeclarationClosingBrace": 0,
|
||||||
|
"FunctionDeclarationOpeningBrace": 0,
|
||||||
|
"FunctionExpressionClosingBrace": 0,
|
||||||
|
"FunctionExpressionOpeningBrace": 0,
|
||||||
|
"FunctionName": 0,
|
||||||
|
"FunctionReservedWord": 0,
|
||||||
|
"IfStatementClosingBrace": 1,
|
||||||
|
"IfStatementConditionalClosing": 0,
|
||||||
|
"IfStatementConditionalOpening": 0,
|
||||||
|
"IfStatementOpeningBrace": 1,
|
||||||
|
"LogicalExpressionOperator": 1,
|
||||||
|
"MemberExpressionOpening": 0,
|
||||||
|
"ObjectExpressionClosingBrace": 0,
|
||||||
|
"ObjectExpressionOpeningBrace": 1,
|
||||||
|
"ParameterComma": 1,
|
||||||
|
"ParameterList": 0,
|
||||||
|
"PropertyName": 0,
|
||||||
|
"PropertyValue": 0,
|
||||||
|
"SwitchDiscriminantClosing": 1,
|
||||||
|
"SwitchDiscriminantOpening": 0,
|
||||||
|
"ThrowKeyword": 1,
|
||||||
|
"TryClosingBrace": 1,
|
||||||
|
"TryKeyword": -1,
|
||||||
|
"TryOpeningBrace": 1,
|
||||||
|
"UnaryExpressionOperator": 0,
|
||||||
|
"VariableName": 1,
|
||||||
|
"WhileStatementClosingBrace": 1,
|
||||||
|
"WhileStatementConditionalClosing": 1,
|
||||||
|
"WhileStatementConditionalOpening": 0,
|
||||||
|
"WhileStatementOpeningBrace": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1 @@
|
|||||||
**/JsLibraries/**
|
**/JsLibraries/**
|
||||||
**/*.css.d.ts
|
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
const path = require('path');
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin').configs.recommended;
|
|
||||||
|
|
||||||
const frontendFolder = __dirname;
|
|
||||||
|
|
||||||
const dirs = fs
|
const dirs = fs
|
||||||
.readdirSync(path.join(frontendFolder, 'src'), { withFileTypes: true })
|
.readdirSync('frontend/src', { withFileTypes: true })
|
||||||
.filter((dirent) => dirent.isDirectory())
|
.filter((dirent) => dirent.isDirectory())
|
||||||
.map((dirent) => dirent.name)
|
.map((dirent) => dirent.name)
|
||||||
.join('|');
|
.join('|');
|
||||||
|
|
||||||
module.exports = {
|
const frontendFolder = __dirname;
|
||||||
root: true,
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
parser: '@babel/eslint-parser',
|
parser: '@babel/eslint-parser',
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
@@ -28,8 +21,7 @@ module.exports = {
|
|||||||
globals: {
|
globals: {
|
||||||
expect: false,
|
expect: false,
|
||||||
chai: false,
|
chai: false,
|
||||||
sinon: false,
|
sinon: false
|
||||||
JSX: true
|
|
||||||
},
|
},
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
@@ -49,9 +41,7 @@ module.exports = {
|
|||||||
'react',
|
'react',
|
||||||
'react-hooks',
|
'react-hooks',
|
||||||
'simple-import-sort',
|
'simple-import-sort',
|
||||||
'import',
|
'import'
|
||||||
'@typescript-eslint',
|
|
||||||
'prettier'
|
|
||||||
],
|
],
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
@@ -234,7 +224,7 @@ module.exports = {
|
|||||||
'consistent-this': ['error', 'self'],
|
'consistent-this': ['error', 'self'],
|
||||||
'eol-last': 'error',
|
'eol-last': 'error',
|
||||||
'func-names': 'off',
|
'func-names': 'off',
|
||||||
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
|
'func-style': ['error', 'declaration'],
|
||||||
indent: ['error', 2, { SwitchCase: 1 }],
|
indent: ['error', 2, { SwitchCase: 1 }],
|
||||||
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
||||||
'keyword-spacing': ['error', { before: true, after: true }],
|
'keyword-spacing': ['error', { before: true, after: true }],
|
||||||
@@ -325,9 +315,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: [
|
files: ['*.js'],
|
||||||
'*.js'
|
|
||||||
],
|
|
||||||
rules: {
|
rules: {
|
||||||
'simple-import-sort/imports': [
|
'simple-import-sort/imports': [
|
||||||
'error',
|
'error',
|
||||||
@@ -342,52 +330,6 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'*.ts',
|
|
||||||
'*.tsx'
|
|
||||||
],
|
|
||||||
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
project: './tsconfig.json'
|
|
||||||
},
|
|
||||||
|
|
||||||
extends: [
|
|
||||||
'prettier'
|
|
||||||
],
|
|
||||||
|
|
||||||
rules: Object.assign(typescriptEslintRecommended.rules, {
|
|
||||||
'no-shadow': 'off',
|
|
||||||
// These should be enabled after cleaning things up
|
|
||||||
'@typescript-eslint/no-unused-vars': 'warn',
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'react/prop-types': 'off',
|
|
||||||
'prettier/prettier': 'error',
|
|
||||||
'simple-import-sort/imports': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
groups: [
|
|
||||||
// Packages
|
|
||||||
// Absolute Paths
|
|
||||||
// Relative Paths
|
|
||||||
// Css
|
|
||||||
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'*.css.d.ts'
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'filenames/match-exported': 'off',
|
|
||||||
'init-declarations': 'off',
|
|
||||||
'prettier/prettier': 'off'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"js": {
|
||||||
|
"indent_size": 2,
|
||||||
|
"indent_char": " ",
|
||||||
|
"indent_level": 2,
|
||||||
|
"indent_with_tabs": false,
|
||||||
|
"preserve_newlines": true,
|
||||||
|
"brace_style": "collapse",
|
||||||
|
"max_preserve_newlines": 2,
|
||||||
|
"jslint_happy": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Ignore everything recursively
|
|
||||||
*
|
|
||||||
|
|
||||||
# But not the .ts files
|
|
||||||
!*.ts*
|
|
||||||
|
|
||||||
*css.d.ts
|
|
||||||
|
|
||||||
# Check subdirectories too
|
|
||||||
!*/
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"arrowParens": "always",
|
|
||||||
"endOfLine": "auto",
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "es5"
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"stylelint-order"
|
"stylelint-order"
|
||||||
],
|
],
|
||||||
"ignoreFiles": [
|
"ignoreFiles": [
|
||||||
"frontend/src/Styles/scaffolding.css",
|
"frontend/src/Styles/scaffolding.css",
|
||||||
"**/*.js"
|
"**/*.js"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"at-rule-empty-line-before": [
|
"at-rule-empty-line-before": [
|
||||||
"always",
|
"always",
|
||||||
{
|
{
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"at-rule-name-case": "lower",
|
||||||
|
"at-rule-name-newline-after": "always-multi-line",
|
||||||
|
"at-rule-name-space-after": "always",
|
||||||
"at-rule-no-unknown": [
|
"at-rule-no-unknown": [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
@@ -25,36 +28,83 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"at-rule-no-vendor-prefix": true,
|
"at-rule-no-vendor-prefix": true,
|
||||||
|
"at-rule-semicolon-newline-after": "always",
|
||||||
|
"at-rule-semicolon-space-before": "never",
|
||||||
|
"block-closing-brace-empty-line-before": "never",
|
||||||
|
"block-closing-brace-newline-after": "always",
|
||||||
|
"block-closing-brace-newline-before": "always",
|
||||||
|
"block-closing-brace-space-after": "always-single-line",
|
||||||
|
"block-closing-brace-space-before": "always-single-line",
|
||||||
"block-no-empty": true,
|
"block-no-empty": true,
|
||||||
|
"block-opening-brace-newline-after": "always",
|
||||||
|
"block-opening-brace-newline-before": "never-single-line",
|
||||||
|
"block-opening-brace-space-after": "always-single-line",
|
||||||
|
"block-opening-brace-space-before": "always",
|
||||||
|
"color-hex-case": "lower",
|
||||||
"color-hex-length": "short",
|
"color-hex-length": "short",
|
||||||
"color-named": "never",
|
"color-named": "never",
|
||||||
"color-no-invalid-hex": true,
|
"color-no-invalid-hex": true,
|
||||||
"comment-whitespace-inside": "always",
|
"comment-whitespace-inside": "always",
|
||||||
|
"declaration-bang-space-after": "never",
|
||||||
|
"declaration-bang-space-before": "always",
|
||||||
"declaration-block-no-duplicate-properties": [
|
"declaration-block-no-duplicate-properties": [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
"ignoreProperties": [
|
"ignoreProperties": [
|
||||||
"composes"
|
"composes"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"declaration-block-no-redundant-longhand-properties": true,
|
"declaration-block-no-redundant-longhand-properties": true,
|
||||||
"declaration-block-no-shorthand-property-overrides": true,
|
"declaration-block-no-shorthand-property-overrides": true,
|
||||||
|
"declaration-block-semicolon-newline-after": "always",
|
||||||
|
"declaration-block-semicolon-newline-before": "never-multi-line",
|
||||||
|
"declaration-block-semicolon-space-before": "never",
|
||||||
"declaration-block-single-line-max-declarations": 1,
|
"declaration-block-single-line-max-declarations": 1,
|
||||||
|
"declaration-block-trailing-semicolon": "always",
|
||||||
|
"declaration-colon-space-after": "always",
|
||||||
|
"declaration-colon-space-before": "never",
|
||||||
"font-family-name-quotes": "always-unless-keyword",
|
"font-family-name-quotes": "always-unless-keyword",
|
||||||
"function-calc-no-unspaced-operator": true,
|
"function-calc-no-unspaced-operator": true,
|
||||||
|
"function-comma-newline-after": "never-multi-line",
|
||||||
|
"function-comma-newline-before": "never-multi-line",
|
||||||
|
"function-comma-space-after": "always",
|
||||||
|
"function-comma-space-before": "never",
|
||||||
"function-linear-gradient-no-nonstandard-direction": true,
|
"function-linear-gradient-no-nonstandard-direction": true,
|
||||||
"function-name-case": "lower",
|
"function-name-case": "lower",
|
||||||
|
"function-parentheses-newline-inside": "never-multi-line",
|
||||||
|
"function-parentheses-space-inside": "never",
|
||||||
"function-url-quotes": "always",
|
"function-url-quotes": "always",
|
||||||
"function-url-scheme-disallowed-list": [
|
"function-url-scheme-disallowed-list": [
|
||||||
"data"
|
"data"
|
||||||
],
|
],
|
||||||
|
"function-whitespace-after": "always",
|
||||||
|
"indentation": 2,
|
||||||
"keyframe-declaration-no-important": true,
|
"keyframe-declaration-no-important": true,
|
||||||
"length-zero-no-unit": true,
|
"length-zero-no-unit": true,
|
||||||
|
"max-empty-lines": 1,
|
||||||
|
"max-line-length": [
|
||||||
|
100,
|
||||||
|
{
|
||||||
|
"ignore": [
|
||||||
|
"non-comments"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"max-nesting-depth": 2,
|
"max-nesting-depth": 2,
|
||||||
|
"media-feature-colon-space-after": "always",
|
||||||
|
"media-feature-colon-space-before": "never",
|
||||||
|
"media-feature-name-case": "lower",
|
||||||
"media-feature-name-no-vendor-prefix": true,
|
"media-feature-name-no-vendor-prefix": true,
|
||||||
|
"media-feature-range-operator-space-after": "always",
|
||||||
|
"media-feature-range-operator-space-before": "always",
|
||||||
"no-empty-source": true,
|
"no-empty-source": true,
|
||||||
|
"no-eol-whitespace": true,
|
||||||
|
"no-extra-semicolons": true,
|
||||||
"no-invalid-double-slash-comments": true,
|
"no-invalid-double-slash-comments": true,
|
||||||
|
"no-missing-end-of-source-newline": true,
|
||||||
|
"number-leading-zero": "always",
|
||||||
|
"number-no-trailing-zeros": true,
|
||||||
"order/order": [
|
"order/order": [
|
||||||
"custom-properties",
|
"custom-properties",
|
||||||
"dollar-variables",
|
"dollar-variables",
|
||||||
@@ -82,7 +132,6 @@
|
|||||||
"right",
|
"right",
|
||||||
"bottom",
|
"bottom",
|
||||||
"left",
|
"left",
|
||||||
"inset",
|
|
||||||
"z-index",
|
"z-index",
|
||||||
"display",
|
"display",
|
||||||
"visibility",
|
"visibility",
|
||||||
@@ -294,33 +343,54 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"property-case": "lower",
|
||||||
"property-no-vendor-prefix": true,
|
"property-no-vendor-prefix": true,
|
||||||
"rule-empty-line-before": [
|
"rule-empty-line-before": [
|
||||||
"always",
|
"always",
|
||||||
{
|
{
|
||||||
"except": [
|
"except": [
|
||||||
"first-nested"
|
"first-nested"
|
||||||
],
|
],
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"after-comment"
|
"after-comment"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selector-attribute-brackets-space-inside": "never",
|
||||||
|
"selector-attribute-operator-space-after": "never",
|
||||||
|
"selector-attribute-operator-space-before": "never",
|
||||||
"selector-attribute-quotes": "never",
|
"selector-attribute-quotes": "never",
|
||||||
"selector-class-pattern": "^[A-Za-z0-9]+$",
|
"selector-class-pattern": "^[A-Za-z0-9]+$",
|
||||||
|
"selector-combinator-space-after": "always",
|
||||||
|
"selector-combinator-space-before": "always",
|
||||||
|
"selector-descendant-combinator-no-non-space": true,
|
||||||
|
"selector-list-comma-newline-after": "always",
|
||||||
|
"selector-list-comma-newline-before": "never-multi-line",
|
||||||
|
"selector-list-comma-space-before": "never",
|
||||||
"selector-max-attribute": 0,
|
"selector-max-attribute": 0,
|
||||||
"selector-max-class": 3,
|
"selector-max-class": 3,
|
||||||
"selector-max-compound-selectors": 3,
|
"selector-max-compound-selectors": 3,
|
||||||
|
"selector-max-empty-lines": 0,
|
||||||
"selector-max-id": 0,
|
"selector-max-id": 0,
|
||||||
"selector-max-universal": 0,
|
"selector-max-universal": 0,
|
||||||
|
"selector-pseudo-class-case": "lower",
|
||||||
|
"selector-pseudo-class-parentheses-space-inside": "never",
|
||||||
|
"selector-pseudo-element-case": "lower",
|
||||||
"selector-pseudo-element-colon-notation": "double",
|
"selector-pseudo-element-colon-notation": "double",
|
||||||
"selector-pseudo-element-no-unknown": true,
|
"selector-pseudo-element-no-unknown": true,
|
||||||
"selector-type-case": "lower",
|
"selector-type-case": "lower",
|
||||||
"selector-type-no-unknown": true,
|
"selector-type-no-unknown": true,
|
||||||
"shorthand-property-no-redundant-values": true,
|
"shorthand-property-no-redundant-values": true,
|
||||||
"string-no-newline": true,
|
"string-no-newline": true,
|
||||||
|
"string-quotes": "single",
|
||||||
"time-min-milliseconds": 100,
|
"time-min-milliseconds": 100,
|
||||||
|
"unit-case": "lower",
|
||||||
"unit-no-unknown": true,
|
"unit-no-unknown": true,
|
||||||
|
"value-list-comma-newline-after": "never-multi-line",
|
||||||
|
"value-list-comma-newline-before": "never-multi-line",
|
||||||
|
"value-list-comma-space-after": "always",
|
||||||
|
"value-list-comma-space-before": "never",
|
||||||
|
"value-list-max-empty-lines": 0,
|
||||||
"value-no-vendor-prefix": true
|
"value-no-vendor-prefix": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"stylelint.vscode-stylelint",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"esbenp.prettier-vscode"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// Place your settings in this file to overwrite default and user settings.
|
|
||||||
{
|
|
||||||
"files.insertFinalNewline": true,
|
|
||||||
|
|
||||||
"files.exclude": {
|
|
||||||
"**/node_modules": true,
|
|
||||||
"**/*.d.css": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"editor.formatOnSave": false,
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"typescript.preferences.quoteStyle": "single",
|
|
||||||
|
|
||||||
"eslint.validate": [
|
|
||||||
"javascript",
|
|
||||||
"javascriptreact",
|
|
||||||
"typescript",
|
|
||||||
"typescriptreact"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@@ -2,25 +2,22 @@ const loose = true;
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
'@babel/plugin-transform-logical-assignment-operators',
|
|
||||||
|
|
||||||
// Stage 1
|
// Stage 1
|
||||||
'@babel/plugin-proposal-export-default-from',
|
'@babel/plugin-proposal-export-default-from',
|
||||||
['@babel/plugin-transform-optional-chaining', { loose }],
|
['@babel/plugin-proposal-optional-chaining', { loose }],
|
||||||
['@babel/plugin-transform-nullish-coalescing-operator', { loose }],
|
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
|
||||||
|
|
||||||
// Stage 2
|
// Stage 2
|
||||||
'@babel/plugin-transform-export-namespace-from',
|
'@babel/plugin-proposal-export-namespace-from',
|
||||||
|
|
||||||
// Stage 3
|
// Stage 3
|
||||||
['@babel/plugin-transform-class-properties', { loose }],
|
['@babel/plugin-proposal-class-properties', { loose }],
|
||||||
'@babel/plugin-syntax-dynamic-import'
|
'@babel/plugin-syntax-dynamic-import'
|
||||||
],
|
],
|
||||||
env: {
|
env: {
|
||||||
development: {
|
development: {
|
||||||
presets: [
|
presets: [
|
||||||
['@babel/preset-react', { development: true }],
|
['@babel/preset-react', { development: true }]
|
||||||
'@babel/preset-typescript'
|
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
'babel-plugin-inline-classnames'
|
'babel-plugin-inline-classnames'
|
||||||
@@ -28,8 +25,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
production: {
|
production: {
|
||||||
presets: [
|
presets: [
|
||||||
'@babel/preset-react',
|
'@babel/preset-react'
|
||||||
'@babel/preset-typescript'
|
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
'babel-plugin-transform-react-remove-prop-types'
|
'babel-plugin-transform-react-remove-prop-types'
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const FileManagerPlugin = require('filemanager-webpack-plugin');
|
const FileManagerPlugin = require('filemanager-webpack-plugin');
|
||||||
@@ -6,7 +5,6 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|||||||
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = (env) => {
|
module.exports = (env) => {
|
||||||
const uiFolder = 'UI';
|
const uiFolder = 'UI';
|
||||||
@@ -36,22 +34,17 @@ module.exports = (env) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
entry: {
|
entry: {
|
||||||
index: 'index.ts'
|
index: 'index.js'
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [
|
|
||||||
'.ts',
|
|
||||||
'.tsx',
|
|
||||||
'.js'
|
|
||||||
],
|
|
||||||
modules: [
|
modules: [
|
||||||
srcFolder,
|
srcFolder,
|
||||||
path.join(srcFolder, 'Shims'),
|
path.join(srcFolder, 'Shims'),
|
||||||
'node_modules'
|
'node_modules'
|
||||||
],
|
],
|
||||||
alias: {
|
alias: {
|
||||||
jquery: 'jquery/dist/jquery.min'
|
jquery: 'jquery/src/jquery'
|
||||||
},
|
},
|
||||||
fallback: {
|
fallback: {
|
||||||
buffer: false,
|
buffer: false,
|
||||||
@@ -66,23 +59,23 @@ module.exports = (env) => {
|
|||||||
output: {
|
output: {
|
||||||
path: distFolder,
|
path: distFolder,
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
filename: '[name]-[contenthash].js',
|
filename: '[name].js',
|
||||||
sourceMapFilename: '[file].map'
|
sourceMapFilename: '[file].map'
|
||||||
},
|
},
|
||||||
|
|
||||||
optimization: {
|
optimization: {
|
||||||
moduleIds: 'deterministic',
|
moduleIds: 'deterministic',
|
||||||
chunkIds: isProduction ? 'deterministic' : 'named'
|
chunkIds: 'named',
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'initial',
|
||||||
|
name: 'vendors'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
performance: {
|
performance: {
|
||||||
hints: false
|
hints: false
|
||||||
},
|
},
|
||||||
|
|
||||||
experiments: {
|
|
||||||
topLevelAwait: true
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
__DEV__: !isProduction,
|
__DEV__: !isProduction,
|
||||||
@@ -90,15 +83,13 @@ module.exports = (env) => {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: 'Content/styles.css',
|
filename: 'Content/styles.css'
|
||||||
chunkFilename: 'Content/[id]-[chunkhash].css'
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: 'frontend/src/index.ejs',
|
template: 'frontend/src/index.ejs',
|
||||||
filename: 'index.html',
|
filename: 'index.html',
|
||||||
publicPath: '/',
|
publicPath: '/'
|
||||||
inject: false
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new FileManagerPlugin({
|
new FileManagerPlugin({
|
||||||
@@ -139,8 +130,6 @@ module.exports = (env) => {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new ForkTsCheckerWebpackPlugin(),
|
|
||||||
|
|
||||||
new LiveReloadPlugin()
|
new LiveReloadPlugin()
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -164,7 +153,7 @@ module.exports = (env) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: [/\.jsx?$/, /\.tsx?$/],
|
test: /\.js?$/,
|
||||||
exclude: /(node_modules|JsLibraries)/,
|
exclude: /(node_modules|JsLibraries)/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
@@ -195,7 +184,6 @@ module.exports = (env) => {
|
|||||||
exclude: /(node_modules|globals.css)/,
|
exclude: /(node_modules|globals.css)/,
|
||||||
use: [
|
use: [
|
||||||
{ loader: MiniCssExtractPlugin.loader },
|
{ loader: MiniCssExtractPlugin.loader },
|
||||||
{ loader: 'css-modules-typescript-loader' },
|
|
||||||
{
|
{
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
@@ -235,7 +223,6 @@ module.exports = (env) => {
|
|||||||
{
|
{
|
||||||
loader: 'url-loader',
|
loader: 'url-loader',
|
||||||
options: {
|
options: {
|
||||||
limit: 10240,
|
|
||||||
mimetype: 'application/font-woff',
|
mimetype: 'application/font-woff',
|
||||||
emitFile: false,
|
emitFile: false,
|
||||||
name: 'Content/Fonts/[name].[ext]'
|
name: 'Content/Fonts/[name].[ext]'
|
||||||
@@ -264,19 +251,18 @@ module.exports = (env) => {
|
|||||||
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
||||||
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
||||||
|
|
||||||
config.optimization = {
|
config.optimization.minimizer = [
|
||||||
minimize: true,
|
new TerserPlugin({
|
||||||
minimizer: [
|
cache: true,
|
||||||
new TerserPlugin({
|
parallel: true,
|
||||||
terserOptions: {
|
sourceMap: true, // Must be set to true if using source-maps in production
|
||||||
sourceMap: true, // Must be set to true if using source-maps in production
|
terserOptions: {
|
||||||
mangle: false,
|
mangle: false,
|
||||||
keep_classnames: true,
|
keep_classnames: true,
|
||||||
keep_fnames: true
|
keep_fnames: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
]
|
];
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
|
{
|
||||||
|
"files.insertFinalNewline": true
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
@@ -36,7 +35,6 @@ class Blocklist extends Component {
|
|||||||
lastToggled: null,
|
lastToggled: null,
|
||||||
selectedState: {},
|
selectedState: {},
|
||||||
isConfirmRemoveModalOpen: false,
|
isConfirmRemoveModalOpen: false,
|
||||||
isConfirmClearModalOpen: false,
|
|
||||||
items: props.items
|
items: props.items
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -91,19 +89,6 @@ class Blocklist extends Component {
|
|||||||
this.setState({ isConfirmRemoveModalOpen: false });
|
this.setState({ isConfirmRemoveModalOpen: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
onClearBlocklistPress = () => {
|
|
||||||
this.setState({ isConfirmClearModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onClearBlocklistConfirmed = () => {
|
|
||||||
this.props.onClearBlocklistPress();
|
|
||||||
this.setState({ isConfirmClearModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onConfirmClearModalClose = () => {
|
|
||||||
this.setState({ isConfirmClearModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
|
||||||
@@ -117,6 +102,7 @@ class Blocklist extends Component {
|
|||||||
totalRecords,
|
totalRecords,
|
||||||
isRemoving,
|
isRemoving,
|
||||||
isClearingBlocklistExecuting,
|
isClearingBlocklistExecuting,
|
||||||
|
onClearBlocklistPress,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -124,8 +110,7 @@ class Blocklist extends Component {
|
|||||||
allSelected,
|
allSelected,
|
||||||
allUnselected,
|
allUnselected,
|
||||||
selectedState,
|
selectedState,
|
||||||
isConfirmRemoveModalOpen,
|
isConfirmRemoveModalOpen
|
||||||
isConfirmClearModalOpen
|
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const selectedIds = this.getSelectedIds();
|
const selectedIds = this.getSelectedIds();
|
||||||
@@ -145,9 +130,8 @@ class Blocklist extends Component {
|
|||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
label={translate('Clear')}
|
label={translate('Clear')}
|
||||||
iconName={icons.CLEAR}
|
iconName={icons.CLEAR}
|
||||||
isDisabled={!items.length}
|
|
||||||
isSpinning={isClearingBlocklistExecuting}
|
isSpinning={isClearingBlocklistExecuting}
|
||||||
onPress={this.onClearBlocklistPress}
|
onPress={onClearBlocklistPress}
|
||||||
/>
|
/>
|
||||||
</PageToolbarSection>
|
</PageToolbarSection>
|
||||||
|
|
||||||
@@ -172,16 +156,16 @@ class Blocklist extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && !!error &&
|
!isFetching && !!error &&
|
||||||
<Alert kind={kinds.DANGER}>
|
<div>
|
||||||
{translate('BlocklistLoadError')}
|
{translate('UnableToLoadBlocklist')}
|
||||||
</Alert>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !error && !items.length &&
|
isPopulated && !error && !items.length &&
|
||||||
<Alert kind={kinds.INFO}>
|
<div>
|
||||||
{translate('NoHistoryBlocklist')}
|
{translate('NoHistory')}
|
||||||
</Alert>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -225,21 +209,11 @@ class Blocklist extends Component {
|
|||||||
isOpen={isConfirmRemoveModalOpen}
|
isOpen={isConfirmRemoveModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('RemoveSelected')}
|
title={translate('RemoveSelected')}
|
||||||
message={translate('RemoveSelectedBlocklistMessageText')}
|
message={translate('AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist')}
|
||||||
confirmLabel={translate('RemoveSelected')}
|
confirmLabel={translate('RemoveSelected')}
|
||||||
onConfirm={this.onRemoveSelectedConfirmed}
|
onConfirm={this.onRemoveSelectedConfirmed}
|
||||||
onCancel={this.onConfirmRemoveModalClose}
|
onCancel={this.onConfirmRemoveModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={isConfirmClearModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title={translate('ClearBlocklist')}
|
|
||||||
message={translate('ClearBlocklistMessageText')}
|
|
||||||
confirmLabel={translate('Clear')}
|
|
||||||
onConfirm={this.onClearBlocklistConfirmed}
|
|
||||||
onCancel={this.onConfirmClearModalClose}
|
|
||||||
/>
|
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'actions': string;
|
|
||||||
'indexer': string;
|
|
||||||
'language': string;
|
|
||||||
'quality': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -82,7 +82,7 @@ class BlocklistRow extends Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'movieMetadata.sortTitle') {
|
if (name === 'movies.sortTitle') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<MovieTitleLink
|
<MovieTitleLink
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'description': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -7,7 +7,6 @@ import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionList
|
|||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||||
import formatAge from 'Utilities/Number/formatAge';
|
import formatAge from 'Utilities/Number/formatAge';
|
||||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './HistoryDetails.css';
|
import styles from './HistoryDetails.css';
|
||||||
|
|
||||||
@@ -16,7 +15,6 @@ function HistoryDetails(props) {
|
|||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
data,
|
data,
|
||||||
downloadId,
|
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
timeFormat
|
timeFormat
|
||||||
} = props;
|
} = props;
|
||||||
@@ -25,11 +23,10 @@ function HistoryDetails(props) {
|
|||||||
const {
|
const {
|
||||||
indexer,
|
indexer,
|
||||||
releaseGroup,
|
releaseGroup,
|
||||||
movieMatchType,
|
|
||||||
customFormatScore,
|
|
||||||
nzbInfoUrl,
|
nzbInfoUrl,
|
||||||
downloadClient,
|
downloadClient,
|
||||||
downloadClientName,
|
downloadClientName,
|
||||||
|
downloadId,
|
||||||
age,
|
age,
|
||||||
ageHours,
|
ageHours,
|
||||||
ageMinutes,
|
ageMinutes,
|
||||||
@@ -47,55 +44,33 @@ function HistoryDetails(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
indexer ?
|
!!indexer &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Indexer')}
|
title={translate('Indexer')}
|
||||||
data={indexer}
|
data={indexer}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
releaseGroup ?
|
!!releaseGroup &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('ReleaseGroup')}
|
title={translate('ReleaseGroup')}
|
||||||
data={releaseGroup}
|
data={releaseGroup}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
customFormatScore && customFormatScore !== '0' ?
|
!!nzbInfoUrl &&
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('CustomFormatScore')}
|
|
||||||
data={formatCustomFormatScore(customFormatScore)}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
movieMatchType ?
|
|
||||||
<DescriptionListItem
|
|
||||||
descriptionClassName={styles.description}
|
|
||||||
title={translate('MovieMatchType')}
|
|
||||||
data={movieMatchType}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
nzbInfoUrl ?
|
|
||||||
<span>
|
<span>
|
||||||
<DescriptionListItemTitle>
|
<DescriptionListItemTitle>
|
||||||
{translate('InfoUrl')}
|
Info URL
|
||||||
</DescriptionListItemTitle>
|
</DescriptionListItemTitle>
|
||||||
|
|
||||||
<DescriptionListItemDescription>
|
<DescriptionListItemDescription>
|
||||||
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
|
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
|
||||||
</DescriptionListItemDescription>
|
</DescriptionListItemDescription>
|
||||||
</span> :
|
</span>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -108,30 +83,27 @@ function HistoryDetails(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
downloadId ?
|
!!downloadId &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('GrabId')}
|
title={translate('GrabID')}
|
||||||
data={downloadId}
|
data={downloadId}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
indexer ?
|
!!indexer &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('AgeWhenGrabbed')}
|
title={translate('AgeWhenGrabbed')}
|
||||||
data={formatAge(age, ageHours, ageMinutes)}
|
data={formatAge(age, ageHours, ageMinutes)}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
publishedDate ?
|
!!publishedDate &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('PublishedDate')}
|
title={translate('PublishedDate')}
|
||||||
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
@@ -151,21 +123,11 @@ function HistoryDetails(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
downloadId ?
|
!!message &&
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('GrabId')}
|
|
||||||
data={downloadId}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
message ?
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Message')}
|
title={translate('Message')}
|
||||||
data={message}
|
data={message}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
@@ -173,7 +135,6 @@ function HistoryDetails(props) {
|
|||||||
|
|
||||||
if (eventType === 'downloadFolderImported') {
|
if (eventType === 'downloadFolderImported') {
|
||||||
const {
|
const {
|
||||||
customFormatScore,
|
|
||||||
droppedPath,
|
droppedPath,
|
||||||
importedPath
|
importedPath
|
||||||
} = data;
|
} = data;
|
||||||
@@ -187,32 +148,21 @@ function HistoryDetails(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
droppedPath ?
|
!!droppedPath &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Source')}
|
title={translate('Source')}
|
||||||
data={droppedPath}
|
data={droppedPath}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
importedPath ?
|
!!importedPath &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('ImportedTo')}
|
title={translate('ImportedTo')}
|
||||||
data={importedPath}
|
data={importedPath}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
customFormatScore && customFormatScore !== '0' ?
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('CustomFormatScore')}
|
|
||||||
data={formatCustomFormatScore(customFormatScore)}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
@@ -220,21 +170,20 @@ function HistoryDetails(props) {
|
|||||||
|
|
||||||
if (eventType === 'movieFileDeleted') {
|
if (eventType === 'movieFileDeleted') {
|
||||||
const {
|
const {
|
||||||
reason,
|
reason
|
||||||
customFormatScore
|
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let reasonMessage = '';
|
let reasonMessage = '';
|
||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case 'Manual':
|
case 'Manual':
|
||||||
reasonMessage = translate('DeletedReasonManual');
|
reasonMessage = translate('FileWasDeletedByViaUI');
|
||||||
break;
|
break;
|
||||||
case 'MissingFromDisk':
|
case 'MissingFromDisk':
|
||||||
reasonMessage = translate('DeletedReasonMissingFromDisk');
|
reasonMessage = translate('MissingFromDisk');
|
||||||
break;
|
break;
|
||||||
case 'Upgrade':
|
case 'Upgrade':
|
||||||
reasonMessage = translate('DeletedReasonUpgrade');
|
reasonMessage = translate('FileWasDeletedByUpgrade');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
reasonMessage = '';
|
reasonMessage = '';
|
||||||
@@ -251,15 +200,6 @@ function HistoryDetails(props) {
|
|||||||
title={translate('Reason')}
|
title={translate('Reason')}
|
||||||
data={reasonMessage}
|
data={reasonMessage}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
|
||||||
customFormatScore && customFormatScore !== '0' ?
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('CustomFormatScore')}
|
|
||||||
data={formatCustomFormatScore(customFormatScore)}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -311,21 +251,11 @@ function HistoryDetails(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
downloadId ?
|
!!message &&
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('GrabId')}
|
|
||||||
data={downloadId}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
message ?
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Message')}
|
title={translate('Message')}
|
||||||
data={message}
|
data={message}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
@@ -346,7 +276,6 @@ HistoryDetails.propTypes = {
|
|||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
downloadId: PropTypes.string,
|
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired
|
timeFormat: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'markAsFailedButton': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -15,19 +15,19 @@ import styles from './HistoryDetailsModal.css';
|
|||||||
function getHeaderTitle(eventType) {
|
function getHeaderTitle(eventType) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'grabbed':
|
||||||
return translate('Grabbed');
|
return 'Grabbed';
|
||||||
case 'downloadFailed':
|
case 'downloadFailed':
|
||||||
return translate('DownloadFailed');
|
return 'Download Failed';
|
||||||
case 'downloadFolderImported':
|
case 'downloadFolderImported':
|
||||||
return translate('MovieImported');
|
return 'Movie Imported';
|
||||||
case 'movieFileDeleted':
|
case 'movieFileDeleted':
|
||||||
return translate('MovieFileDeleted');
|
return 'Movie File Deleted';
|
||||||
case 'movieFileRenamed':
|
case 'movieFileRenamed':
|
||||||
return translate('MovieFileRenamed');
|
return 'Movie File Renamed';
|
||||||
case 'downloadIgnored':
|
case 'downloadIgnored':
|
||||||
return translate('DownloadIgnored');
|
return 'Download Ignored';
|
||||||
default:
|
default:
|
||||||
return translate('Unknown');
|
return 'Unknown';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@ function HistoryDetailsModal(props) {
|
|||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
data,
|
data,
|
||||||
downloadId,
|
|
||||||
isMarkingAsFailed,
|
isMarkingAsFailed,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
@@ -60,7 +59,6 @@ function HistoryDetailsModal(props) {
|
|||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
sourceTitle={sourceTitle}
|
sourceTitle={sourceTitle}
|
||||||
data={data}
|
data={data}
|
||||||
downloadId={downloadId}
|
|
||||||
shortDateFormat={shortDateFormat}
|
shortDateFormat={shortDateFormat}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
/>
|
/>
|
||||||
@@ -75,7 +73,7 @@ function HistoryDetailsModal(props) {
|
|||||||
isSpinning={isMarkingAsFailed}
|
isSpinning={isMarkingAsFailed}
|
||||||
onPress={onMarkAsFailedPress}
|
onPress={onMarkAsFailedPress}
|
||||||
>
|
>
|
||||||
{translate('MarkAsFailed')}
|
Mark as Failed
|
||||||
</SpinnerButton>
|
</SpinnerButton>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +93,6 @@ HistoryDetailsModal.propTypes = {
|
|||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
downloadId: PropTypes.string,
|
|
||||||
isMarkingAsFailed: PropTypes.bool.isRequired,
|
isMarkingAsFailed: PropTypes.bool.isRequired,
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired,
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
@@ -12,9 +11,8 @@ import Table from 'Components/Table/Table';
|
|||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import TablePager from 'Components/Table/TablePager';
|
import TablePager from 'Components/Table/TablePager';
|
||||||
import { align, icons, kinds } from 'Helpers/Props';
|
import { align, icons } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import HistoryFilterModal from './HistoryFilterModal';
|
|
||||||
import HistoryRowConnector from './HistoryRowConnector';
|
import HistoryRowConnector from './HistoryRowConnector';
|
||||||
|
|
||||||
class History extends Component {
|
class History extends Component {
|
||||||
@@ -34,7 +32,6 @@ class History extends Component {
|
|||||||
columns,
|
columns,
|
||||||
selectedFilterKey,
|
selectedFilterKey,
|
||||||
filters,
|
filters,
|
||||||
customFilters,
|
|
||||||
totalRecords,
|
totalRecords,
|
||||||
onFilterSelect,
|
onFilterSelect,
|
||||||
onFirstPagePress,
|
onFirstPagePress,
|
||||||
@@ -72,8 +69,7 @@ class History extends Component {
|
|||||||
alignMenu={align.RIGHT}
|
alignMenu={align.RIGHT}
|
||||||
selectedFilterKey={selectedFilterKey}
|
selectedFilterKey={selectedFilterKey}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
customFilters={customFilters}
|
customFilters={[]}
|
||||||
filterModalConnectorComponent={HistoryFilterModal}
|
|
||||||
onFilterSelect={onFilterSelect}
|
onFilterSelect={onFilterSelect}
|
||||||
/>
|
/>
|
||||||
</PageToolbarSection>
|
</PageToolbarSection>
|
||||||
@@ -87,9 +83,9 @@ class History extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetchingAny && hasError &&
|
!isFetchingAny && hasError &&
|
||||||
<Alert kind={kinds.DANGER}>
|
<div>
|
||||||
{translate('HistoryLoadError')}
|
{translate('UnableToLoadHistory')}
|
||||||
</Alert>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -97,9 +93,9 @@ class History extends Component {
|
|||||||
// wait for the episodes to populate because they are never coming.
|
// wait for the episodes to populate because they are never coming.
|
||||||
|
|
||||||
isPopulated && !hasError && !items.length &&
|
isPopulated && !hasError && !items.length &&
|
||||||
<Alert kind={kinds.INFO}>
|
<div>
|
||||||
{translate('NoHistoryFound')}
|
{translate('NoHistory')}
|
||||||
</Alert>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -147,9 +143,8 @@ History.propTypes = {
|
|||||||
moviesError: PropTypes.object,
|
moviesError: PropTypes.object,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
selectedFilterKey: PropTypes.string.isRequired,
|
||||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
totalRecords: PropTypes.number,
|
totalRecords: PropTypes.number,
|
||||||
onFilterSelect: PropTypes.func.isRequired,
|
onFilterSelect: PropTypes.func.isRequired,
|
||||||
onFirstPagePress: PropTypes.func.isRequired
|
onFirstPagePress: PropTypes.func.isRequired
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { connect } from 'react-redux';
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import withCurrentPage from 'Components/withCurrentPage';
|
import withCurrentPage from 'Components/withCurrentPage';
|
||||||
import * as historyActions from 'Store/Actions/historyActions';
|
import * as historyActions from 'Store/Actions/historyActions';
|
||||||
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
|
|
||||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||||
import History from './History';
|
import History from './History';
|
||||||
|
|
||||||
@@ -12,13 +11,11 @@ function createMapStateToProps() {
|
|||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.history,
|
(state) => state.history,
|
||||||
(state) => state.movies,
|
(state) => state.movies,
|
||||||
createCustomFiltersSelector('history'),
|
(history, movies) => {
|
||||||
(history, movies, customFilters) => {
|
|
||||||
return {
|
return {
|
||||||
isMoviesFetching: movies.isFetching,
|
isMoviesFetching: movies.isFetching,
|
||||||
isMoviesPopulated: movies.isPopulated,
|
isMoviesPopulated: movies.isPopulated,
|
||||||
moviesError: movies.error,
|
moviesError: movies.error,
|
||||||
customFilters,
|
|
||||||
...history
|
...history
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'cell': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -3,10 +3,9 @@ import React from 'react';
|
|||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './HistoryEventTypeCell.css';
|
import styles from './HistoryEventTypeCell.css';
|
||||||
|
|
||||||
function getIconName(eventType, data) {
|
function getIconName(eventType) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'grabbed':
|
||||||
return icons.DOWNLOADING;
|
return icons.DOWNLOADING;
|
||||||
@@ -17,7 +16,7 @@ function getIconName(eventType, data) {
|
|||||||
case 'downloadFailed':
|
case 'downloadFailed':
|
||||||
return icons.DOWNLOADING;
|
return icons.DOWNLOADING;
|
||||||
case 'movieFileDeleted':
|
case 'movieFileDeleted':
|
||||||
return data.reason === 'MissingFromDisk' ? icons.FILE_MISSING : icons.DELETE;
|
return icons.DELETE;
|
||||||
case 'movieFileRenamed':
|
case 'movieFileRenamed':
|
||||||
return icons.ORGANIZE;
|
return icons.ORGANIZE;
|
||||||
case 'downloadIgnored':
|
case 'downloadIgnored':
|
||||||
@@ -39,26 +38,26 @@ function getIconKind(eventType) {
|
|||||||
function getTooltip(eventType, data) {
|
function getTooltip(eventType, data) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'grabbed':
|
||||||
return translate('MovieGrabbedHistoryTooltip', { indexer: data.indexer, downloadClient: data.downloadClient });
|
return `Movie grabbed from ${data.indexer} and sent to ${data.downloadClient}`;
|
||||||
case 'movieFolderImported':
|
case 'movieFolderImported':
|
||||||
return translate('MovieFolderImportedTooltip');
|
return 'Movie imported from movie folder';
|
||||||
case 'downloadFolderImported':
|
case 'downloadFolderImported':
|
||||||
return translate('MovieImportedTooltip');
|
return 'Movie downloaded successfully and picked up from download client';
|
||||||
case 'downloadFailed':
|
case 'downloadFailed':
|
||||||
return translate('MovieDownloadFailedTooltip');
|
return 'Movie download failed';
|
||||||
case 'movieFileDeleted':
|
case 'movieFileDeleted':
|
||||||
return data.reason === 'MissingFromDisk' ? translate('MovieFileMissingTooltip') : translate('MovieFileDeletedTooltip');
|
return 'Movie file deleted';
|
||||||
case 'movieFileRenamed':
|
case 'movieFileRenamed':
|
||||||
return translate('MovieFileRenamedTooltip');
|
return 'Movie file renamed';
|
||||||
case 'downloadIgnored':
|
case 'downloadIgnored':
|
||||||
return translate('MovieDownloadIgnoredTooltip');
|
return 'Movie Download Ignored';
|
||||||
default:
|
default:
|
||||||
return translate('UnknownEventTooltip');
|
return 'Unknown event';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function HistoryEventTypeCell({ eventType, data }) {
|
function HistoryEventTypeCell({ eventType, data }) {
|
||||||
const iconName = getIconName(eventType, data);
|
const iconName = getIconName(eventType);
|
||||||
const iconKind = getIconKind(eventType);
|
const iconKind = getIconKind(eventType);
|
||||||
const tooltip = getTooltip(eventType, data);
|
const tooltip = getTooltip(eventType, data);
|
||||||
|
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import React, { useCallback } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import AppState from 'App/State/AppState';
|
|
||||||
import FilterModal from 'Components/Filter/FilterModal';
|
|
||||||
import { setHistoryFilter } from 'Store/Actions/historyActions';
|
|
||||||
|
|
||||||
function createHistorySelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.history.items,
|
|
||||||
(queueItems) => {
|
|
||||||
return queueItems;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilterBuilderPropsSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.history.filterBuilderProps,
|
|
||||||
(filterBuilderProps) => {
|
|
||||||
return filterBuilderProps;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HistoryFilterModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function HistoryFilterModal(props: HistoryFilterModalProps) {
|
|
||||||
const sectionItems = useSelector(createHistorySelector());
|
|
||||||
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
|
|
||||||
const customFilterType = 'history';
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const dispatchSetFilter = useCallback(
|
|
||||||
(payload: unknown) => {
|
|
||||||
dispatch(setHistoryFilter(payload));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FilterModal
|
|
||||||
// TODO: Don't spread all the props
|
|
||||||
{...props}
|
|
||||||
sectionItems={sectionItems}
|
|
||||||
filterBuilderProps={filterBuilderProps}
|
|
||||||
customFilterType={customFilterType}
|
|
||||||
dispatchSetFilter={dispatchSetFilter}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'customFormatScore': string;
|
|
||||||
'details': string;
|
|
||||||
'downloadClient': string;
|
|
||||||
'indexer': string;
|
|
||||||
'releaseGroup': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -4,8 +4,7 @@ import IconButton from 'Components/Link/IconButton';
|
|||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
import { icons } from 'Helpers/Props';
|
||||||
import { icons, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import MovieFormats from 'Movie/MovieFormats';
|
import MovieFormats from 'Movie/MovieFormats';
|
||||||
import MovieLanguage from 'Movie/MovieLanguage';
|
import MovieLanguage from 'Movie/MovieLanguage';
|
||||||
import MovieQuality from 'Movie/MovieQuality';
|
import MovieQuality from 'Movie/MovieQuality';
|
||||||
@@ -64,7 +63,6 @@ class HistoryRow extends Component {
|
|||||||
sourceTitle,
|
sourceTitle,
|
||||||
date,
|
date,
|
||||||
data,
|
data,
|
||||||
downloadId,
|
|
||||||
isMarkingAsFailed,
|
isMarkingAsFailed,
|
||||||
columns,
|
columns,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
@@ -99,7 +97,7 @@ class HistoryRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'movieMetadata.sortTitle') {
|
if (name === 'movies.sortTitle') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<MovieTitleLink
|
<MovieTitleLink
|
||||||
@@ -178,14 +176,7 @@ class HistoryRow extends Component {
|
|||||||
key={name}
|
key={name}
|
||||||
className={styles.customFormatScore}
|
className={styles.customFormatScore}
|
||||||
>
|
>
|
||||||
<Tooltip
|
{formatCustomFormatScore(customFormatScore)}
|
||||||
anchor={formatCustomFormatScore(
|
|
||||||
customFormatScore,
|
|
||||||
customFormats.length
|
|
||||||
)}
|
|
||||||
tooltip={<MovieFormats formats={customFormats} />}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -217,12 +208,10 @@ class HistoryRow extends Component {
|
|||||||
key={name}
|
key={name}
|
||||||
className={styles.details}
|
className={styles.details}
|
||||||
>
|
>
|
||||||
<div className={styles.actionContents}>
|
<IconButton
|
||||||
<IconButton
|
name={icons.INFO}
|
||||||
name={icons.INFO}
|
onPress={this.onDetailsPress}
|
||||||
onPress={this.onDetailsPress}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -236,7 +225,6 @@ class HistoryRow extends Component {
|
|||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
sourceTitle={sourceTitle}
|
sourceTitle={sourceTitle}
|
||||||
data={data}
|
data={data}
|
||||||
downloadId={downloadId}
|
|
||||||
isMarkingAsFailed={isMarkingAsFailed}
|
isMarkingAsFailed={isMarkingAsFailed}
|
||||||
shortDateFormat={shortDateFormat}
|
shortDateFormat={shortDateFormat}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
@@ -261,7 +249,6 @@ HistoryRow.propTypes = {
|
|||||||
sourceTitle: PropTypes.string.isRequired,
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
date: PropTypes.string.isRequired,
|
date: PropTypes.string.isRequired,
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
downloadId: PropTypes.string,
|
|
||||||
isMarkingAsFailed: PropTypes.bool,
|
isMarkingAsFailed: PropTypes.bool,
|
||||||
markAsFailedError: PropTypes.object,
|
markAsFailedError: PropTypes.object,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
@@ -270,8 +257,4 @@ HistoryRow.propTypes = {
|
|||||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
onMarkAsFailedPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
HistoryRow.defaultProps = {
|
|
||||||
customFormats: []
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HistoryRow;
|
export default HistoryRow;
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'torrent': string;
|
|
||||||
'usenet': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||||
@@ -14,7 +12,7 @@ import Table from 'Components/Table/Table';
|
|||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import TablePager from 'Components/Table/TablePager';
|
import TablePager from 'Components/Table/TablePager';
|
||||||
import { align, icons, kinds } from 'Helpers/Props';
|
import { align, icons } from 'Helpers/Props';
|
||||||
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
||||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
@@ -22,10 +20,9 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
|||||||
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
||||||
import selectAll from 'Utilities/Table/selectAll';
|
import selectAll from 'Utilities/Table/selectAll';
|
||||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||||
import QueueFilterModal from './QueueFilterModal';
|
|
||||||
import QueueOptionsConnector from './QueueOptionsConnector';
|
import QueueOptionsConnector from './QueueOptionsConnector';
|
||||||
import QueueRowConnector from './QueueRowConnector';
|
import QueueRowConnector from './QueueRowConnector';
|
||||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
|
||||||
|
|
||||||
class Queue extends Component {
|
class Queue extends Component {
|
||||||
|
|
||||||
@@ -155,16 +152,11 @@ class Queue extends Component {
|
|||||||
isMoviesPopulated,
|
isMoviesPopulated,
|
||||||
moviesError,
|
moviesError,
|
||||||
columns,
|
columns,
|
||||||
selectedFilterKey,
|
|
||||||
filters,
|
|
||||||
customFilters,
|
|
||||||
count,
|
|
||||||
totalRecords,
|
totalRecords,
|
||||||
isGrabbing,
|
isGrabbing,
|
||||||
isRemoving,
|
isRemoving,
|
||||||
isRefreshMonitoredDownloadsExecuting,
|
isRefreshMonitoredDownloadsExecuting,
|
||||||
onRefreshPress,
|
onRefreshPress,
|
||||||
onFilterSelect,
|
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -227,15 +219,6 @@ class Queue extends Component {
|
|||||||
iconName={icons.TABLE}
|
iconName={icons.TABLE}
|
||||||
/>
|
/>
|
||||||
</TableOptionsModalWrapper>
|
</TableOptionsModalWrapper>
|
||||||
|
|
||||||
<FilterMenu
|
|
||||||
alignMenu={align.RIGHT}
|
|
||||||
selectedFilterKey={selectedFilterKey}
|
|
||||||
filters={filters}
|
|
||||||
customFilters={customFilters}
|
|
||||||
filterModalConnectorComponent={QueueFilterModal}
|
|
||||||
onFilterSelect={onFilterSelect}
|
|
||||||
/>
|
|
||||||
</PageToolbarSection>
|
</PageToolbarSection>
|
||||||
</PageToolbar>
|
</PageToolbar>
|
||||||
|
|
||||||
@@ -248,21 +231,17 @@ class Queue extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isRefreshing && hasError ?
|
!isRefreshing && hasError ?
|
||||||
<Alert kind={kinds.DANGER}>
|
<div>
|
||||||
{translate('QueueLoadError')}
|
{translate('FailedToLoadQueue')}
|
||||||
</Alert> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isAllPopulated && !hasError && !items.length ?
|
isAllPopulated && !hasError && !items.length ?
|
||||||
<Alert kind={kinds.INFO}>
|
<div>
|
||||||
{
|
{translate('QueueIsEmpty')}
|
||||||
selectedFilterKey !== 'all' && count > 0 ?
|
</div> :
|
||||||
translate('QueueFilterHasNoItems') :
|
|
||||||
translate('QueueIsEmpty')
|
|
||||||
}
|
|
||||||
</Alert> :
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,16 +286,9 @@ class Queue extends Component {
|
|||||||
}
|
}
|
||||||
</PageContentBody>
|
</PageContentBody>
|
||||||
|
|
||||||
<RemoveQueueItemModal
|
<RemoveQueueItemsModal
|
||||||
isOpen={isConfirmRemoveModalOpen}
|
isOpen={isConfirmRemoveModalOpen}
|
||||||
selectedCount={selectedCount}
|
selectedCount={selectedCount}
|
||||||
canChangeCategory={isConfirmRemoveModalOpen && (
|
|
||||||
selectedIds.every((id) => {
|
|
||||||
const item = items.find((i) => i.id === id);
|
|
||||||
|
|
||||||
return !!(item && item.downloadClientHasPostImportCategory);
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
canIgnore={isConfirmRemoveModalOpen && (
|
canIgnore={isConfirmRemoveModalOpen && (
|
||||||
selectedIds.every((id) => {
|
selectedIds.every((id) => {
|
||||||
const item = items.find((i) => i.id === id);
|
const item = items.find((i) => i.id === id);
|
||||||
@@ -324,7 +296,7 @@ class Queue extends Component {
|
|||||||
return !!(item && item.movieId);
|
return !!(item && item.movieId);
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
pending={isConfirmRemoveModalOpen && (
|
allPending={isConfirmRemoveModalOpen && (
|
||||||
selectedIds.every((id) => {
|
selectedIds.every((id) => {
|
||||||
const item = items.find((i) => i.id === id);
|
const item = items.find((i) => i.id === id);
|
||||||
|
|
||||||
@@ -352,22 +324,13 @@ Queue.propTypes = {
|
|||||||
moviesError: PropTypes.object,
|
moviesError: PropTypes.object,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
|
||||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
count: PropTypes.number.isRequired,
|
|
||||||
totalRecords: PropTypes.number,
|
totalRecords: PropTypes.number,
|
||||||
isGrabbing: PropTypes.bool.isRequired,
|
isGrabbing: PropTypes.bool.isRequired,
|
||||||
isRemoving: PropTypes.bool.isRequired,
|
isRemoving: PropTypes.bool.isRequired,
|
||||||
isRefreshMonitoredDownloadsExecuting: PropTypes.bool.isRequired,
|
isRefreshMonitoredDownloadsExecuting: PropTypes.bool.isRequired,
|
||||||
onRefreshPress: PropTypes.func.isRequired,
|
onRefreshPress: PropTypes.func.isRequired,
|
||||||
onGrabSelectedPress: PropTypes.func.isRequired,
|
onGrabSelectedPress: PropTypes.func.isRequired,
|
||||||
onRemoveSelectedPress: PropTypes.func.isRequired,
|
onRemoveSelectedPress: PropTypes.func.isRequired
|
||||||
onFilterSelect: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
Queue.defaultProps = {
|
|
||||||
count: 0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Queue;
|
export default Queue;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import * as commandNames from 'Commands/commandNames';
|
|||||||
import withCurrentPage from 'Components/withCurrentPage';
|
import withCurrentPage from 'Components/withCurrentPage';
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
import * as queueActions from 'Store/Actions/queueActions';
|
import * as queueActions from 'Store/Actions/queueActions';
|
||||||
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
|
|
||||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||||
import Queue from './Queue';
|
import Queue from './Queue';
|
||||||
@@ -16,16 +15,12 @@ function createMapStateToProps() {
|
|||||||
(state) => state.movies,
|
(state) => state.movies,
|
||||||
(state) => state.queue.options,
|
(state) => state.queue.options,
|
||||||
(state) => state.queue.paged,
|
(state) => state.queue.paged,
|
||||||
(state) => state.queue.status.item,
|
|
||||||
createCustomFiltersSelector('queue'),
|
|
||||||
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
|
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
|
||||||
(movies, options, queue, status, customFilters, isRefreshMonitoredDownloadsExecuting) => {
|
(movies, options, queue, isRefreshMonitoredDownloadsExecuting) => {
|
||||||
return {
|
return {
|
||||||
count: options.includeUnknownMovieItems ? status.totalCount : status.count,
|
|
||||||
isMoviesFetching: movies.isFetching,
|
isMoviesFetching: movies.isFetching,
|
||||||
isMoviesPopulated: movies.isPopulated,
|
isMoviesPopulated: movies.isPopulated,
|
||||||
moviesError: movies.error,
|
moviesError: movies.error,
|
||||||
customFilters,
|
|
||||||
isRefreshMonitoredDownloadsExecuting,
|
isRefreshMonitoredDownloadsExecuting,
|
||||||
...options,
|
...options,
|
||||||
...queue
|
...queue
|
||||||
@@ -111,10 +106,6 @@ class QueueConnector extends Component {
|
|||||||
this.props.setQueueSort({ sortKey });
|
this.props.setQueueSort({ sortKey });
|
||||||
};
|
};
|
||||||
|
|
||||||
onFilterSelect = (selectedFilterKey) => {
|
|
||||||
this.props.setQueueFilter({ selectedFilterKey });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTableOptionChange = (payload) => {
|
onTableOptionChange = (payload) => {
|
||||||
this.props.setQueueTableOption(payload);
|
this.props.setQueueTableOption(payload);
|
||||||
|
|
||||||
@@ -149,7 +140,6 @@ class QueueConnector extends Component {
|
|||||||
onLastPagePress={this.onLastPagePress}
|
onLastPagePress={this.onLastPagePress}
|
||||||
onPageSelect={this.onPageSelect}
|
onPageSelect={this.onPageSelect}
|
||||||
onSortPress={this.onSortPress}
|
onSortPress={this.onSortPress}
|
||||||
onFilterSelect={this.onFilterSelect}
|
|
||||||
onTableOptionChange={this.onTableOptionChange}
|
onTableOptionChange={this.onTableOptionChange}
|
||||||
onRefreshPress={this.onRefreshPress}
|
onRefreshPress={this.onRefreshPress}
|
||||||
onGrabSelectedPress={this.onGrabSelectedPress}
|
onGrabSelectedPress={this.onGrabSelectedPress}
|
||||||
@@ -172,7 +162,6 @@ QueueConnector.propTypes = {
|
|||||||
gotoQueueLastPage: PropTypes.func.isRequired,
|
gotoQueueLastPage: PropTypes.func.isRequired,
|
||||||
gotoQueuePage: PropTypes.func.isRequired,
|
gotoQueuePage: PropTypes.func.isRequired,
|
||||||
setQueueSort: PropTypes.func.isRequired,
|
setQueueSort: PropTypes.func.isRequired,
|
||||||
setQueueFilter: PropTypes.func.isRequired,
|
|
||||||
setQueueTableOption: PropTypes.func.isRequired,
|
setQueueTableOption: PropTypes.func.isRequired,
|
||||||
clearQueue: PropTypes.func.isRequired,
|
clearQueue: PropTypes.func.isRequired,
|
||||||
grabQueueItems: PropTypes.func.isRequired,
|
grabQueueItems: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
.progressBarContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'progressBarContainer': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,71 +1,116 @@
|
|||||||
|
import moment from 'moment';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import { icons, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import QueueStatus from './QueueStatus';
|
|
||||||
import styles from './QueueDetails.css';
|
|
||||||
|
|
||||||
function QueueDetails(props) {
|
function QueueDetails(props) {
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
size,
|
size,
|
||||||
sizeleft,
|
sizeleft,
|
||||||
|
estimatedCompletionTime,
|
||||||
status,
|
status,
|
||||||
trackedDownloadState,
|
trackedDownloadState,
|
||||||
trackedDownloadStatus,
|
trackedDownloadStatus,
|
||||||
statusMessages,
|
|
||||||
errorMessage,
|
errorMessage,
|
||||||
progressBar
|
progressBar
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
||||||
const isDownloading = status === 'downloading';
|
|
||||||
const isPaused = status === 'paused';
|
|
||||||
const hasWarning = trackedDownloadStatus === 'warning';
|
|
||||||
const hasError = trackedDownloadStatus === 'error';
|
|
||||||
|
|
||||||
if (
|
|
||||||
(isDownloading || isPaused) &&
|
|
||||||
!hasWarning &&
|
|
||||||
!hasError
|
|
||||||
) {
|
|
||||||
const state = isPaused ? translate('Paused') : translate('Downloading');
|
|
||||||
|
|
||||||
if (progress < 5) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOADING}
|
|
||||||
title={`${state} - ${progress.toFixed(1)}% ${title}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (status === 'pending') {
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Icon
|
||||||
className={styles.progressBarContainer}
|
name={icons.PENDING}
|
||||||
anchor={progressBar}
|
title={translate('ReleaseWillBeProcessedInterp', [moment(estimatedCompletionTime).fromNow()])}
|
||||||
title={`${state} - ${progress.toFixed(1)}%`}
|
|
||||||
body={
|
|
||||||
<div>{title}</div>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.LEFT}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
if (status === 'completed') {
|
||||||
<QueueStatus
|
if (errorMessage) {
|
||||||
sourceTitle={title}
|
return (
|
||||||
status={status}
|
<Icon
|
||||||
trackedDownloadStatus={trackedDownloadStatus}
|
name={icons.DOWNLOAD}
|
||||||
trackedDownloadState={trackedDownloadState}
|
kind={kinds.DANGER}
|
||||||
statusMessages={statusMessages}
|
title={translate('ImportFailedInterp', [errorMessage])}
|
||||||
errorMessage={errorMessage}
|
/>
|
||||||
position={tooltipPositions.LEFT}
|
);
|
||||||
/>
|
}
|
||||||
);
|
|
||||||
|
if (trackedDownloadStatus === 'warning') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOAD}
|
||||||
|
kind={kinds.WARNING}
|
||||||
|
title={translate('UnableToImportCheckLogs')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importPending') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOAD}
|
||||||
|
kind={kinds.PURPLE}
|
||||||
|
title={`${translate('Downloaded')} - ${translate('WaitingToImport')}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importing') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOAD}
|
||||||
|
kind={kinds.PURPLE}
|
||||||
|
title={`${translate('Downloaded')} - ${translate('Importing')}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessage) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOADING}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title={translate('DownloadFailedInterp', [errorMessage])}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'failed') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOADING}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title={translate('DownloadFailedCheckDownloadClientForMoreDetails')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'warning') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOADING}
|
||||||
|
kind={kinds.WARNING}
|
||||||
|
title={translate('DownloadWarningCheckDownloadClientForMoreDetails')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress < 5) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOADING}
|
||||||
|
title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return progressBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueDetails.propTypes = {
|
QueueDetails.propTypes = {
|
||||||
@@ -76,14 +121,8 @@ QueueDetails.propTypes = {
|
|||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
trackedDownloadState: PropTypes.string.isRequired,
|
trackedDownloadState: PropTypes.string.isRequired,
|
||||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
trackedDownloadStatus: PropTypes.string.isRequired,
|
||||||
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
errorMessage: PropTypes.string,
|
errorMessage: PropTypes.string,
|
||||||
progressBar: PropTypes.node.isRequired
|
progressBar: PropTypes.node.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
QueueDetails.defaultProps = {
|
|
||||||
trackedDownloadStatus: 'ok',
|
|
||||||
trackedDownloadState: 'downloading'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QueueDetails;
|
export default QueueDetails;
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import React, { useCallback } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import AppState from 'App/State/AppState';
|
|
||||||
import FilterModal from 'Components/Filter/FilterModal';
|
|
||||||
import { setQueueFilter } from 'Store/Actions/queueActions';
|
|
||||||
|
|
||||||
function createQueueSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.queue.paged.items,
|
|
||||||
(queueItems) => {
|
|
||||||
return queueItems;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilterBuilderPropsSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.queue.paged.filterBuilderProps,
|
|
||||||
(filterBuilderProps) => {
|
|
||||||
return filterBuilderProps;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface QueueFilterModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function QueueFilterModal(props: QueueFilterModalProps) {
|
|
||||||
const sectionItems = useSelector(createQueueSelector());
|
|
||||||
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
|
|
||||||
const customFilterType = 'queue';
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const dispatchSetFilter = useCallback(
|
|
||||||
(payload: unknown) => {
|
|
||||||
dispatch(setQueueFilter(payload));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FilterModal
|
|
||||||
// TODO: Don't spread all the props
|
|
||||||
{...props}
|
|
||||||
sectionItems={sectionItems}
|
|
||||||
filterBuilderProps={filterBuilderProps}
|
|
||||||
customFilterType={customFilterType}
|
|
||||||
dispatchSetFilter={dispatchSetFilter}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -61,7 +61,7 @@ class QueueOptions extends Component {
|
|||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="includeUnknownMovieItems"
|
name="includeUnknownMovieItems"
|
||||||
value={includeUnknownMovieItems}
|
value={includeUnknownMovieItems}
|
||||||
helpText={translate('ShowUnknownMovieItemsHelpText')}
|
helpText={translate('IncludeUnknownMovieItemsHelpText')}
|
||||||
onChange={this.onOptionChange}
|
onChange={this.onOptionChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|||||||
@@ -16,12 +16,6 @@
|
|||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customFormatScore {
|
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 55px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'actions': string;
|
|
||||||
'customFormatScore': string;
|
|
||||||
'progress': string;
|
|
||||||
'protocol': string;
|
|
||||||
'quality': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -4,19 +4,17 @@ import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
|
|||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||||
import ProgressBar from 'Components/ProgressBar';
|
import ProgressBar from 'Components/ProgressBar';
|
||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
// import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||||
import MovieFormats from 'Movie/MovieFormats';
|
import MovieFormats from 'Movie/MovieFormats';
|
||||||
import MovieLanguage from 'Movie/MovieLanguage';
|
import MovieLanguage from 'Movie/MovieLanguage';
|
||||||
import MovieQuality from 'Movie/MovieQuality';
|
import MovieQuality from 'Movie/MovieQuality';
|
||||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import QueueStatusCell from './QueueStatusCell';
|
import QueueStatusCell from './QueueStatusCell';
|
||||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||||
@@ -44,14 +42,14 @@ class QueueRow extends Component {
|
|||||||
this.setState({ isRemoveQueueItemModalOpen: true });
|
this.setState({ isRemoveQueueItemModalOpen: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
onRemoveQueueItemModalConfirmed = (blocklist, skipRedownload) => {
|
onRemoveQueueItemModalConfirmed = (blocklist) => {
|
||||||
const {
|
const {
|
||||||
onRemoveQueueItemPress,
|
onRemoveQueueItemPress,
|
||||||
onQueueRowModalOpenOrClose
|
onQueueRowModalOpenOrClose
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
onQueueRowModalOpenOrClose(false);
|
onQueueRowModalOpenOrClose(false);
|
||||||
onRemoveQueueItemPress(blocklist, skipRedownload);
|
onRemoveQueueItemPress(blocklist);
|
||||||
|
|
||||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||||
};
|
};
|
||||||
@@ -90,15 +88,12 @@ class QueueRow extends Component {
|
|||||||
movie,
|
movie,
|
||||||
quality,
|
quality,
|
||||||
customFormats,
|
customFormats,
|
||||||
customFormatScore,
|
|
||||||
languages,
|
languages,
|
||||||
protocol,
|
protocol,
|
||||||
indexer,
|
indexer,
|
||||||
outputPath,
|
outputPath,
|
||||||
downloadClient,
|
downloadClient,
|
||||||
downloadClientHasPostImportCategory,
|
|
||||||
estimatedCompletionTime,
|
estimatedCompletionTime,
|
||||||
added,
|
|
||||||
timeleft,
|
timeleft,
|
||||||
size,
|
size,
|
||||||
sizeleft,
|
sizeleft,
|
||||||
@@ -206,24 +201,6 @@ class QueueRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'customFormatScore') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.customFormatScore}
|
|
||||||
>
|
|
||||||
<Tooltip
|
|
||||||
anchor={formatCustomFormatScore(
|
|
||||||
customFormatScore,
|
|
||||||
customFormats.length
|
|
||||||
)}
|
|
||||||
tooltip={<MovieFormats formats={customFormats} />}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'protocol') {
|
if (name === 'protocol') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
@@ -317,15 +294,6 @@ class QueueRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'added') {
|
|
||||||
return (
|
|
||||||
<RelativeDateCellConnector
|
|
||||||
key={name}
|
|
||||||
date={added}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'actions') {
|
if (name === 'actions') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell
|
<TableRowCell
|
||||||
@@ -374,7 +342,6 @@ class QueueRow extends Component {
|
|||||||
<RemoveQueueItemModal
|
<RemoveQueueItemModal
|
||||||
isOpen={isRemoveQueueItemModalOpen}
|
isOpen={isRemoveQueueItemModalOpen}
|
||||||
sourceTitle={title}
|
sourceTitle={title}
|
||||||
canChangeCategory={!!downloadClientHasPostImportCategory}
|
|
||||||
canIgnore={!!movie}
|
canIgnore={!!movie}
|
||||||
isPending={isPending}
|
isPending={isPending}
|
||||||
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
||||||
@@ -398,15 +365,12 @@ QueueRow.propTypes = {
|
|||||||
movie: PropTypes.object,
|
movie: PropTypes.object,
|
||||||
quality: PropTypes.object.isRequired,
|
quality: PropTypes.object.isRequired,
|
||||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
customFormats: PropTypes.arrayOf(PropTypes.object),
|
||||||
customFormatScore: PropTypes.number.isRequired,
|
|
||||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
protocol: PropTypes.string.isRequired,
|
protocol: PropTypes.string.isRequired,
|
||||||
indexer: PropTypes.string,
|
indexer: PropTypes.string,
|
||||||
outputPath: PropTypes.string,
|
outputPath: PropTypes.string,
|
||||||
downloadClient: PropTypes.string,
|
downloadClient: PropTypes.string,
|
||||||
downloadClientHasPostImportCategory: PropTypes.bool,
|
|
||||||
estimatedCompletionTime: PropTypes.string,
|
estimatedCompletionTime: PropTypes.string,
|
||||||
added: PropTypes.string,
|
|
||||||
timeleft: PropTypes.string,
|
timeleft: PropTypes.string,
|
||||||
size: PropTypes.number,
|
size: PropTypes.number,
|
||||||
year: PropTypes.number,
|
year: PropTypes.number,
|
||||||
@@ -426,7 +390,6 @@ QueueRow.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
QueueRow.defaultProps = {
|
QueueRow.defaultProps = {
|
||||||
customFormats: [],
|
|
||||||
isGrabbing: false,
|
isGrabbing: false,
|
||||||
isRemoving: false
|
isRemoving: false
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
.noMessages {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'noMessages': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './QueueStatus.css';
|
|
||||||
|
|
||||||
function getDetailedPopoverBody(statusMessages) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
statusMessages.map(({ title, messages }) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={title}
|
|
||||||
className={messages.length ? undefined: styles.noMessages}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
messages.map((message) => {
|
|
||||||
return (
|
|
||||||
<li key={message}>
|
|
||||||
{message}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function QueueStatus(props) {
|
|
||||||
const {
|
|
||||||
sourceTitle,
|
|
||||||
status,
|
|
||||||
trackedDownloadStatus,
|
|
||||||
trackedDownloadState,
|
|
||||||
statusMessages,
|
|
||||||
errorMessage,
|
|
||||||
position,
|
|
||||||
canFlip
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const hasWarning = trackedDownloadStatus === 'warning';
|
|
||||||
const hasError = trackedDownloadStatus === 'error';
|
|
||||||
|
|
||||||
// status === 'downloading'
|
|
||||||
let iconName = icons.DOWNLOADING;
|
|
||||||
let iconKind = kinds.DEFAULT;
|
|
||||||
let title = translate('Downloading');
|
|
||||||
|
|
||||||
if (status === 'paused') {
|
|
||||||
iconName = icons.PAUSED;
|
|
||||||
title = translate('Paused');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'queued') {
|
|
||||||
iconName = icons.QUEUED;
|
|
||||||
title = translate('Queued');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'completed') {
|
|
||||||
iconName = icons.DOWNLOADED;
|
|
||||||
title = translate('Downloaded');
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importPending') {
|
|
||||||
title += ` - ${translate('WaitingToImport')}`;
|
|
||||||
iconKind = kinds.PURPLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importing') {
|
|
||||||
title += ` - ${translate('Importing')}`;
|
|
||||||
iconKind = kinds.PURPLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'failedPending') {
|
|
||||||
title += ` - ${translate('WaitingToProcess')}`;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasWarning) {
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'delay') {
|
|
||||||
iconName = icons.PENDING;
|
|
||||||
title = translate('Pending');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'downloadClientUnavailable') {
|
|
||||||
iconName = icons.PENDING;
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
title = translate('PendingDownloadClientUnavailable');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'failed') {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('DownloadFailed');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'warning') {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
|
|
||||||
title = translate('DownloadWarning', { warningMessage });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasError) {
|
|
||||||
if (status === 'completed') {
|
|
||||||
iconName = icons.DOWNLOAD;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('ImportFailed', { sourceTitle });
|
|
||||||
} else {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('DownloadFailed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Icon
|
|
||||||
name={iconName}
|
|
||||||
kind={iconKind}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
title={title}
|
|
||||||
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
|
|
||||||
position={position}
|
|
||||||
canFlip={canFlip}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
QueueStatus.propTypes = {
|
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
|
||||||
status: PropTypes.string.isRequired,
|
|
||||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
|
||||||
trackedDownloadState: PropTypes.string.isRequired,
|
|
||||||
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
errorMessage: PropTypes.string,
|
|
||||||
position: PropTypes.oneOf(tooltipPositions.all).isRequired,
|
|
||||||
canFlip: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
QueueStatus.defaultProps = {
|
|
||||||
trackedDownloadStatus: 'ok',
|
|
||||||
trackedDownloadState: 'downloading',
|
|
||||||
canFlip: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QueueStatus;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'status': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,10 +1,39 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import { tooltipPositions } from 'Helpers/Props';
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
import QueueStatus from './QueueStatus';
|
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './QueueStatusCell.css';
|
import styles from './QueueStatusCell.css';
|
||||||
|
|
||||||
|
function getDetailedPopoverBody(statusMessages) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
statusMessages.map(({ title, messages }) => {
|
||||||
|
return (
|
||||||
|
<div key={title}>
|
||||||
|
{title}
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
messages.map((message) => {
|
||||||
|
return (
|
||||||
|
<li key={message}>
|
||||||
|
{message}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function QueueStatusCell(props) {
|
function QueueStatusCell(props) {
|
||||||
const {
|
const {
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
@@ -15,16 +44,97 @@ function QueueStatusCell(props) {
|
|||||||
errorMessage
|
errorMessage
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const hasWarning = trackedDownloadStatus === 'warning';
|
||||||
|
const hasError = trackedDownloadStatus === 'error';
|
||||||
|
|
||||||
|
// status === 'downloading'
|
||||||
|
let iconName = icons.DOWNLOADING;
|
||||||
|
let iconKind = kinds.DEFAULT;
|
||||||
|
let title = translate('Downloading');
|
||||||
|
|
||||||
|
if (status === 'paused') {
|
||||||
|
iconName = icons.PAUSED;
|
||||||
|
title = translate('Paused');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'queued') {
|
||||||
|
iconName = icons.QUEUED;
|
||||||
|
title = translate('Queued');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'completed') {
|
||||||
|
iconName = icons.DOWNLOADED;
|
||||||
|
title = translate('Downloaded');
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importPending') {
|
||||||
|
title += ` - ${translate('WaitingToImport')}`;
|
||||||
|
iconKind = kinds.PURPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importing') {
|
||||||
|
title += ` - ${translate('Importing')}`;
|
||||||
|
iconKind = kinds.PURPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'failedPending') {
|
||||||
|
title += ` - ${translate('WaitingToProcess')}`;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasWarning) {
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'delay') {
|
||||||
|
iconName = icons.PENDING;
|
||||||
|
title = translate('Pending');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'DownloadClientUnavailable') {
|
||||||
|
iconName = icons.PENDING;
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
title = `${translate('Pending')} - ${translate('DownloadClientUnavailable')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'failed') {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('DownloadFailed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'warning') {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
|
||||||
|
title = translate('DownloadWarning', [warningMessage]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
if (status === 'completed') {
|
||||||
|
iconName = icons.DOWNLOAD;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('ImportFailed', [sourceTitle]);
|
||||||
|
} else {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('DownloadFailed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowCell className={styles.status}>
|
<TableRowCell className={styles.status}>
|
||||||
<QueueStatus
|
<Popover
|
||||||
sourceTitle={sourceTitle}
|
anchor={
|
||||||
status={status}
|
<Icon
|
||||||
trackedDownloadStatus={trackedDownloadStatus}
|
name={iconName}
|
||||||
trackedDownloadState={trackedDownloadState}
|
kind={iconKind}
|
||||||
statusMessages={statusMessages}
|
/>
|
||||||
errorMessage={errorMessage}
|
}
|
||||||
|
title={title}
|
||||||
|
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
|
||||||
position={tooltipPositions.RIGHT}
|
position={tooltipPositions.RIGHT}
|
||||||
|
canFlip={false}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
@@ -40,8 +150,8 @@ QueueStatusCell.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
QueueStatusCell.defaultProps = {
|
QueueStatusCell.defaultProps = {
|
||||||
trackedDownloadStatus: 'ok',
|
trackedDownloadStatus: translate('Ok'),
|
||||||
trackedDownloadState: 'downloading'
|
trackedDownloadState: translate('Downloading')
|
||||||
};
|
};
|
||||||
|
|
||||||
export default QueueStatusCell;
|
export default QueueStatusCell;
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'message': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
|
class RemoveQueueItemModal extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
remove: true,
|
||||||
|
blocklist: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
resetState = function() {
|
||||||
|
this.setState({
|
||||||
|
remove: true,
|
||||||
|
blocklist: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onRemoveChange = ({ value }) => {
|
||||||
|
this.setState({ remove: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onBlocklistChange = ({ value }) => {
|
||||||
|
this.setState({ blocklist: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onRemoveConfirmed = () => {
|
||||||
|
const state = this.state;
|
||||||
|
|
||||||
|
this.resetState();
|
||||||
|
this.props.onRemovePress(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
onModalClose = () => {
|
||||||
|
this.resetState();
|
||||||
|
this.props.onModalClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
sourceTitle,
|
||||||
|
canIgnore,
|
||||||
|
isPending
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { remove, blocklist } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
size={sizes.MEDIUM}
|
||||||
|
onModalClose={this.onModalClose}
|
||||||
|
>
|
||||||
|
<ModalContent
|
||||||
|
onModalClose={this.onModalClose}
|
||||||
|
>
|
||||||
|
<ModalHeader>
|
||||||
|
{translate('Remove')} - {sourceTitle}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<div>
|
||||||
|
{translate('RemoveFromQueueText', [sourceTitle])}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
isPending ?
|
||||||
|
null :
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="remove"
|
||||||
|
value={remove}
|
||||||
|
helpTextWarning={translate('RemoveHelpTextWarning')}
|
||||||
|
isDisabled={!canIgnore}
|
||||||
|
onChange={this.onRemoveChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
}
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('BlocklistRelease')}</FormLabel>
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="blocklist"
|
||||||
|
value={blocklist}
|
||||||
|
helpText={translate('BlocklistHelpText')}
|
||||||
|
onChange={this.onBlocklistChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={this.onModalClose}>
|
||||||
|
{translate('Close')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
onPress={this.onRemoveConfirmed}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveQueueItemModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
|
canIgnore: PropTypes.bool.isRequired,
|
||||||
|
isPending: PropTypes.bool.isRequired,
|
||||||
|
onRemovePress: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemoveQueueItemModal;
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
import React, { useCallback, useMemo, useState } from 'react';
|
|
||||||
import FormGroup from 'Components/Form/FormGroup';
|
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
|
||||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './RemoveQueueItemModal.css';
|
|
||||||
|
|
||||||
interface RemovePressProps {
|
|
||||||
remove: boolean;
|
|
||||||
changeCategory: boolean;
|
|
||||||
blocklist: boolean;
|
|
||||||
skipRedownload: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RemoveQueueItemModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
sourceTitle: string;
|
|
||||||
canChangeCategory: boolean;
|
|
||||||
canIgnore: boolean;
|
|
||||||
isPending: boolean;
|
|
||||||
selectedCount?: number;
|
|
||||||
onRemovePress(props: RemovePressProps): void;
|
|
||||||
onModalClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
type RemovalMethod = 'removeFromClient' | 'changeCategory' | 'ignore';
|
|
||||||
type BlocklistMethod =
|
|
||||||
| 'doNotBlocklist'
|
|
||||||
| 'blocklistAndSearch'
|
|
||||||
| 'blocklistOnly';
|
|
||||||
|
|
||||||
function RemoveQueueItemModal(props: RemoveQueueItemModalProps) {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
sourceTitle,
|
|
||||||
canIgnore,
|
|
||||||
canChangeCategory,
|
|
||||||
isPending,
|
|
||||||
selectedCount,
|
|
||||||
onRemovePress,
|
|
||||||
onModalClose,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const multipleSelected = selectedCount && selectedCount > 1;
|
|
||||||
|
|
||||||
const [removalMethod, setRemovalMethod] =
|
|
||||||
useState<RemovalMethod>('removeFromClient');
|
|
||||||
const [blocklistMethod, setBlocklistMethod] =
|
|
||||||
useState<BlocklistMethod>('doNotBlocklist');
|
|
||||||
|
|
||||||
const { title, message } = useMemo(() => {
|
|
||||||
if (!selectedCount) {
|
|
||||||
return {
|
|
||||||
title: translate('RemoveQueueItem', { sourceTitle }),
|
|
||||||
message: translate('RemoveQueueItemConfirmation', { sourceTitle }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedCount === 1) {
|
|
||||||
return {
|
|
||||||
title: translate('RemoveSelectedItem'),
|
|
||||||
message: translate('RemoveSelectedItemQueueMessageText'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: translate('RemoveSelectedItems'),
|
|
||||||
message: translate('RemoveSelectedItemsQueueMessageText', {
|
|
||||||
selectedCount,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}, [sourceTitle, selectedCount]);
|
|
||||||
|
|
||||||
const removalMethodOptions = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: 'removeFromClient',
|
|
||||||
value: translate('RemoveFromDownloadClient'),
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('RemoveMultipleFromDownloadClientHint')
|
|
||||||
: translate('RemoveFromDownloadClientHint'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'changeCategory',
|
|
||||||
value: translate('ChangeCategory'),
|
|
||||||
isDisabled: !canChangeCategory,
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('ChangeCategoryMultipleHint')
|
|
||||||
: translate('ChangeCategoryHint'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'ignore',
|
|
||||||
value: multipleSelected
|
|
||||||
? translate('IgnoreDownloads')
|
|
||||||
: translate('IgnoreDownload'),
|
|
||||||
isDisabled: !canIgnore,
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('IgnoreDownloadsHint')
|
|
||||||
: translate('IgnoreDownloadHint'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [canChangeCategory, canIgnore, multipleSelected]);
|
|
||||||
|
|
||||||
const blocklistMethodOptions = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: 'doNotBlocklist',
|
|
||||||
value: translate('DoNotBlocklist'),
|
|
||||||
hint: translate('DoNotBlocklistHint'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'blocklistAndSearch',
|
|
||||||
value: translate('BlocklistAndSearch'),
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('BlocklistAndSearchMultipleHint')
|
|
||||||
: translate('BlocklistAndSearchHint'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'blocklistOnly',
|
|
||||||
value: translate('BlocklistOnly'),
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('BlocklistMultipleOnlyHint')
|
|
||||||
: translate('BlocklistOnlyHint'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [multipleSelected]);
|
|
||||||
|
|
||||||
const handleRemovalMethodChange = useCallback(
|
|
||||||
({ value }: { value: RemovalMethod }) => {
|
|
||||||
setRemovalMethod(value);
|
|
||||||
},
|
|
||||||
[setRemovalMethod]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleBlocklistMethodChange = useCallback(
|
|
||||||
({ value }: { value: BlocklistMethod }) => {
|
|
||||||
setBlocklistMethod(value);
|
|
||||||
},
|
|
||||||
[setBlocklistMethod]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleConfirmRemove = useCallback(() => {
|
|
||||||
onRemovePress({
|
|
||||||
remove: removalMethod === 'removeFromClient',
|
|
||||||
changeCategory: removalMethod === 'changeCategory',
|
|
||||||
blocklist: blocklistMethod !== 'doNotBlocklist',
|
|
||||||
skipRedownload: blocklistMethod === 'blocklistOnly',
|
|
||||||
});
|
|
||||||
|
|
||||||
setRemovalMethod('removeFromClient');
|
|
||||||
setBlocklistMethod('doNotBlocklist');
|
|
||||||
}, [
|
|
||||||
removalMethod,
|
|
||||||
blocklistMethod,
|
|
||||||
setRemovalMethod,
|
|
||||||
setBlocklistMethod,
|
|
||||||
onRemovePress,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleModalClose = useCallback(() => {
|
|
||||||
setRemovalMethod('removeFromClient');
|
|
||||||
setBlocklistMethod('doNotBlocklist');
|
|
||||||
|
|
||||||
onModalClose();
|
|
||||||
}, [setRemovalMethod, setBlocklistMethod, onModalClose]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={handleModalClose}>
|
|
||||||
<ModalContent onModalClose={handleModalClose}>
|
|
||||||
<ModalHeader>{title}</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<div className={styles.message}>{message}</div>
|
|
||||||
|
|
||||||
{isPending ? null : (
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('RemoveQueueItemRemovalMethod')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.SELECT}
|
|
||||||
name="removalMethod"
|
|
||||||
value={removalMethod}
|
|
||||||
values={removalMethodOptions}
|
|
||||||
isDisabled={!canChangeCategory && !canIgnore}
|
|
||||||
helpTextWarning={translate(
|
|
||||||
'RemoveQueueItemRemovalMethodHelpTextWarning'
|
|
||||||
)}
|
|
||||||
onChange={handleRemovalMethodChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>
|
|
||||||
{multipleSelected
|
|
||||||
? translate('BlocklistReleases')
|
|
||||||
: translate('BlocklistRelease')}
|
|
||||||
</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.SELECT}
|
|
||||||
name="blocklistMethod"
|
|
||||||
value={blocklistMethod}
|
|
||||||
values={blocklistMethodOptions}
|
|
||||||
helpText={translate('BlocklistReleaseHelpText')}
|
|
||||||
onChange={handleBlocklistMethodChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={handleModalClose}>{translate('Close')}</Button>
|
|
||||||
|
|
||||||
<Button kind={kinds.DANGER} onPress={handleConfirmRemove}>
|
|
||||||
{translate('Remove')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RemoveQueueItemModal;
|
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './RemoveQueueItemsModal.css';
|
||||||
|
|
||||||
|
class RemoveQueueItemsModal extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
remove: true,
|
||||||
|
blocklist: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
resetState = function() {
|
||||||
|
this.setState({
|
||||||
|
remove: true,
|
||||||
|
blocklist: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onRemoveChange = ({ value }) => {
|
||||||
|
this.setState({ remove: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onBlocklistChange = ({ value }) => {
|
||||||
|
this.setState({ blocklist: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onRemoveConfirmed = () => {
|
||||||
|
const state = this.state;
|
||||||
|
|
||||||
|
this.resetState();
|
||||||
|
this.props.onRemovePress(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
onModalClose = () => {
|
||||||
|
this.resetState();
|
||||||
|
this.props.onModalClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
selectedCount,
|
||||||
|
canIgnore,
|
||||||
|
allPending
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { remove, blocklist } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
size={sizes.MEDIUM}
|
||||||
|
onModalClose={this.onModalClose}
|
||||||
|
>
|
||||||
|
<ModalContent
|
||||||
|
onModalClose={this.onModalClose}
|
||||||
|
>
|
||||||
|
<ModalHeader>
|
||||||
|
{selectedCount > 1 ? translate('RemoveSelectedItems') : translate('RemoveSelectedItem')}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<div className={styles.message}>
|
||||||
|
{selectedCount > 1 ? translate('AreYouSureYouWantToRemoveSelectedItemsFromQueue', selectedCount) : translate('AreYouSureYouWantToRemoveSelectedItemFromQueue')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
allPending ?
|
||||||
|
null :
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="remove"
|
||||||
|
value={remove}
|
||||||
|
helpTextWarning={translate('RemoveHelpTextWarning')}
|
||||||
|
isDisabled={!canIgnore}
|
||||||
|
onChange={this.onRemoveChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
}
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>
|
||||||
|
{selectedCount > 1 ? translate('BlocklistReleases') : translate('BlocklistRelease')}
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="blocklist"
|
||||||
|
value={blocklist}
|
||||||
|
helpText={translate('BlocklistHelpText')}
|
||||||
|
onChange={this.onBlocklistChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={this.onModalClose}>
|
||||||
|
{translate('Close')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
onPress={this.onRemoveConfirmed}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveQueueItemsModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
selectedCount: PropTypes.number.isRequired,
|
||||||
|
canIgnore: PropTypes.bool.isRequired,
|
||||||
|
allPending: PropTypes.bool.isRequired,
|
||||||
|
onRemovePress: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemoveQueueItemsModal;
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'timeleft': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import formatTime from 'Utilities/Date/formatTime';
|
import formatTime from 'Utilities/Date/formatTime';
|
||||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||||
@@ -28,13 +25,11 @@ function TimeleftCell(props) {
|
|||||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowCell className={styles.timeleft}>
|
<TableRowCell
|
||||||
<Tooltip
|
className={styles.timeleft}
|
||||||
anchor={<Icon name={icons.INFO} />}
|
title={translate('DelayingDownloadUntilInterp', [date, time])}
|
||||||
tooltip={translate('DelayingDownloadUntil', { date, time })}
|
>
|
||||||
kind={kinds.INVERSE}
|
-
|
||||||
position={tooltipPositions.TOP}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -44,13 +39,11 @@ function TimeleftCell(props) {
|
|||||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowCell className={styles.timeleft}>
|
<TableRowCell
|
||||||
<Tooltip
|
className={styles.timeleft}
|
||||||
anchor={<Icon name={icons.INFO} />}
|
title={translate('RetryingDownloadInterp', [date, time])}
|
||||||
tooltip={translate('RetryingDownloadOn', { date, time })}
|
>
|
||||||
kind={kinds.INVERSE}
|
-
|
||||||
position={tooltipPositions.TOP}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'clearLookupButton': string;
|
|
||||||
'helpText': string;
|
|
||||||
'message': string;
|
|
||||||
'noMoviesText': string;
|
|
||||||
'noResults': string;
|
|
||||||
'searchContainer': string;
|
|
||||||
'searchIconContainer': string;
|
|
||||||
'searchInput': string;
|
|
||||||
'searchResults': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import TextInput from 'Components/Form/TextInput';
|
import TextInput from 'Components/Form/TextInput';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Button from 'Components/Link/Button';
|
import Button from 'Components/Link/Button';
|
||||||
@@ -131,12 +130,7 @@ class AddNewMovie extends Component {
|
|||||||
<div className={styles.helpText}>
|
<div className={styles.helpText}>
|
||||||
{translate('FailedLoadingSearchResults')}
|
{translate('FailedLoadingSearchResults')}
|
||||||
</div>
|
</div>
|
||||||
<Alert kind={kinds.WARNING}>{getErrorMessage(error)}</Alert>
|
<div>{getErrorMessage(error)}</div>
|
||||||
<div>
|
|
||||||
<Link to="https://wiki.servarr.com/radarr/troubleshooting#invalid-response-received-from-tmdb">
|
|
||||||
{translate('WhySearchesCouldBeFailing')}
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div> : null
|
</div> : null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +155,7 @@ class AddNewMovie extends Component {
|
|||||||
!isFetching && !error && !items.length && !!term &&
|
!isFetching && !error && !items.length && !!term &&
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
<div className={styles.noResults}>
|
<div className={styles.noResults}>
|
||||||
{translate('CouldNotFindResults', { term })}
|
{translate('CouldNotFindResults', [term])}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{translate('YouCanAlsoSearch')}
|
{translate('YouCanAlsoSearch')}
|
||||||
|
|||||||
@@ -3,13 +3,10 @@ import React, { Component } from 'react';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { clearAddMovie, lookupMovie } from 'Store/Actions/addMovieActions';
|
import { clearAddMovie, lookupMovie } from 'Store/Actions/addMovieActions';
|
||||||
import { clearMovieFiles, fetchMovieFiles } from 'Store/Actions/movieFileActions';
|
|
||||||
import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions';
|
import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||||
import { fetchImportExclusions } from 'Store/Actions/Settings/importExclusions';
|
import { fetchImportExclusions } from 'Store/Actions/Settings/importExclusions';
|
||||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
|
||||||
import selectUniqueIds from 'Utilities/Object/selectUniqueIds';
|
|
||||||
import parseUrl from 'Utilities/String/parseUrl';
|
import parseUrl from 'Utilities/String/parseUrl';
|
||||||
import AddNewMovie from './AddNewMovie';
|
import AddNewMovie from './AddNewMovie';
|
||||||
|
|
||||||
@@ -38,9 +35,7 @@ const mapDispatchToProps = {
|
|||||||
fetchRootFolders,
|
fetchRootFolders,
|
||||||
fetchImportExclusions,
|
fetchImportExclusions,
|
||||||
fetchQueueDetails,
|
fetchQueueDetails,
|
||||||
clearQueueDetails,
|
clearQueueDetails
|
||||||
fetchMovieFiles,
|
|
||||||
clearMovieFiles
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddNewMovieConnector extends Component {
|
class AddNewMovieConnector extends Component {
|
||||||
@@ -60,20 +55,6 @@ class AddNewMovieConnector extends Component {
|
|||||||
this.props.fetchQueueDetails();
|
this.props.fetchQueueDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
const {
|
|
||||||
items
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (hasDifferentItems(prevProps.items, items)) {
|
|
||||||
const movieIds = selectUniqueIds(items, 'internalId');
|
|
||||||
|
|
||||||
if (movieIds.length) {
|
|
||||||
this.props.fetchMovieFiles({ movieId: movieIds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this._movieLookupTimeout) {
|
if (this._movieLookupTimeout) {
|
||||||
clearTimeout(this._movieLookupTimeout);
|
clearTimeout(this._movieLookupTimeout);
|
||||||
@@ -81,7 +62,6 @@ class AddNewMovieConnector extends Component {
|
|||||||
|
|
||||||
this.props.clearAddMovie();
|
this.props.clearAddMovie();
|
||||||
this.props.clearQueueDetails();
|
this.props.clearQueueDetails();
|
||||||
this.props.clearMovieFiles();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -127,15 +107,12 @@ class AddNewMovieConnector extends Component {
|
|||||||
|
|
||||||
AddNewMovieConnector.propTypes = {
|
AddNewMovieConnector.propTypes = {
|
||||||
term: PropTypes.string,
|
term: PropTypes.string,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
lookupMovie: PropTypes.func.isRequired,
|
lookupMovie: PropTypes.func.isRequired,
|
||||||
clearAddMovie: PropTypes.func.isRequired,
|
clearAddMovie: PropTypes.func.isRequired,
|
||||||
fetchRootFolders: PropTypes.func.isRequired,
|
fetchRootFolders: PropTypes.func.isRequired,
|
||||||
fetchImportExclusions: PropTypes.func.isRequired,
|
fetchImportExclusions: PropTypes.func.isRequired,
|
||||||
fetchQueueDetails: PropTypes.func.isRequired,
|
fetchQueueDetails: PropTypes.func.isRequired,
|
||||||
clearQueueDetails: PropTypes.func.isRequired,
|
clearQueueDetails: PropTypes.func.isRequired
|
||||||
fetchMovieFiles: PropTypes.func.isRequired,
|
|
||||||
clearMovieFiles: PropTypes.func.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector);
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'addButton': string;
|
|
||||||
'container': string;
|
|
||||||
'info': string;
|
|
||||||
'labelIcon': string;
|
|
||||||
'modalFooter': string;
|
|
||||||
'overview': string;
|
|
||||||
'poster': string;
|
|
||||||
'searchForMissingMovieContainer': string;
|
|
||||||
'searchForMissingMovieInput': string;
|
|
||||||
'searchForMissingMovieLabel': string;
|
|
||||||
'searchForMissingMovieLabelContainer': string;
|
|
||||||
'year': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -20,10 +20,6 @@ class AddNewMovieModalContent extends Component {
|
|||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onQualityProfileIdChange = ({ value }) => {
|
|
||||||
this.props.onInputChange({ name: 'qualityProfileId', value: parseInt(value) });
|
|
||||||
};
|
|
||||||
|
|
||||||
onAddMoviePress = () => {
|
onAddMoviePress = () => {
|
||||||
this.props.onAddMoviePress();
|
this.props.onAddMoviePress();
|
||||||
};
|
};
|
||||||
@@ -40,7 +36,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
isAdding,
|
isAdding,
|
||||||
rootFolderPath,
|
rootFolderPath,
|
||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileIds,
|
||||||
minimumAvailability,
|
minimumAvailability,
|
||||||
searchForMovie,
|
searchForMovie,
|
||||||
folder,
|
folder,
|
||||||
@@ -130,9 +126,9 @@ class AddNewMovieModalContent extends Component {
|
|||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||||
name="qualityProfileId"
|
name="qualityProfileIds"
|
||||||
onChange={this.onQualityProfileIdChange}
|
onChange={onInputChange}
|
||||||
{...qualityProfileId}
|
{...qualityProfileIds}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
@@ -189,7 +185,7 @@ AddNewMovieModalContent.propTypes = {
|
|||||||
addError: PropTypes.object,
|
addError: PropTypes.object,
|
||||||
rootFolderPath: PropTypes.object,
|
rootFolderPath: PropTypes.object,
|
||||||
monitor: PropTypes.object.isRequired,
|
monitor: PropTypes.object.isRequired,
|
||||||
qualityProfileId: PropTypes.object,
|
qualityProfileIds: PropTypes.arrayOf(PropTypes.object),
|
||||||
minimumAvailability: PropTypes.object.isRequired,
|
minimumAvailability: PropTypes.object.isRequired,
|
||||||
searchForMovie: PropTypes.object.isRequired,
|
searchForMovie: PropTypes.object.isRequired,
|
||||||
folder: PropTypes.string.isRequired,
|
folder: PropTypes.string.isRequired,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class AddNewMovieModalContentConnector extends Component {
|
|||||||
tmdbId,
|
tmdbId,
|
||||||
rootFolderPath,
|
rootFolderPath,
|
||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileIds,
|
||||||
minimumAvailability,
|
minimumAvailability,
|
||||||
searchForMovie,
|
searchForMovie,
|
||||||
tags
|
tags
|
||||||
@@ -68,7 +68,7 @@ class AddNewMovieModalContentConnector extends Component {
|
|||||||
tmdbId,
|
tmdbId,
|
||||||
rootFolderPath: rootFolderPath.value,
|
rootFolderPath: rootFolderPath.value,
|
||||||
monitor: monitor.value,
|
monitor: monitor.value,
|
||||||
qualityProfileId: qualityProfileId.value,
|
qualityProfileIds: qualityProfileIds.value,
|
||||||
minimumAvailability: minimumAvailability.value,
|
minimumAvailability: minimumAvailability.value,
|
||||||
searchForMovie: searchForMovie.value,
|
searchForMovie: searchForMovie.value,
|
||||||
tags: tags.value
|
tags: tags.value
|
||||||
@@ -93,7 +93,7 @@ AddNewMovieModalContentConnector.propTypes = {
|
|||||||
tmdbId: PropTypes.number.isRequired,
|
tmdbId: PropTypes.number.isRequired,
|
||||||
rootFolderPath: PropTypes.object,
|
rootFolderPath: PropTypes.object,
|
||||||
monitor: PropTypes.object.isRequired,
|
monitor: PropTypes.object.isRequired,
|
||||||
qualityProfileId: PropTypes.object,
|
qualityProfileIds: PropTypes.arrayOf(PropTypes.object),
|
||||||
minimumAvailability: PropTypes.object.isRequired,
|
minimumAvailability: PropTypes.object.isRequired,
|
||||||
searchForMovie: PropTypes.object.isRequired,
|
searchForMovie: PropTypes.object.isRequired,
|
||||||
tags: PropTypes.object.isRequired,
|
tags: PropTypes.object.isRequired,
|
||||||
|
|||||||
@@ -85,13 +85,8 @@
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.studio,
|
|
||||||
.genres {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.links {
|
.links {
|
||||||
margin-left: 5px;
|
margin-left: 8px;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'alreadyExistsIcon': string;
|
|
||||||
'certification': string;
|
|
||||||
'content': string;
|
|
||||||
'exclusionIcon': string;
|
|
||||||
'genres': string;
|
|
||||||
'icons': string;
|
|
||||||
'links': string;
|
|
||||||
'overlay': string;
|
|
||||||
'overview': string;
|
|
||||||
'poster': string;
|
|
||||||
'posterContainer': string;
|
|
||||||
'runtime': string;
|
|
||||||
'searchResult': string;
|
|
||||||
'statusContainer': string;
|
|
||||||
'studio': string;
|
|
||||||
'title': string;
|
|
||||||
'titleContainer': string;
|
|
||||||
'titleRow': string;
|
|
||||||
'underlay': string;
|
|
||||||
'year': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import ImdbRating from 'Components/ImdbRating';
|
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import TmdbRating from 'Components/TmdbRating';
|
import TmdbRating from 'Components/TmdbRating';
|
||||||
@@ -62,13 +61,11 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
titleSlug,
|
titleSlug,
|
||||||
year,
|
year,
|
||||||
studio,
|
studio,
|
||||||
genres,
|
|
||||||
status,
|
status,
|
||||||
overview,
|
overview,
|
||||||
ratings,
|
ratings,
|
||||||
folder,
|
folder,
|
||||||
images,
|
images,
|
||||||
existingMovieId,
|
|
||||||
isExistingMovie,
|
isExistingMovie,
|
||||||
isExclusionMovie,
|
isExclusionMovie,
|
||||||
isSmallScreen,
|
isSmallScreen,
|
||||||
@@ -76,19 +73,22 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
id,
|
id,
|
||||||
monitored,
|
monitored,
|
||||||
isAvailable,
|
isAvailable,
|
||||||
movieFile,
|
queueStatus,
|
||||||
queueItem,
|
queueState,
|
||||||
runtime,
|
runtime,
|
||||||
movieRuntimeFormat,
|
movieRuntimeFormat,
|
||||||
certification
|
certification,
|
||||||
|
statistics
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
movieFileCount
|
||||||
|
} = statistics;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isNewAddMovieModalOpen
|
isNewAddMovieModalOpen
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const hasMovieFile = !!movieFile;
|
|
||||||
|
|
||||||
const linkProps = isExistingMovie ? { to: `/movie/${titleSlug}` } : { onPress: this.onPress };
|
const linkProps = isExistingMovie ? { to: `/movie/${titleSlug}` } : { onPress: this.onPress };
|
||||||
const posterWidth = 167;
|
const posterWidth = 167;
|
||||||
const posterHeight = 250;
|
const posterHeight = 250;
|
||||||
@@ -117,20 +117,19 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
images={images}
|
images={images}
|
||||||
size={250}
|
size={250}
|
||||||
overflow={true}
|
overflow={true}
|
||||||
lazy={false}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
isExistingMovie &&
|
isExistingMovie &&
|
||||||
<MovieIndexProgressBar
|
<MovieIndexProgressBar
|
||||||
movieId={existingMovieId}
|
|
||||||
movieFile={movieFile}
|
|
||||||
monitored={monitored}
|
monitored={monitored}
|
||||||
hasFile={hasMovieFile}
|
hasFile={movieFileCount > 0}
|
||||||
status={status}
|
status={status}
|
||||||
width={posterWidth}
|
posterWidth={posterWidth}
|
||||||
detailedProgressBar={true}
|
detailedProgressBar={true}
|
||||||
|
queueStatus={queueStatus}
|
||||||
|
queueState={queueState}
|
||||||
isAvailable={isAvailable}
|
isAvailable={isAvailable}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -201,46 +200,13 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
{
|
|
||||||
ratings.imdb ?
|
|
||||||
<Label size={sizes.LARGE}>
|
|
||||||
<ImdbRating
|
|
||||||
ratings={ratings}
|
|
||||||
iconSize={13}
|
|
||||||
/>
|
|
||||||
</Label> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
!!studio &&
|
!!studio &&
|
||||||
<Label size={sizes.LARGE}>
|
<Label size={sizes.LARGE}>
|
||||||
<Icon
|
{studio}
|
||||||
name={icons.STUDIO}
|
|
||||||
size={13}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className={styles.studio}>
|
|
||||||
{studio}
|
|
||||||
</span>
|
|
||||||
</Label>
|
</Label>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
genres.length > 0 ?
|
|
||||||
<Label size={sizes.LARGE}>
|
|
||||||
<Icon
|
|
||||||
name={icons.GENRE}
|
|
||||||
size={13}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className={styles.genres}>
|
|
||||||
{genres.slice(0, 3).join(', ')}
|
|
||||||
</span>
|
|
||||||
</Label> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
anchor={
|
anchor={
|
||||||
<Label
|
<Label
|
||||||
@@ -252,15 +218,15 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<span className={styles.links}>
|
<span className={styles.links}>
|
||||||
{translate('Links')}
|
Links
|
||||||
</span>
|
</span>
|
||||||
</Label>
|
</Label>
|
||||||
}
|
}
|
||||||
tooltip={
|
tooltip={
|
||||||
<MovieDetailsLinks
|
<MovieDetailsLinks
|
||||||
tmdbId={tmdbId}
|
tmdbId={tmdbId}
|
||||||
imdbId={imdbId}
|
|
||||||
youTubeTrailerId={youTubeTrailerId}
|
youTubeTrailerId={youTubeTrailerId}
|
||||||
|
imdbId={imdbId}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
canFlip={true}
|
canFlip={true}
|
||||||
@@ -271,10 +237,9 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
{
|
{
|
||||||
isExistingMovie && isSmallScreen &&
|
isExistingMovie && isSmallScreen &&
|
||||||
<MovieStatusLabel
|
<MovieStatusLabel
|
||||||
hasMovieFiles={hasMovieFile}
|
hasMovieFiles={movieFileCount > 0}
|
||||||
monitored={monitored}
|
monitored={monitored}
|
||||||
isAvailable={isAvailable}
|
isAvailable={isAvailable}
|
||||||
queueItem={queueItem}
|
|
||||||
id={id}
|
id={id}
|
||||||
useLabel={true}
|
useLabel={true}
|
||||||
colorImpairedMode={colorImpairedMode}
|
colorImpairedMode={colorImpairedMode}
|
||||||
@@ -311,29 +276,32 @@ AddNewMovieSearchResult.propTypes = {
|
|||||||
titleSlug: PropTypes.string.isRequired,
|
titleSlug: PropTypes.string.isRequired,
|
||||||
year: PropTypes.number.isRequired,
|
year: PropTypes.number.isRequired,
|
||||||
studio: PropTypes.string,
|
studio: PropTypes.string,
|
||||||
genres: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
overview: PropTypes.string,
|
overview: PropTypes.string,
|
||||||
ratings: PropTypes.object.isRequired,
|
ratings: PropTypes.object.isRequired,
|
||||||
folder: PropTypes.string.isRequired,
|
folder: PropTypes.string.isRequired,
|
||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
existingMovieId: PropTypes.number,
|
|
||||||
isExistingMovie: PropTypes.bool.isRequired,
|
isExistingMovie: PropTypes.bool.isRequired,
|
||||||
isExclusionMovie: PropTypes.bool.isRequired,
|
isExclusionMovie: PropTypes.bool.isRequired,
|
||||||
isSmallScreen: PropTypes.bool.isRequired,
|
isSmallScreen: PropTypes.bool.isRequired,
|
||||||
id: PropTypes.number,
|
id: PropTypes.number,
|
||||||
|
queueItems: PropTypes.arrayOf(PropTypes.object),
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
|
hasFile: PropTypes.bool.isRequired,
|
||||||
isAvailable: PropTypes.bool.isRequired,
|
isAvailable: PropTypes.bool.isRequired,
|
||||||
movieFile: PropTypes.object,
|
|
||||||
queueItem: PropTypes.object,
|
|
||||||
colorImpairedMode: PropTypes.bool,
|
colorImpairedMode: PropTypes.bool,
|
||||||
|
queueStatus: PropTypes.string,
|
||||||
|
queueState: PropTypes.string,
|
||||||
runtime: PropTypes.number.isRequired,
|
runtime: PropTypes.number.isRequired,
|
||||||
movieRuntimeFormat: PropTypes.string.isRequired,
|
movieRuntimeFormat: PropTypes.string.isRequired,
|
||||||
certification: PropTypes.string
|
certification: PropTypes.string,
|
||||||
|
statistics: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
AddNewMovieSearchResult.defaultProps = {
|
AddNewMovieSearchResult.defaultProps = {
|
||||||
genres: []
|
statistics: {
|
||||||
|
movieFileCount: 0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddNewMovieSearchResult;
|
export default AddNewMovieSearchResult;
|
||||||
|
|||||||
@@ -11,21 +11,16 @@ function createMapStateToProps() {
|
|||||||
createExclusionMovieSelector(),
|
createExclusionMovieSelector(),
|
||||||
createDimensionsSelector(),
|
createDimensionsSelector(),
|
||||||
(state) => state.queue.details.items,
|
(state) => state.queue.details.items,
|
||||||
(state) => state.movieFiles.items,
|
|
||||||
(state, { internalId }) => internalId,
|
(state, { internalId }) => internalId,
|
||||||
(state) => state.settings.ui.item.movieRuntimeFormat,
|
(isExistingMovie, isExclusionMovie, dimensions, queueItems, internalId) => {
|
||||||
(isExistingMovie, isExclusionMovie, dimensions, queueItems, movieFiles, internalId, movieRuntimeFormat) => {
|
const firstQueueItem = queueItems.find((q) => q.movieId === internalId && internalId > 0);
|
||||||
const queueItem = queueItems.find((item) => internalId > 0 && item.movieId === internalId);
|
|
||||||
const movieFile = movieFiles.find((item) => internalId > 0 && item.movieId === internalId);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
existingMovieId: internalId,
|
|
||||||
isExistingMovie,
|
isExistingMovie,
|
||||||
isExclusionMovie,
|
isExclusionMovie,
|
||||||
isSmallScreen: dimensions.isSmallScreen,
|
isSmallScreen: dimensions.isSmallScreen,
|
||||||
queueItem,
|
queueStatus: firstQueueItem ? firstQueueItem.status : null,
|
||||||
movieFile,
|
queueState: firstQueueItem ? firstQueueItem.trackedDownloadState : null
|
||||||
movieRuntimeFormat
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { reduce } from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
import { kinds } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||||
import selectAll from 'Utilities/Table/selectAll';
|
import selectAll from 'Utilities/Table/selectAll';
|
||||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||||
import ImportMovieFooterConnector from './ImportMovieFooterConnector';
|
import ImportMovieFooterConnector from './ImportMovieFooterConnector';
|
||||||
@@ -20,8 +18,6 @@ class ImportMovie extends Component {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.scrollerRef = React.createRef();
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
allSelected: false,
|
allSelected: false,
|
||||||
allUnselected: false,
|
allUnselected: false,
|
||||||
@@ -31,21 +27,18 @@ class ImportMovie extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
setScrollerRef = (ref) => {
|
||||||
|
this.setState({ scroller: ref });
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
getSelectedIds = () => {
|
getSelectedIds = () => {
|
||||||
return reduce(
|
return getSelectedIds(this.state.selectedState, { parseIds: false });
|
||||||
this.state.selectedState,
|
|
||||||
(result, value, id) => {
|
|
||||||
if (value) {
|
|
||||||
result.push(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onSelectAllChange = ({ value }) => {
|
onSelectAllChange = ({ value }) => {
|
||||||
@@ -95,21 +88,25 @@ class ImportMovie extends Component {
|
|||||||
const {
|
const {
|
||||||
allSelected,
|
allSelected,
|
||||||
allUnselected,
|
allUnselected,
|
||||||
selectedState
|
selectedState,
|
||||||
|
scroller
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('ImportMovies')}>
|
<PageContent title={translate('ImportMovies')}>
|
||||||
<PageContentBody ref={this.scrollerRef} >
|
<PageContentBody
|
||||||
|
registerScroller={this.setScrollerRef}
|
||||||
|
onScroll={this.onScroll}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
rootFoldersFetching ? <LoadingIndicator /> : null
|
rootFoldersFetching ? <LoadingIndicator /> : null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!rootFoldersFetching && !!rootFoldersError ?
|
!rootFoldersFetching && !!rootFoldersError ?
|
||||||
<Alert kind={kinds.DANGER}>
|
<div>
|
||||||
{translate('UnableToLoadRootFolders')}
|
{translate('UnableToLoadRootFolders')}
|
||||||
</Alert> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,9 +115,9 @@ class ImportMovie extends Component {
|
|||||||
!rootFoldersFetching &&
|
!rootFoldersFetching &&
|
||||||
rootFoldersPopulated &&
|
rootFoldersPopulated &&
|
||||||
!unmappedFolders.length ?
|
!unmappedFolders.length ?
|
||||||
<Alert kind={kinds.INFO}>
|
<div>
|
||||||
{translate('AllMoviesInPathHaveBeenImported', { path })}
|
{translate('AllMoviesInPathHaveBeenImported', [path])}
|
||||||
</Alert> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,14 +126,14 @@ class ImportMovie extends Component {
|
|||||||
!rootFoldersFetching &&
|
!rootFoldersFetching &&
|
||||||
rootFoldersPopulated &&
|
rootFoldersPopulated &&
|
||||||
!!unmappedFolders.length &&
|
!!unmappedFolders.length &&
|
||||||
this.scrollerRef.current ?
|
scroller ?
|
||||||
<ImportMovieTableConnector
|
<ImportMovieTableConnector
|
||||||
rootFolderId={rootFolderId}
|
rootFolderId={rootFolderId}
|
||||||
unmappedFolders={unmappedFolders}
|
unmappedFolders={unmappedFolders}
|
||||||
allSelected={allSelected}
|
allSelected={allSelected}
|
||||||
allUnselected={allUnselected}
|
allUnselected={allUnselected}
|
||||||
selectedState={selectedState}
|
selectedState={selectedState}
|
||||||
scroller={this.scrollerRef.current}
|
scroller={scroller}
|
||||||
onSelectAllChange={this.onSelectAllChange}
|
onSelectAllChange={this.onSelectAllChange}
|
||||||
onSelectedChange={this.onSelectedChange}
|
onSelectedChange={this.onSelectedChange}
|
||||||
onRemoveSelectedStateItem={this.onRemoveSelectedStateItem}
|
onRemoveSelectedStateItem={this.onRemoveSelectedStateItem}
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
.inputContainer {
|
.inputContainer {
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
|
|
||||||
div {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
@@ -43,17 +35,3 @@
|
|||||||
.importError {
|
.importError {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointSmall) {
|
|
||||||
.inputContainer {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.importButtonContainer {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'importButton': string;
|
|
||||||
'importButtonContainer': string;
|
|
||||||
'importError': string;
|
|
||||||
'inputContainer': string;
|
|
||||||
'label': string;
|
|
||||||
'loading': string;
|
|
||||||
'loadingButton': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -25,13 +25,13 @@ class ImportMovieFooter extends Component {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
defaultMonitor,
|
defaultMonitor,
|
||||||
defaultQualityProfileId,
|
defaultQualityProfileIds,
|
||||||
defaultMinimumAvailability
|
defaultMinimumAvailability
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
monitor: defaultMonitor,
|
monitor: defaultMonitor,
|
||||||
qualityProfileId: defaultQualityProfileId,
|
qualityProfileIds: defaultQualityProfileIds,
|
||||||
minimumAvailability: defaultMinimumAvailability
|
minimumAvailability: defaultMinimumAvailability
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -39,16 +39,16 @@ class ImportMovieFooter extends Component {
|
|||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
const {
|
const {
|
||||||
defaultMonitor,
|
defaultMonitor,
|
||||||
defaultQualityProfileId,
|
defaultQualityProfileIds,
|
||||||
defaultMinimumAvailability,
|
defaultMinimumAvailability,
|
||||||
isMonitorMixed,
|
isMonitorMixed,
|
||||||
isQualityProfileIdMixed,
|
isQualityProfileIdsMixed,
|
||||||
isMinimumAvailabilityMixed
|
isMinimumAvailabilityMixed
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileIds,
|
||||||
minimumAvailability
|
minimumAvailability
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
@@ -60,10 +60,10 @@ class ImportMovieFooter extends Component {
|
|||||||
newState.monitor = defaultMonitor;
|
newState.monitor = defaultMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isQualityProfileIdMixed && qualityProfileId !== MIXED) {
|
if (isQualityProfileIdsMixed && qualityProfileIds !== MIXED) {
|
||||||
newState.qualityProfileId = MIXED;
|
newState.qualityProfileIds = MIXED;
|
||||||
} else if (!isQualityProfileIdMixed && qualityProfileId !== defaultQualityProfileId) {
|
} else if (!isQualityProfileIdsMixed && qualityProfileIds !== defaultQualityProfileIds) {
|
||||||
newState.qualityProfileId = defaultQualityProfileId;
|
newState.qualityProfileIds = defaultQualityProfileIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMinimumAvailabilityMixed && minimumAvailability !== MIXED) {
|
if (isMinimumAvailabilityMixed && minimumAvailability !== MIXED) {
|
||||||
@@ -94,7 +94,7 @@ class ImportMovieFooter extends Component {
|
|||||||
isImporting,
|
isImporting,
|
||||||
isLookingUpMovie,
|
isLookingUpMovie,
|
||||||
isMonitorMixed,
|
isMonitorMixed,
|
||||||
isQualityProfileIdMixed,
|
isQualityProfileIdsMixed,
|
||||||
isMinimumAvailabilityMixed,
|
isMinimumAvailabilityMixed,
|
||||||
hasUnsearchedItems,
|
hasUnsearchedItems,
|
||||||
importError,
|
importError,
|
||||||
@@ -105,7 +105,7 @@ class ImportMovieFooter extends Component {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileIds,
|
||||||
minimumAvailability
|
minimumAvailability
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
@@ -148,10 +148,10 @@ class ImportMovieFooter extends Component {
|
|||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||||
name="qualityProfileId"
|
name="qualityProfileIds"
|
||||||
value={qualityProfileId}
|
value={qualityProfileIds}
|
||||||
isDisabled={!selectedCount}
|
isDisabled={!selectedCount}
|
||||||
includeMixed={isQualityProfileIdMixed}
|
includeMixed={isQualityProfileIdsMixed}
|
||||||
onChange={this.onInputChange}
|
onChange={this.onInputChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -257,10 +257,10 @@ ImportMovieFooter.propTypes = {
|
|||||||
isImporting: PropTypes.bool.isRequired,
|
isImporting: PropTypes.bool.isRequired,
|
||||||
isLookingUpMovie: PropTypes.bool.isRequired,
|
isLookingUpMovie: PropTypes.bool.isRequired,
|
||||||
defaultMonitor: PropTypes.string.isRequired,
|
defaultMonitor: PropTypes.string.isRequired,
|
||||||
defaultQualityProfileId: PropTypes.number,
|
defaultQualityProfileIds: PropTypes.arrayOf(PropTypes.number),
|
||||||
defaultMinimumAvailability: PropTypes.string,
|
defaultMinimumAvailability: PropTypes.string,
|
||||||
isMonitorMixed: PropTypes.bool.isRequired,
|
isMonitorMixed: PropTypes.bool.isRequired,
|
||||||
isQualityProfileIdMixed: PropTypes.bool.isRequired,
|
isQualityProfileIdsMixed: PropTypes.bool.isRequired,
|
||||||
isMinimumAvailabilityMixed: PropTypes.bool.isRequired,
|
isMinimumAvailabilityMixed: PropTypes.bool.isRequired,
|
||||||
hasUnsearchedItems: PropTypes.bool.isRequired,
|
hasUnsearchedItems: PropTypes.bool.isRequired,
|
||||||
importError: PropTypes.object,
|
importError: PropTypes.object,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function createMapStateToProps() {
|
|||||||
(addMovie, importMovie, selectedIds) => {
|
(addMovie, importMovie, selectedIds) => {
|
||||||
const {
|
const {
|
||||||
monitor: defaultMonitor,
|
monitor: defaultMonitor,
|
||||||
qualityProfileId: defaultQualityProfileId,
|
qualityProfileIds: defaultQualityProfileIds,
|
||||||
minimumAvailability: defaultMinimumAvailability
|
minimumAvailability: defaultMinimumAvailability
|
||||||
} = addMovie.defaults;
|
} = addMovie.defaults;
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ function createMapStateToProps() {
|
|||||||
} = importMovie;
|
} = importMovie;
|
||||||
|
|
||||||
const isMonitorMixed = isMixed(items, selectedIds, defaultMonitor, 'monitor');
|
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 isMinimumAvailabilityMixed = isMixed(items, selectedIds, defaultMinimumAvailability, 'minimumAvailability');
|
||||||
const hasUnsearchedItems = !isLookingUpMovie && items.some((item) => !item.isPopulated);
|
const hasUnsearchedItems = !isLookingUpMovie && items.some((item) => !item.isPopulated);
|
||||||
|
|
||||||
@@ -39,10 +39,10 @@ function createMapStateToProps() {
|
|||||||
isLookingUpMovie,
|
isLookingUpMovie,
|
||||||
isImporting,
|
isImporting,
|
||||||
defaultMonitor,
|
defaultMonitor,
|
||||||
defaultQualityProfileId,
|
defaultQualityProfileIds,
|
||||||
defaultMinimumAvailability,
|
defaultMinimumAvailability,
|
||||||
isMonitorMixed,
|
isMonitorMixed,
|
||||||
isQualityProfileIdMixed,
|
isQualityProfileIdsMixed,
|
||||||
isMinimumAvailabilityMixed,
|
isMinimumAvailabilityMixed,
|
||||||
importError,
|
importError,
|
||||||
hasUnsearchedItems
|
hasUnsearchedItems
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'detailsIcon': string;
|
|
||||||
'folder': string;
|
|
||||||
'minimumAvailability': string;
|
|
||||||
'monitor': string;
|
|
||||||
'movie': string;
|
|
||||||
'qualityProfile': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'folder': string;
|
|
||||||
'minimumAvailability': string;
|
|
||||||
'monitor': string;
|
|
||||||
'movie': string;
|
|
||||||
'qualityProfile': string;
|
|
||||||
'selectInput': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -10,9 +10,8 @@ import styles from './ImportMovieRow.css';
|
|||||||
function ImportMovieRow(props) {
|
function ImportMovieRow(props) {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
relativePath,
|
|
||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileIds,
|
||||||
minimumAvailability,
|
minimumAvailability,
|
||||||
selectedMovie,
|
selectedMovie,
|
||||||
isExistingMovie,
|
isExistingMovie,
|
||||||
@@ -32,7 +31,7 @@ function ImportMovieRow(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<VirtualTableRowCell className={styles.folder}>
|
<VirtualTableRowCell className={styles.folder}>
|
||||||
{relativePath}
|
{id}
|
||||||
</VirtualTableRowCell>
|
</VirtualTableRowCell>
|
||||||
|
|
||||||
<VirtualTableRowCell className={styles.movie}>
|
<VirtualTableRowCell className={styles.movie}>
|
||||||
@@ -63,8 +62,8 @@ function ImportMovieRow(props) {
|
|||||||
<VirtualTableRowCell className={styles.qualityProfile}>
|
<VirtualTableRowCell className={styles.qualityProfile}>
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||||
name="qualityProfileId"
|
name="qualityProfileIds"
|
||||||
value={qualityProfileId}
|
value={qualityProfileIds}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
/>
|
/>
|
||||||
</VirtualTableRowCell>
|
</VirtualTableRowCell>
|
||||||
@@ -74,9 +73,8 @@ function ImportMovieRow(props) {
|
|||||||
|
|
||||||
ImportMovieRow.propTypes = {
|
ImportMovieRow.propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
relativePath: PropTypes.string.isRequired,
|
|
||||||
monitor: PropTypes.string.isRequired,
|
monitor: PropTypes.string.isRequired,
|
||||||
qualityProfileId: PropTypes.number.isRequired,
|
qualityProfileIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
minimumAvailability: PropTypes.string.isRequired,
|
minimumAvailability: PropTypes.string.isRequired,
|
||||||
selectedMovie: PropTypes.object,
|
selectedMovie: PropTypes.object,
|
||||||
isExistingMovie: PropTypes.bool.isRequired,
|
isExistingMovie: PropTypes.bool.isRequired,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class ImportMovieTable extends Component {
|
|||||||
const {
|
const {
|
||||||
unmappedFolders,
|
unmappedFolders,
|
||||||
defaultMonitor,
|
defaultMonitor,
|
||||||
defaultQualityProfileId,
|
defaultQualityProfileIds,
|
||||||
defaultMinimumAvailability,
|
defaultMinimumAvailability,
|
||||||
onMovieLookup,
|
onMovieLookup,
|
||||||
onSetImportMovieValue
|
onSetImportMovieValue
|
||||||
@@ -23,14 +23,14 @@ class ImportMovieTable extends Component {
|
|||||||
|
|
||||||
const values = {
|
const values = {
|
||||||
monitor: defaultMonitor,
|
monitor: defaultMonitor,
|
||||||
qualityProfileId: defaultQualityProfileId,
|
qualityProfileIds: defaultQualityProfileIds,
|
||||||
minimumAvailability: defaultMinimumAvailability
|
minimumAvailability: defaultMinimumAvailability
|
||||||
};
|
};
|
||||||
|
|
||||||
unmappedFolders.forEach((unmappedFolder) => {
|
unmappedFolders.forEach((unmappedFolder) => {
|
||||||
const id = unmappedFolder.name;
|
const id = unmappedFolder.name;
|
||||||
|
|
||||||
onMovieLookup(id, unmappedFolder.path, unmappedFolder.relativePath);
|
onMovieLookup(id, unmappedFolder.path);
|
||||||
|
|
||||||
onSetImportMovieValue({
|
onSetImportMovieValue({
|
||||||
id,
|
id,
|
||||||
@@ -167,7 +167,7 @@ ImportMovieTable.propTypes = {
|
|||||||
items: PropTypes.arrayOf(PropTypes.object),
|
items: PropTypes.arrayOf(PropTypes.object),
|
||||||
unmappedFolders: PropTypes.arrayOf(PropTypes.object),
|
unmappedFolders: PropTypes.arrayOf(PropTypes.object),
|
||||||
defaultMonitor: PropTypes.string.isRequired,
|
defaultMonitor: PropTypes.string.isRequired,
|
||||||
defaultQualityProfileId: PropTypes.number,
|
defaultQualityProfileIds: PropTypes.arrayOf(PropTypes.number),
|
||||||
defaultMinimumAvailability: PropTypes.string,
|
defaultMinimumAvailability: PropTypes.string,
|
||||||
allSelected: PropTypes.bool.isRequired,
|
allSelected: PropTypes.bool.isRequired,
|
||||||
allUnselected: PropTypes.bool.isRequired,
|
allUnselected: PropTypes.bool.isRequired,
|
||||||
|
|||||||