mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-18 21:34:28 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a556a89aa |
+2
-1
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
# 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/Readarr text eol=lf
|
distribution/debian/* text eol=lf
|
||||||
|
macOS/Readarr 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
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ contact_links:
|
|||||||
- name: Support via Discord
|
- name: Support via Discord
|
||||||
url: https://readarr.com/discord
|
url: https://readarr.com/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/Readarr
|
||||||
|
about: Discuss and search thru support topics.
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ jobs:
|
|||||||
issue-comment: >
|
issue-comment: >
|
||||||
:wave: @{issue-author}, we use the issue tracker exclusively
|
:wave: @{issue-author}, we use the issue tracker exclusively
|
||||||
for bug reports and feature requests. However, this issue appears
|
for bug reports and feature requests. However, this issue appears
|
||||||
to be a support request. Please hop over onto our [Discord](https://readarr.com/discord).
|
to be a support request. Please hop over onto our [Discord](https://readarr.com/discord)
|
||||||
|
or [Subreddit](https://reddit.com/r/readarr)
|
||||||
close-issue: true
|
close-issue: true
|
||||||
lock-issue: false
|
lock-issue: false
|
||||||
- uses: dessant/support-requests@v3
|
- uses: dessant/support-requests@v3
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ Note that only one type of a given book is supported. If you want both an audiob
|
|||||||
|
|
||||||
[](https://wiki.servarr.com/readarr)
|
[](https://wiki.servarr.com/readarr)
|
||||||
[](https://readarr.com/discord)
|
[](https://readarr.com/discord)
|
||||||
|
[](https://www.reddit.com/r/readarr)
|
||||||
|
|
||||||
Note: GitHub Issues are for Bugs and Feature Requests Only
|
Note: GitHub Issues are for Bugs and Feature Requests Only
|
||||||
|
|
||||||
|
|||||||
+141
-294
@@ -9,13 +9,13 @@ variables:
|
|||||||
testsFolder: './_tests'
|
testsFolder: './_tests'
|
||||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||||
majorVersion: '0.3.14'
|
majorVersion: '0.3.1'
|
||||||
minorVersion: $[counter('minorVersion', 1)]
|
minorVersion: $[counter('minorVersion', 1)]
|
||||||
readarrVersion: '$(majorVersion).$(minorVersion)'
|
readarrVersion: '$(majorVersion).$(minorVersion)'
|
||||||
buildName: '$(Build.SourceBranchName).$(readarrVersion)'
|
buildName: '$(Build.SourceBranchName).$(readarrVersion)'
|
||||||
sentryOrg: 'servarr'
|
sentryOrg: 'servarr'
|
||||||
sentryUrl: 'https://sentry.servarr.com'
|
sentryUrl: 'https://sentry.servarr.com'
|
||||||
dotnetVersion: '6.0.417'
|
dotnetVersion: '6.0.408'
|
||||||
nodeVersion: '16.X'
|
nodeVersion: '16.X'
|
||||||
innoVersion: '6.2.0'
|
innoVersion: '6.2.0'
|
||||||
windowsImage: 'windows-2022'
|
windowsImage: 'windows-2022'
|
||||||
@@ -27,10 +27,6 @@ trigger:
|
|||||||
include:
|
include:
|
||||||
- develop
|
- develop
|
||||||
- master
|
- master
|
||||||
paths:
|
|
||||||
exclude:
|
|
||||||
- .github
|
|
||||||
- src/Readarr.Api.*/openapi.json
|
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
branches:
|
branches:
|
||||||
@@ -38,37 +34,82 @@ pr:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
exclude:
|
exclude:
|
||||||
- .github
|
|
||||||
- src/NzbDrone.Core/Localization/Core
|
- src/NzbDrone.Core/Localization/Core
|
||||||
- src/Readarr.Api.*/openapi.json
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- stage: Setup
|
|
||||||
displayName: Setup
|
- stage: Build_Backend_Windows
|
||||||
|
displayName: Build Backend
|
||||||
|
dependsOn: []
|
||||||
jobs:
|
jobs:
|
||||||
- job:
|
- job: Backend
|
||||||
displayName: Build Variables
|
strategy:
|
||||||
|
matrix:
|
||||||
|
Windows:
|
||||||
|
osName: 'Windows'
|
||||||
|
imageName: ${{ variables.windowsImage }}
|
||||||
|
enableAnalysis: 'false'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.linuxImage }}
|
vmImage: $(imageName)
|
||||||
|
variables:
|
||||||
|
# Disable stylecop here - linting errors get caught by the analyze task
|
||||||
|
EnableAnalyzers: $(enableAnalysis)
|
||||||
steps:
|
steps:
|
||||||
# Set the build name properly. The 'name' property won't recursively expand so hack here:
|
# Set the build name properly. The 'name' property won't recursively expand so hack here:
|
||||||
- bash: echo "##vso[build.updatebuildnumber]$READARRVERSION"
|
- bash: echo "##vso[build.updatebuildnumber]$READARRVERSION"
|
||||||
displayName: Set Build Name
|
displayName: Set Build Name
|
||||||
|
- checkout: self
|
||||||
|
submodules: true
|
||||||
|
fetchDepth: 1
|
||||||
|
- task: UseDotNet@2
|
||||||
|
displayName: 'Install .net core'
|
||||||
|
inputs:
|
||||||
|
version: $(dotnetVersion)
|
||||||
- bash: |
|
- bash: |
|
||||||
if [[ $BUILD_REASON == "PullRequest" ]]; then
|
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}"
|
||||||
git diff origin/develop...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
|
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
||||||
echo $? > not_backend_update
|
|
||||||
else
|
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
||||||
echo 0 > not_backend_update
|
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
||||||
fi
|
fi
|
||||||
cat not_backend_update
|
displayName: Extra Platform Support
|
||||||
displayName: Check for Backend File Changes
|
- task: Cache@2
|
||||||
- publish: not_backend_update
|
inputs:
|
||||||
artifact: not_backend_update
|
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
||||||
displayName: Publish update type
|
path: $(nugetCacheFolder)
|
||||||
- stage: Build_Backend
|
displayName: Cache NuGet packages
|
||||||
displayName: Build Backend
|
- bash: ./build.sh --backend --enable-bsd
|
||||||
dependsOn: Setup
|
displayName: Build Readarr Backend
|
||||||
|
env:
|
||||||
|
NUGET_PACKAGES: $(nugetCacheFolder)
|
||||||
|
- powershell: Get-ChildItem _output\net6.0*,_output\*.Update\* -Recurse | Where { $_.Fullname -notlike "*\publish\*" -and $_.attributes -notlike "*directory*" } | Remove-Item
|
||||||
|
displayName: Clean up intermediate output
|
||||||
|
- publish: $(outputFolder)
|
||||||
|
artifact: '$(osName)Backend'
|
||||||
|
displayName: Publish Backend
|
||||||
|
- publish: '$(testsFolder)/net6.0/win-x64/publish'
|
||||||
|
artifact: win-x64-tests
|
||||||
|
displayName: Publish win-x64 Test Package
|
||||||
|
- publish: '$(testsFolder)/net6.0/linux-x64/publish'
|
||||||
|
artifact: linux-x64-tests
|
||||||
|
displayName: Publish linux-x64 Test Package
|
||||||
|
- publish: '$(testsFolder)/net6.0/linux-x86/publish'
|
||||||
|
artifact: linux-x86-tests
|
||||||
|
displayName: Publish linux-x86 Test Package
|
||||||
|
- publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
|
||||||
|
artifact: linux-musl-x64-tests
|
||||||
|
displayName: Publish linux-musl-x64 Test Package
|
||||||
|
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
|
||||||
|
artifact: freebsd-x64-tests
|
||||||
|
displayName: Publish freebsd-x64 Test Package
|
||||||
|
- publish: '$(testsFolder)/net6.0/osx-x64/publish'
|
||||||
|
artifact: osx-x64-tests
|
||||||
|
displayName: Publish osx-x64 Test Package
|
||||||
|
|
||||||
|
- stage: Build_Backend_Other
|
||||||
|
displayName: Build Backend (Other OS)
|
||||||
|
dependsOn: []
|
||||||
jobs:
|
jobs:
|
||||||
- job: Backend
|
- job: Backend
|
||||||
strategy:
|
strategy:
|
||||||
@@ -81,10 +122,6 @@ stages:
|
|||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
imageName: ${{ variables.macImage }}
|
imageName: ${{ variables.macImage }}
|
||||||
enableAnalysis: 'false'
|
enableAnalysis: 'false'
|
||||||
Windows:
|
|
||||||
osName: 'Windows'
|
|
||||||
imageName: ${{ variables.windowsImage }}
|
|
||||||
enableAnalysis: 'false'
|
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
@@ -100,17 +137,22 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
version: $(dotnetVersion)
|
version: $(dotnetVersion)
|
||||||
- bash: |
|
- bash: |
|
||||||
BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
|
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}"
|
||||||
echo $BUNDLEDVERSIONS
|
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
||||||
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
|
||||||
echo "Extra platforms already enabled"
|
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
||||||
else
|
|
||||||
echo "Enabling extra platform support"
|
|
||||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
||||||
fi
|
fi
|
||||||
displayName: Enable Extra Platform Support
|
displayName: Extra Platform Support
|
||||||
|
- task: Cache@2
|
||||||
|
inputs:
|
||||||
|
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
||||||
|
path: $(nugetCacheFolder)
|
||||||
|
displayName: Cache NuGet packages
|
||||||
- bash: ./build.sh --backend --enable-extra-platforms
|
- bash: ./build.sh --backend --enable-extra-platforms
|
||||||
displayName: Build Readarr Backend
|
displayName: Build Readarr Backend
|
||||||
|
env:
|
||||||
|
NUGET_PACKAGES: $(nugetCacheFolder)
|
||||||
- bash: |
|
- bash: |
|
||||||
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
||||||
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
||||||
@@ -118,38 +160,10 @@ stages:
|
|||||||
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
||||||
displayName: Clean up intermediate output
|
displayName: Clean up intermediate output
|
||||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||||
- publish: $(outputFolder)
|
|
||||||
artifact: '$(osName)Backend'
|
|
||||||
displayName: Publish Backend
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
|
||||||
- publish: '$(testsFolder)/net6.0/win-x64/publish'
|
|
||||||
artifact: win-x64-tests
|
|
||||||
displayName: Publish win-x64 Test Package
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
|
||||||
- publish: '$(testsFolder)/net6.0/linux-x64/publish'
|
|
||||||
artifact: linux-x64-tests
|
|
||||||
displayName: Publish linux-x64 Test Package
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
|
||||||
- publish: '$(testsFolder)/net6.0/linux-x86/publish'
|
|
||||||
artifact: linux-x86-tests
|
|
||||||
displayName: Publish linux-x86 Test Package
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
|
||||||
- publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
|
|
||||||
artifact: linux-musl-x64-tests
|
|
||||||
displayName: Publish linux-musl-x64 Test Package
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
|
||||||
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
|
|
||||||
artifact: freebsd-x64-tests
|
|
||||||
displayName: Publish freebsd-x64 Test Package
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
|
||||||
- publish: '$(testsFolder)/net6.0/osx-x64/publish'
|
|
||||||
artifact: osx-x64-tests
|
|
||||||
displayName: Publish osx-x64 Test Package
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
|
||||||
|
|
||||||
- stage: Build_Frontend
|
- stage: Build_Frontend
|
||||||
displayName: Frontend
|
displayName: Frontend
|
||||||
dependsOn: Setup
|
dependsOn: []
|
||||||
jobs:
|
jobs:
|
||||||
- job: Build
|
- job: Build
|
||||||
strategy:
|
strategy:
|
||||||
@@ -178,6 +192,7 @@ stages:
|
|||||||
key: 'yarn | "$(osName)" | yarn.lock'
|
key: 'yarn | "$(osName)" | yarn.lock'
|
||||||
restoreKeys: |
|
restoreKeys: |
|
||||||
yarn | "$(osName)"
|
yarn | "$(osName)"
|
||||||
|
yarn
|
||||||
path: $(yarnCacheFolder)
|
path: $(yarnCacheFolder)
|
||||||
displayName: Cache Yarn packages
|
displayName: Cache Yarn packages
|
||||||
- bash: ./build.sh --frontend
|
- bash: ./build.sh --frontend
|
||||||
@@ -189,10 +204,10 @@ stages:
|
|||||||
artifact: '$(osName)Frontend'
|
artifact: '$(osName)Frontend'
|
||||||
displayName: Publish Frontend
|
displayName: Publish Frontend
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
|
||||||
- stage: Installer
|
- stage: Installer
|
||||||
dependsOn:
|
dependsOn:
|
||||||
- Build_Backend
|
- Build_Backend_Windows
|
||||||
- Build_Frontend
|
- Build_Frontend
|
||||||
jobs:
|
jobs:
|
||||||
- job: Windows_Installer
|
- job: Windows_Installer
|
||||||
@@ -216,8 +231,8 @@ stages:
|
|||||||
displayName: Fetch Frontend
|
displayName: Fetch Frontend
|
||||||
- bash: |
|
- bash: |
|
||||||
./build.sh --packages --installer
|
./build.sh --packages --installer
|
||||||
cp distribution/windows/setup/output/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe
|
cp setup/output/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||||
cp distribution/windows/setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x86-installer.exe
|
cp setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x86-installer.exe
|
||||||
displayName: Create Installers
|
displayName: Create Installers
|
||||||
- publish: $(Build.ArtifactStagingDirectory)
|
- publish: $(Build.ArtifactStagingDirectory)
|
||||||
artifact: 'WindowsInstaller'
|
artifact: 'WindowsInstaller'
|
||||||
@@ -225,7 +240,7 @@ stages:
|
|||||||
|
|
||||||
- stage: Packages
|
- stage: Packages
|
||||||
dependsOn:
|
dependsOn:
|
||||||
- Build_Backend
|
- Build_Backend_Windows
|
||||||
- Build_Frontend
|
- Build_Frontend
|
||||||
jobs:
|
jobs:
|
||||||
- job: Other_Packages
|
- job: Other_Packages
|
||||||
@@ -391,29 +406,14 @@ stages:
|
|||||||
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
||||||
SENTRY_ORG: $(sentryOrg)
|
SENTRY_ORG: $(sentryOrg)
|
||||||
SENTRY_URL: $(sentryUrl)
|
SENTRY_URL: $(sentryUrl)
|
||||||
|
|
||||||
- stage: Unit_Test
|
- stage: Unit_Test
|
||||||
displayName: Unit Tests
|
displayName: Unit Tests
|
||||||
dependsOn: Build_Backend
|
dependsOn: Build_Backend_Windows
|
||||||
|
condition: succeeded()
|
||||||
jobs:
|
jobs:
|
||||||
- job: Prepare
|
|
||||||
pool:
|
|
||||||
vmImage: ${{ variables.linuxImage }}
|
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: 'not_backend_update'
|
|
||||||
targetPath: '.'
|
|
||||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
|
||||||
name: setVar
|
|
||||||
|
|
||||||
- job: Unit
|
- job: Unit
|
||||||
displayName: Unit Native
|
displayName: Unit Native
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
workspace:
|
workspace:
|
||||||
clean: all
|
clean: all
|
||||||
|
|
||||||
@@ -479,8 +479,6 @@ stages:
|
|||||||
|
|
||||||
- job: Unit_Docker
|
- job: Unit_Docker
|
||||||
displayName: Unit Docker
|
displayName: Unit Docker
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
alpine:
|
alpine:
|
||||||
@@ -494,11 +492,11 @@ stages:
|
|||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.linuxImage }}
|
vmImage: ${{ variables.linuxImage }}
|
||||||
|
|
||||||
container: $[ variables['containerImage'] ]
|
container: $[ variables['containerImage'] ]
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
timeoutInMinutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .NET'
|
displayName: 'Install .NET'
|
||||||
@@ -532,14 +530,12 @@ stages:
|
|||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
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
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
variables:
|
variables:
|
||||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||||
artifactName: linux-x64-tests
|
artifactName: LinuxCoreTests
|
||||||
Readarr__Postgres__Host: 'localhost'
|
Readarr__Postgres__Host: 'localhost'
|
||||||
Readarr__Postgres__Port: '5432'
|
Readarr__Postgres__Port: '5432'
|
||||||
Readarr__Postgres__User: 'readarr'
|
Readarr__Postgres__User: 'readarr'
|
||||||
@@ -549,7 +545,7 @@ stages:
|
|||||||
vmImage: ${{ variables.linuxImage }}
|
vmImage: ${{ variables.linuxImage }}
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
timeoutInMinutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core'
|
||||||
@@ -560,7 +556,7 @@ stages:
|
|||||||
displayName: Download Test Artifact
|
displayName: Download Test Artifact
|
||||||
inputs:
|
inputs:
|
||||||
buildType: 'current'
|
buildType: 'current'
|
||||||
artifactName: $(artifactName)
|
artifactName: 'linux-x64-Tests'
|
||||||
targetPath: $(testsFolder)
|
targetPath: $(testsFolder)
|
||||||
- bash: find ${TESTSFOLDER} -name "Readarr.Test.Dummy" -exec chmod a+x {} \;
|
- bash: find ${TESTSFOLDER} -name "Readarr.Test.Dummy" -exec chmod a+x {} \;
|
||||||
displayName: Make Test Dummy Executable
|
displayName: Make Test Dummy Executable
|
||||||
@@ -583,84 +579,15 @@ 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: 'Readarr.*.linux-core-x64.tar.gz'
|
|
||||||
artifactName: linux-x64-tests
|
|
||||||
Readarr__Postgres__Host: 'localhost'
|
|
||||||
Readarr__Postgres__Port: '5432'
|
|
||||||
Readarr__Postgres__User: 'readarr'
|
|
||||||
Readarr__Postgres__Password: 'readarr'
|
|
||||||
|
|
||||||
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: find ${TESTSFOLDER} -name "Readarr.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=readarr \
|
|
||||||
-e POSTGRES_USER=readarr \
|
|
||||||
-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
|
||||||
displayName: Integration
|
displayName: Integration
|
||||||
dependsOn: Packages
|
dependsOn: Packages
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Prepare
|
|
||||||
pool:
|
|
||||||
vmImage: ${{ variables.linuxImage }}
|
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: 'not_backend_update'
|
|
||||||
targetPath: '.'
|
|
||||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
|
||||||
name: setVar
|
|
||||||
|
|
||||||
- job: Integration_Native
|
- job: Integration_Native
|
||||||
displayName: Integration Native
|
displayName: Integration Native
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
MacCore:
|
MacCore:
|
||||||
@@ -681,7 +608,7 @@ stages:
|
|||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core'
|
||||||
@@ -703,7 +630,7 @@ stages:
|
|||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- task: ExtractFiles@1
|
- task: ExtractFiles@1
|
||||||
inputs:
|
inputs:
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||||
displayName: Extract Package
|
displayName: Extract Package
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -722,10 +649,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
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
variables:
|
variables:
|
||||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||||
Readarr__Postgres__Host: 'localhost'
|
Readarr__Postgres__Host: 'localhost'
|
||||||
@@ -757,7 +682,7 @@ stages:
|
|||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- task: ExtractFiles@1
|
- task: ExtractFiles@1
|
||||||
inputs:
|
inputs:
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||||
displayName: Extract Package
|
displayName: Extract Package
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -780,77 +705,12 @@ 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: 'Readarr.*.linux-core-x64.tar.gz'
|
|
||||||
Readarr__Postgres__Host: 'localhost'
|
|
||||||
Readarr__Postgres__Port: '5432'
|
|
||||||
Readarr__Postgres__User: 'readarr'
|
|
||||||
Readarr__Postgres__Password: 'readarr'
|
|
||||||
|
|
||||||
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/Readarr/. ./bin/
|
|
||||||
displayName: Move Package Contents
|
|
||||||
- bash: |
|
|
||||||
docker run -d --name=postgres15 \
|
|
||||||
-e POSTGRES_PASSWORD=readarr \
|
|
||||||
-e POSTGRES_USER=readarr \
|
|
||||||
-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
|
||||||
|
|
||||||
- job: Integration_FreeBSD
|
- job: Integration_FreeBSD
|
||||||
displayName: Integration Native FreeBSD
|
displayName: Integration Native FreeBSD
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
workspace:
|
workspace:
|
||||||
clean: all
|
clean: all
|
||||||
variables:
|
variables:
|
||||||
@@ -895,8 +755,6 @@ stages:
|
|||||||
|
|
||||||
- job: Integration_Docker
|
- job: Integration_Docker
|
||||||
displayName: Integration Docker
|
displayName: Integration Docker
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
alpine:
|
alpine:
|
||||||
@@ -915,7 +773,7 @@ stages:
|
|||||||
container: $[ variables['containerImage'] ]
|
container: $[ variables['containerImage'] ]
|
||||||
|
|
||||||
timeoutInMinutes: 15
|
timeoutInMinutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .NET'
|
displayName: 'Install .NET'
|
||||||
@@ -943,7 +801,7 @@ stages:
|
|||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- task: ExtractFiles@1
|
- task: ExtractFiles@1
|
||||||
inputs:
|
inputs:
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||||
displayName: Extract Package
|
displayName: Extract Package
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -965,7 +823,7 @@ stages:
|
|||||||
- stage: Automation
|
- stage: Automation
|
||||||
displayName: Automation
|
displayName: Automation
|
||||||
dependsOn: Packages
|
dependsOn: Packages
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Automation
|
- job: Automation
|
||||||
strategy:
|
strategy:
|
||||||
@@ -975,23 +833,20 @@ stages:
|
|||||||
artifactName: 'linux-x64'
|
artifactName: 'linux-x64'
|
||||||
imageName: ${{ variables.linuxImage }}
|
imageName: ${{ variables.linuxImage }}
|
||||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||||
failBuild: true
|
|
||||||
Mac:
|
Mac:
|
||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
artifactName: 'osx-x64'
|
artifactName: 'osx-x64'
|
||||||
imageName: ${{ variables.macImage }}
|
imageName: ${{ variables.macImage }}
|
||||||
pattern: 'Readarr.*.osx-core-x64.tar.gz'
|
pattern: 'Readarr.*.osx-core-x64.tar.gz'
|
||||||
failBuild: true
|
|
||||||
Windows:
|
Windows:
|
||||||
osName: 'Windows'
|
osName: 'Windows'
|
||||||
artifactName: 'win-x64'
|
artifactName: 'win-x64'
|
||||||
imageName: ${{ variables.windowsImage }}
|
imageName: ${{ variables.windowsImage }}
|
||||||
pattern: 'Readarr.*.windows-core-x64.zip'
|
pattern: 'Readarr.*.windows-core-x64.zip'
|
||||||
failBuild: true
|
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core'
|
||||||
@@ -1013,7 +868,7 @@ stages:
|
|||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- task: ExtractFiles@1
|
- task: ExtractFiles@1
|
||||||
inputs:
|
inputs:
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||||
displayName: Extract Package
|
displayName: Extract Package
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -1033,35 +888,20 @@ stages:
|
|||||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
|
TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
|
||||||
- publish: $(Build.ArtifactStagingDirectory)/screenshots
|
- publish: $(Build.ArtifactStagingDirectory)/screenshots
|
||||||
artifact: '$(osName)AutomationScreenshots'
|
artifact: '$(osName)AutomationScreenshots'
|
||||||
displayName: Publish Screenshot Bundle
|
|
||||||
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
|
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
|
||||||
|
displayName: Publish Screenshot Bundle
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: '$(osName) Automation Tests'
|
testRunTitle: '$(osName) Automation Tests'
|
||||||
failTaskOnFailedTests: $(failBuild)
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
- stage: Analyze
|
- stage: Analyze
|
||||||
dependsOn:
|
dependsOn: []
|
||||||
- Setup
|
|
||||||
displayName: Analyze
|
displayName: Analyze
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Prepare
|
|
||||||
pool:
|
|
||||||
vmImage: ${{ variables.linuxImage }}
|
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: 'not_backend_update'
|
|
||||||
targetPath: '.'
|
|
||||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
|
||||||
name: setVar
|
|
||||||
|
|
||||||
- job: Lint_Frontend
|
- job: Lint_Frontend
|
||||||
displayName: Lint Frontend
|
displayName: Lint Frontend
|
||||||
strategy:
|
strategy:
|
||||||
@@ -1087,6 +927,7 @@ stages:
|
|||||||
key: 'yarn | "$(osName)" | yarn.lock'
|
key: 'yarn | "$(osName)" | yarn.lock'
|
||||||
restoreKeys: |
|
restoreKeys: |
|
||||||
yarn | "$(osName)"
|
yarn | "$(osName)"
|
||||||
|
yarn
|
||||||
path: $(yarnCacheFolder)
|
path: $(yarnCacheFolder)
|
||||||
displayName: Cache Yarn packages
|
displayName: Cache Yarn packages
|
||||||
- bash: ./build.sh --lint
|
- bash: ./build.sh --lint
|
||||||
@@ -1115,16 +956,11 @@ stages:
|
|||||||
cliProjectVersion: '$(readarrVersion)'
|
cliProjectVersion: '$(readarrVersion)'
|
||||||
cliSources: './frontend'
|
cliSources: './frontend'
|
||||||
- task: SonarCloudAnalyze@1
|
- task: SonarCloudAnalyze@1
|
||||||
|
|
||||||
- job: Api_Docs
|
- job: Api_Docs
|
||||||
displayName: API Docs
|
displayName: API Docs
|
||||||
dependsOn: Prepare
|
|
||||||
condition: |
|
condition: |
|
||||||
and
|
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
|
||||||
(
|
|
||||||
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
|
|
||||||
and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
)
|
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.windowsImage }}
|
vmImage: ${{ variables.windowsImage }}
|
||||||
@@ -1137,7 +973,7 @@ stages:
|
|||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
persistCredentials: true
|
persistCredentials: true
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
- bash: ./docs.sh Windows
|
- bash: ./docs.sh Windows
|
||||||
displayName: Create openapi.json
|
displayName: Create openapi.json
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -1145,9 +981,10 @@ stages:
|
|||||||
git config --global user.name "Servarr"
|
git config --global user.name "Servarr"
|
||||||
git checkout -b api-docs
|
git checkout -b api-docs
|
||||||
git add .
|
git add .
|
||||||
if git status | grep -q modified
|
git status
|
||||||
|
if git status | grep modified
|
||||||
then
|
then
|
||||||
git commit -am 'Automated API Docs update'
|
git commit -am 'Automated API Docs update [skip ci]'
|
||||||
git push -f --set-upstream origin api-docs
|
git push -f --set-upstream origin api-docs
|
||||||
curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/readarr/readarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
|
curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/readarr/readarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
|
||||||
else
|
else
|
||||||
@@ -1171,25 +1008,33 @@ stages:
|
|||||||
|
|
||||||
- job: Analyze_Backend
|
- job: Analyze_Backend
|
||||||
displayName: Backend
|
displayName: Backend
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
disable.coverage.autogenerate: 'true'
|
disable.coverage.autogenerate: 'true'
|
||||||
EnableAnalyzers: 'false'
|
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.windowsImage }}
|
vmImage: ${{ variables.linuxImage }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core 2.1'
|
||||||
|
inputs:
|
||||||
|
version: 2.1.815
|
||||||
|
- task: UseDotNet@2
|
||||||
|
displayName: 'Install .net core 3.1'
|
||||||
|
inputs:
|
||||||
|
version: 3.1.413
|
||||||
|
- task: UseDotNet@2
|
||||||
|
displayName: 'Install .net core 5.0'
|
||||||
inputs:
|
inputs:
|
||||||
version: $(dotnetVersion)
|
version: $(dotnetVersion)
|
||||||
- checkout: self # Need history for Sonar analysis
|
- checkout: self # Need history for Sonar analysis
|
||||||
submodules: true
|
submodules: true
|
||||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
- task: Cache@2
|
||||||
displayName: Enable Windows Test Service
|
inputs:
|
||||||
|
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
||||||
|
path: $(nugetCacheFolder)
|
||||||
|
displayName: Cache NuGet packages
|
||||||
|
|
||||||
- task: SonarCloudPrepare@1
|
- task: SonarCloudPrepare@1
|
||||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||||
inputs:
|
inputs:
|
||||||
@@ -1200,14 +1045,16 @@ stages:
|
|||||||
projectName: 'Readarr'
|
projectName: 'Readarr'
|
||||||
projectVersion: '$(readarrVersion)'
|
projectVersion: '$(readarrVersion)'
|
||||||
extraProperties: |
|
extraProperties: |
|
||||||
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
|
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,./src/Libraries/**
|
||||||
sonar.coverage.exclusions=**/Readarr.Api.V1/**/*
|
sonar.coverage.exclusions=**/Readarr.Api.V1/**/*
|
||||||
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: |
|
||||||
./build.sh --backend -f net6.0 -r win-x64
|
./build.sh --backend -f net6.0 -r linux-x64
|
||||||
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
TEST_DIR=_tests/net6.0/linux-x64/publish/ ./test.sh Linux Unit Coverage
|
||||||
displayName: Coverage Unit Tests
|
displayName: Coverage Unit Tests
|
||||||
|
env:
|
||||||
|
NUGET_PACKAGES: $(nugetCacheFolder)
|
||||||
- task: SonarCloudAnalyze@1
|
- task: SonarCloudAnalyze@1
|
||||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||||
displayName: Publish SonarCloud Results
|
displayName: Publish SonarCloud Results
|
||||||
@@ -1230,6 +1077,7 @@ stages:
|
|||||||
- Unit_Test
|
- Unit_Test
|
||||||
- Integration
|
- Integration
|
||||||
- Automation
|
- Automation
|
||||||
|
- Build_Backend_Other
|
||||||
condition: eq(variables['system.pullrequest.isfork'], false)
|
condition: eq(variables['system.pullrequest.isfork'], false)
|
||||||
displayName: Build Status Report
|
displayName: Build Status Report
|
||||||
jobs:
|
jobs:
|
||||||
@@ -1253,4 +1101,3 @@ stages:
|
|||||||
DISCORDCHANNELID: $(discordChannelId)
|
DISCORDCHANNELID: $(discordChannelId)
|
||||||
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
||||||
DISCORDTHREADID: $(discordThreadId)
|
DISCORDTHREADID: $(discordThreadId)
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ UpdateVersionNumber()
|
|||||||
echo "Updating Version Info"
|
echo "Updating Version Info"
|
||||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$READARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$READARRVERSION<\/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>$READARRVERSION<\/string>/g" distribution/osx/Readarr.app/Contents/Info.plist
|
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" macOS/Readarr.app/Contents/Info.plist
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ PackageMacOSApp()
|
|||||||
|
|
||||||
rm -rf $folder
|
rm -rf $folder
|
||||||
mkdir -p $folder
|
mkdir -p $folder
|
||||||
cp -r distribution/osx/Readarr.app $folder
|
cp -r macOS/Readarr.app $folder
|
||||||
mkdir -p $folder/Readarr.app/Contents/MacOS
|
mkdir -p $folder/Readarr.app/Contents/MacOS
|
||||||
|
|
||||||
echo "Copying Binaries"
|
echo "Copying Binaries"
|
||||||
@@ -245,7 +245,7 @@ BuildInstaller()
|
|||||||
local framework="$1"
|
local framework="$1"
|
||||||
local runtime="$2"
|
local runtime="$2"
|
||||||
|
|
||||||
./_inno/ISCC.exe distribution/windows/setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
./_inno/ISCC.exe setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallInno()
|
InstallInno()
|
||||||
@@ -391,21 +391,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
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ module.exports = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
// 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: {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ module.exports = (env) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
entry: {
|
entry: {
|
||||||
index: 'index.ts'
|
index: 'index.js'
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -98,8 +98,7 @@ module.exports = (env) => {
|
|||||||
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({
|
||||||
|
|||||||
@@ -338,8 +338,4 @@ Queue.propTypes = {
|
|||||||
onRemoveSelectedPress: PropTypes.func.isRequired
|
onRemoveSelectedPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
Queue.defaultProps = {
|
|
||||||
count: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Queue;
|
export default Queue;
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import PageConnector from 'Components/Page/PageConnector';
|
|||||||
import ApplyTheme from './ApplyTheme';
|
import ApplyTheme from './ApplyTheme';
|
||||||
import AppRoutes from './AppRoutes';
|
import AppRoutes from './AppRoutes';
|
||||||
|
|
||||||
function App({ store, history }) {
|
function App({ store, history, hasTranslationsError }) {
|
||||||
return (
|
return (
|
||||||
<DocumentTitle title={window.Readarr.instanceName}>
|
<DocumentTitle title={window.Readarr.instanceName}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ConnectedRouter history={history}>
|
<ConnectedRouter history={history}>
|
||||||
<ApplyTheme>
|
<ApplyTheme>
|
||||||
<PageConnector>
|
<PageConnector hasTranslationsError={hasTranslationsError}>
|
||||||
<AppRoutes app={App} />
|
<AppRoutes app={App} />
|
||||||
</PageConnector>
|
</PageConnector>
|
||||||
</ApplyTheme>
|
</ApplyTheme>
|
||||||
@@ -25,7 +25,8 @@ function App({ store, history }) {
|
|||||||
|
|
||||||
App.propTypes = {
|
App.propTypes = {
|
||||||
store: PropTypes.object.isRequired,
|
store: PropTypes.object.isRequired,
|
||||||
history: PropTypes.object.isRequired
|
history: PropTypes.object.isRequired,
|
||||||
|
hasTranslationsError: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
.version {
|
.version {
|
||||||
margin: 0 3px;
|
margin: 0 3px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-family: var(--defaultFontFamily);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.maintenance {
|
.maintenance {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Button from 'Components/Link/Button';
|
import Button from 'Components/Link/Button';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
@@ -65,12 +64,12 @@ function AppUpdatedModalContent(props) {
|
|||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
{translate('AppUpdated', { appName: 'Readarr' })}
|
Readarr Updated
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<div>
|
||||||
<InlineMarkdown data={translate('AppUpdatedVersion', { appName: 'Readarr', version })} blockClassName={styles.version} />
|
Version <span className={styles.version}>{version}</span> of Readarr has been installed, in order to get the latest changes you'll need to reload Readarr.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -78,14 +77,16 @@ function AppUpdatedModalContent(props) {
|
|||||||
<div>
|
<div>
|
||||||
{
|
{
|
||||||
!update.changes &&
|
!update.changes &&
|
||||||
<div className={styles.maintenance}>{translate('MaintenanceRelease')}</div>
|
<div className={styles.maintenance}>
|
||||||
|
{translate('MaintenanceRelease')}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!!update.changes &&
|
!!update.changes &&
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.changes}>
|
<div className={styles.changes}>
|
||||||
{translate('WhatsNew')}
|
What's new?
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UpdateChanges
|
<UpdateChanges
|
||||||
@@ -112,14 +113,14 @@ function AppUpdatedModalContent(props) {
|
|||||||
<Button
|
<Button
|
||||||
onPress={onSeeChangesPress}
|
onPress={onSeeChangesPress}
|
||||||
>
|
>
|
||||||
{translate('RecentChanges')}
|
Recent Changes
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
kind={kinds.PRIMARY}
|
kind={kinds.PRIMARY}
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
{translate('Reload')}
|
Reload
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './ConnectionLostModal.css';
|
import styles from './ConnectionLostModal.css';
|
||||||
|
|
||||||
function ConnectionLostModal(props) {
|
function ConnectionLostModal(props) {
|
||||||
@@ -23,16 +22,16 @@ function ConnectionLostModal(props) {
|
|||||||
>
|
>
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
{translate('ConnectionLost')}
|
Connection Lost
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<div>
|
||||||
{translate('ConnectionLostToBackend', { appName: 'Readarr' })}
|
Readarr has lost its connection to the backend and will need to be reloaded to restore functionality.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.automatic}>
|
<div className={styles.automatic}>
|
||||||
{translate('ConnectionLostReconnect', { appName: 'Readarr' })}
|
Readarr will try to connect automatically, or you can click reload below.
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
@@ -40,7 +39,7 @@ function ConnectionLostModal(props) {
|
|||||||
kind={kinds.PRIMARY}
|
kind={kinds.PRIMARY}
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
{translate('Reload')}
|
Reload
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ function findImage(images, coverType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUrl(image, coverType, size) {
|
function getUrl(image, coverType, size) {
|
||||||
const imageUrl = image?.url;
|
if (image) {
|
||||||
|
// Remove protocol
|
||||||
|
let url = image.url;
|
||||||
|
|
||||||
if (imageUrl) {
|
url = url.replace(`${coverType}.jpg`, `${coverType}-${size}.jpg`);
|
||||||
return imageUrl.replace(`${coverType}.jpg`, `${coverType}-${size}.jpg`);
|
|
||||||
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,10 +44,6 @@
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filterIcon {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.authorNavigationButtons {
|
.authorNavigationButtons {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ interface CssExports {
|
|||||||
'authorUpButton': string;
|
'authorUpButton': string;
|
||||||
'contentContainer': string;
|
'contentContainer': string;
|
||||||
'errorMessage': string;
|
'errorMessage': string;
|
||||||
'filterIcon': string;
|
|
||||||
'innerContentBody': string;
|
'innerContentBody': string;
|
||||||
'metadataMessage': string;
|
'metadataMessage': string;
|
||||||
'selectedTab': string;
|
'selectedTab': string;
|
||||||
|
|||||||
@@ -239,14 +239,9 @@ class AuthorDetails extends Component {
|
|||||||
saveError,
|
saveError,
|
||||||
isDeleting,
|
isDeleting,
|
||||||
deleteError,
|
deleteError,
|
||||||
statistics = {}
|
statistics
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
|
||||||
bookFileCount = 0,
|
|
||||||
totalBookCount = 0
|
|
||||||
} = statistics;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOrganizeModalOpen,
|
isOrganizeModalOpen,
|
||||||
isRetagModalOpen,
|
isRetagModalOpen,
|
||||||
@@ -440,7 +435,7 @@ class AuthorDetails extends Component {
|
|||||||
className={styles.tab}
|
className={styles.tab}
|
||||||
selectedClassName={styles.selectedTab}
|
selectedClassName={styles.selectedTab}
|
||||||
>
|
>
|
||||||
{translate('BooksTotal', [totalBookCount])}
|
{translate('BooksTotal', [statistics.totalBookCount])}
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
@@ -468,7 +463,7 @@ class AuthorDetails extends Component {
|
|||||||
className={styles.tab}
|
className={styles.tab}
|
||||||
selectedClassName={styles.selectedTab}
|
selectedClassName={styles.selectedTab}
|
||||||
>
|
>
|
||||||
{translate('FilesTotal', [bookFileCount])}
|
{translate('FilesTotal', [statistics.bookFileCount])}
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -136,9 +136,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-weight: 300;
|
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
line-height: 30px;
|
line-height: 50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,12 @@ const defaultFontSize = parseInt(fonts.defaultFontSize);
|
|||||||
const lineHeight = parseFloat(fonts.lineHeight);
|
const lineHeight = parseFloat(fonts.lineHeight);
|
||||||
|
|
||||||
function getFanartUrl(images) {
|
function getFanartUrl(images) {
|
||||||
return images.find((x) => x.coverType === 'fanart')?.url;
|
const fanartImage = images.find((x) => x.coverType === 'fanart');
|
||||||
|
|
||||||
|
if (fanartImage) {
|
||||||
|
// Remove protocol
|
||||||
|
return fanartImage.url.replace(/^https?:/, '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuthorDetailsHeader extends Component {
|
class AuthorDetailsHeader extends Component {
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
.container {
|
|
||||||
border: 1px solid var(--borderColor);
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: var(--inputBackgroundColor);
|
|
||||||
|
|
||||||
&:last-of-type {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'container': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import AuthorHistoryContentConnector from 'Author/History/AuthorHistoryContentConnector';
|
import AuthorHistoryContentConnector from 'Author/History/AuthorHistoryContentConnector';
|
||||||
import AuthorHistoryTableContent from 'Author/History/AuthorHistoryTableContent';
|
import AuthorHistoryTableContent from 'Author/History/AuthorHistoryTableContent';
|
||||||
import styles from './AuthorHistoryTable.css';
|
|
||||||
|
|
||||||
function AuthorHistoryTable(props) {
|
function AuthorHistoryTable(props) {
|
||||||
const {
|
const {
|
||||||
@@ -9,12 +8,10 @@ function AuthorHistoryTable(props) {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<AuthorHistoryContentConnector
|
||||||
<AuthorHistoryContentConnector
|
component={AuthorHistoryTableContent}
|
||||||
component={AuthorHistoryTableContent}
|
{...otherProps}
|
||||||
{...otherProps}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
.blankpad {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-left: 2em;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'blankpad': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -7,7 +7,6 @@ import TableBody from 'Components/Table/TableBody';
|
|||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import AuthorHistoryRowConnector from './AuthorHistoryRowConnector';
|
import AuthorHistoryRowConnector from './AuthorHistoryRowConnector';
|
||||||
import styles from './AuthorHistoryTableContent.css';
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@@ -65,7 +64,7 @@ class AuthorHistoryTableContent extends Component {
|
|||||||
const hasItems = !!items.length;
|
const hasItems = !!items.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{
|
{
|
||||||
isFetching &&
|
isFetching &&
|
||||||
<LoadingIndicator />
|
<LoadingIndicator />
|
||||||
@@ -80,7 +79,7 @@ class AuthorHistoryTableContent extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !hasItems && !error &&
|
isPopulated && !hasItems && !error &&
|
||||||
<div className={styles.blankpad}>
|
<div>
|
||||||
{translate('NoHistory')}
|
{translate('NoHistory')}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -104,7 +103,7 @@ class AuthorHistoryTableContent extends Component {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
}
|
}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import AuthorIndex from './AuthorIndex';
|
|||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createAuthorClientSideCollectionItemsSelector('authorIndex'),
|
createAuthorClientSideCollectionItemsSelector('authorIndex'),
|
||||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_AUTHOR),
|
createCommandExecutingSelector(commandNames.REFRESH_AUTHOR),
|
||||||
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
||||||
createCommandExecutingSelector(commandNames.RENAME_AUTHOR),
|
createCommandExecutingSelector(commandNames.RENAME_AUTHOR),
|
||||||
createCommandExecutingSelector(commandNames.RETAG_AUTHOR),
|
createCommandExecutingSelector(commandNames.RETAG_AUTHOR),
|
||||||
@@ -24,17 +24,17 @@ function createMapStateToProps() {
|
|||||||
(
|
(
|
||||||
author,
|
author,
|
||||||
isRefreshingAuthor,
|
isRefreshingAuthor,
|
||||||
isRssSyncExecuting,
|
|
||||||
isOrganizingAuthor,
|
isOrganizingAuthor,
|
||||||
isRetaggingAuthor,
|
isRetaggingAuthor,
|
||||||
|
isRssSyncExecuting,
|
||||||
dimensionsState
|
dimensionsState
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
...author,
|
...author,
|
||||||
isRefreshingAuthor,
|
isRefreshingAuthor,
|
||||||
isRssSyncExecuting,
|
|
||||||
isOrganizingAuthor,
|
isOrganizingAuthor,
|
||||||
isRetaggingAuthor,
|
isRetaggingAuthor,
|
||||||
|
isRssSyncExecuting,
|
||||||
isSmallScreen: dimensionsState.isSmallScreen
|
isSmallScreen: dimensionsState.isSmallScreen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-30
@@ -14,39 +14,14 @@ import { inputTypes } from 'Helpers/Props';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const nameOptions = [
|
const nameOptions = [
|
||||||
{
|
{ key: 'firstLast', value: translate('NameFirstLast') },
|
||||||
key: 'firstLast',
|
{ key: 'lastFirst', value: translate('NameLastFirst') }
|
||||||
get value() {
|
|
||||||
return translate('NameFirstLast');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'lastFirst',
|
|
||||||
get value() {
|
|
||||||
return translate('NameLastFirst');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const posterSizeOptions = [
|
const posterSizeOptions = [
|
||||||
{
|
{ key: 'small', value: 'Small' },
|
||||||
key: 'small',
|
{ key: 'medium', value: 'Medium' },
|
||||||
get value() {
|
{ key: 'large', value: 'Large' }
|
||||||
return translate('Small');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'medium',
|
|
||||||
get value() {
|
|
||||||
return translate('Medium');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'large',
|
|
||||||
get value() {
|
|
||||||
return translate('Large');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
class AuthorIndexOverviewOptionsModalContent extends Component {
|
class AuthorIndexOverviewOptionsModalContent extends Component {
|
||||||
|
|||||||
@@ -14,45 +14,15 @@ import { inputTypes } from 'Helpers/Props';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const posterSizeOptions = [
|
const posterSizeOptions = [
|
||||||
{
|
{ key: 'small', value: 'Small' },
|
||||||
key: 'small',
|
{ key: 'medium', value: 'Medium' },
|
||||||
get value() {
|
{ key: 'large', value: 'Large' }
|
||||||
return translate('Small');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'medium',
|
|
||||||
get value() {
|
|
||||||
return translate('Medium');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'large',
|
|
||||||
get value() {
|
|
||||||
return translate('Large');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const nameOptions = [
|
const nameOptions = [
|
||||||
{
|
{ key: 'no', value: translate('NoName') },
|
||||||
key: 'no',
|
{ key: 'firstLast', value: translate('NameFirstLast') },
|
||||||
get value() {
|
{ key: 'lastFirst', value: translate('NameLastFirst') }
|
||||||
return translate('NoName');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'firstLast',
|
|
||||||
get value() {
|
|
||||||
return translate('NameFirstLast');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'lastFirst',
|
|
||||||
get value() {
|
|
||||||
return translate('NameLastFirst');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
class AuthorIndexPosterOptionsModalContent extends Component {
|
class AuthorIndexPosterOptionsModalContent extends Component {
|
||||||
|
|||||||
@@ -7,18 +7,8 @@ import { inputTypes } from 'Helpers/Props';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const nameOptions = [
|
const nameOptions = [
|
||||||
{
|
{ key: 'firstLast', value: translate('NameFirstLast') },
|
||||||
key: 'firstLast',
|
{ key: 'lastFirst', value: translate('NameLastFirst') }
|
||||||
get value() {
|
|
||||||
return translate('NameFirstLast');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'lastFirst',
|
|
||||||
get value() {
|
|
||||||
return translate('NameLastFirst');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
class AuthorIndexTableOptions extends Component {
|
class AuthorIndexTableOptions extends Component {
|
||||||
|
|||||||
@@ -6,5 +6,4 @@
|
|||||||
|
|
||||||
.statusIcon {
|
.statusIcon {
|
||||||
width: 20px !important;
|
width: 20px !important;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,14 +99,9 @@ class BookDetails extends Component {
|
|||||||
nextBook,
|
nextBook,
|
||||||
isSearching,
|
isSearching,
|
||||||
onRefreshPress,
|
onRefreshPress,
|
||||||
onSearchPress,
|
onSearchPress
|
||||||
statistics = {}
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
|
||||||
bookFileCount = 0
|
|
||||||
} = statistics;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOrganizeModalOpen,
|
isOrganizeModalOpen,
|
||||||
isRetagModalOpen,
|
isRetagModalOpen,
|
||||||
@@ -243,21 +238,21 @@ class BookDetails extends Component {
|
|||||||
className={styles.tab}
|
className={styles.tab}
|
||||||
selectedClassName={styles.selectedTab}
|
selectedClassName={styles.selectedTab}
|
||||||
>
|
>
|
||||||
{translate('History')}
|
History
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
className={styles.tab}
|
className={styles.tab}
|
||||||
selectedClassName={styles.selectedTab}
|
selectedClassName={styles.selectedTab}
|
||||||
>
|
>
|
||||||
{translate('Search')}
|
Search
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
className={styles.tab}
|
className={styles.tab}
|
||||||
selectedClassName={styles.selectedTab}
|
selectedClassName={styles.selectedTab}
|
||||||
>
|
>
|
||||||
{translate('FilesTotal', [bookFileCount])}
|
Files
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -340,7 +335,6 @@ BookDetails.propTypes = {
|
|||||||
ratings: PropTypes.object.isRequired,
|
ratings: PropTypes.object.isRequired,
|
||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
links: PropTypes.arrayOf(PropTypes.object).isRequired,
|
links: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
statistics: PropTypes.object.isRequired,
|
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
isSaving: PropTypes.bool.isRequired,
|
isSaving: PropTypes.bool.isRequired,
|
||||||
|
|||||||
@@ -117,9 +117,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-weight: 300;
|
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
line-height: 30px;
|
line-height: 50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ const defaultFontSize = parseInt(fonts.defaultFontSize);
|
|||||||
const lineHeight = parseFloat(fonts.lineHeight);
|
const lineHeight = parseFloat(fonts.lineHeight);
|
||||||
|
|
||||||
function getFanartUrl(images) {
|
function getFanartUrl(images) {
|
||||||
return images.find((x) => x.coverType === 'fanart')?.url;
|
const fanartImage = images.find((x) => x.coverType === 'fanart');
|
||||||
|
|
||||||
|
if (fanartImage) {
|
||||||
|
// Remove protocol
|
||||||
|
return fanartImage.url.replace(/^https?:/, '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BookDetailsHeader extends Component {
|
class BookDetailsHeader extends Component {
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import BookIndex from './BookIndex';
|
|||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createBookClientSideCollectionItemsSelector('bookIndex'),
|
createBookClientSideCollectionItemsSelector('bookIndex'),
|
||||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_AUTHOR),
|
createCommandExecutingSelector(commandNames.REFRESH_AUTHOR),
|
||||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_BOOK),
|
createCommandExecutingSelector(commandNames.REFRESH_BOOK),
|
||||||
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
||||||
createCommandExecutingSelector(commandNames.CUTOFF_UNMET_BOOK_SEARCH),
|
createCommandExecutingSelector(commandNames.CUTOFF_UNMET_BOOK_SEARCH),
|
||||||
createCommandExecutingSelector(commandNames.MISSING_BOOK_SEARCH),
|
createCommandExecutingSelector(commandNames.MISSING_BOOK_SEARCH),
|
||||||
|
|||||||
@@ -229,6 +229,7 @@ class BookIndexRow extends Component {
|
|||||||
className={styles[name]}
|
className={styles[name]}
|
||||||
>
|
>
|
||||||
{bookFileCount}
|
{bookFileCount}
|
||||||
|
|
||||||
</VirtualTableRowCell>
|
</VirtualTableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Modal from 'Components/Modal/Modal';
|
import Modal from 'Components/Modal/Modal';
|
||||||
import { sizes } from 'Helpers/Props';
|
|
||||||
import BookInteractiveSearchModalContent from './BookInteractiveSearchModalContent';
|
import BookInteractiveSearchModalContent from './BookInteractiveSearchModalContent';
|
||||||
|
|
||||||
function BookInteractiveSearchModal(props) {
|
function BookInteractiveSearchModal(props) {
|
||||||
@@ -15,7 +14,6 @@ function BookInteractiveSearchModal(props) {
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
size={sizes.EXTRA_LARGE}
|
|
||||||
closeOnBackgroundClick={false}
|
closeOnBackgroundClick={false}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
.container {
|
|
||||||
border: 1px solid var(--borderColor);
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: var(--inputBackgroundColor);
|
|
||||||
|
|
||||||
&:last-of-type {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'container': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BookFileEditorTableContentConnector from './BookFileEditorTableContentConnector';
|
import BookFileEditorTableContentConnector from './BookFileEditorTableContentConnector';
|
||||||
import styles from './BookFileEditorTable.css';
|
|
||||||
|
|
||||||
function BookFileEditorTable(props) {
|
function BookFileEditorTable(props) {
|
||||||
const {
|
const {
|
||||||
@@ -8,11 +7,9 @@ function BookFileEditorTable(props) {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<BookFileEditorTableContentConnector
|
||||||
<BookFileEditorTableContentConnector
|
{...otherProps}
|
||||||
{...otherProps}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.filesTable {
|
.filesTable {
|
||||||
margin: 10px;
|
margin-bottom: 20px;
|
||||||
padding-top: 5px;
|
padding-top: 15px;
|
||||||
border: 1px solid var(--borderColor);
|
border: 1px solid var(--borderColor);
|
||||||
border-top: 1px solid var(--borderColor);
|
border-top: 1px solid var(--borderColor);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -13,15 +13,9 @@
|
|||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 10px;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectInput {
|
.selectInput {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blankpad {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-left: 2em;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Please do not change this file!
|
// Please do not change this file!
|
||||||
interface CssExports {
|
interface CssExports {
|
||||||
'actions': string;
|
'actions': string;
|
||||||
'blankpad': string;
|
|
||||||
'filesTable': string;
|
'filesTable': string;
|
||||||
'selectInput': string;
|
'selectInput': string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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 SelectInput from 'Components/Form/SelectInput';
|
import SelectInput from 'Components/Form/SelectInput';
|
||||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
@@ -121,7 +120,7 @@ class BookFileEditorTableContent extends Component {
|
|||||||
const hasSelectedFiles = this.getSelectedIds().length > 0;
|
const hasSelectedFiles = this.getSelectedIds().length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{
|
{
|
||||||
isFetching && !isPopulated ?
|
isFetching && !isPopulated ?
|
||||||
<LoadingIndicator /> :
|
<LoadingIndicator /> :
|
||||||
@@ -130,13 +129,13 @@ class BookFileEditorTableContent extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && error ?
|
!isFetching && error ?
|
||||||
<Alert kind={kinds.DANGER}>{error}</Alert> :
|
<div>{error}</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !items.length ?
|
isPopulated && !items.length ?
|
||||||
<div className={styles.blankpad}>
|
<div>
|
||||||
No book files to manage.
|
No book files to manage.
|
||||||
</div> :
|
</div> :
|
||||||
null
|
null
|
||||||
@@ -174,30 +173,26 @@ class BookFileEditorTableContent extends Component {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
<div className={styles.actions}>
|
||||||
isPopulated && items.length ? (
|
<SpinnerButton
|
||||||
<div className={styles.actions}>
|
kind={kinds.DANGER}
|
||||||
<SpinnerButton
|
isSpinning={isDeleting}
|
||||||
kind={kinds.DANGER}
|
isDisabled={!hasSelectedFiles}
|
||||||
isSpinning={isDeleting}
|
onPress={this.onDeletePress}
|
||||||
isDisabled={!hasSelectedFiles}
|
>
|
||||||
onPress={this.onDeletePress}
|
Delete
|
||||||
>
|
</SpinnerButton>
|
||||||
{translate('Delete')}
|
|
||||||
</SpinnerButton>
|
|
||||||
|
|
||||||
<div className={styles.selectInput}>
|
<div className={styles.selectInput}>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
name="quality"
|
name="quality"
|
||||||
value="selectQuality"
|
value="selectQuality"
|
||||||
values={qualityOptions}
|
values={qualityOptions}
|
||||||
isDisabled={!hasSelectedFiles}
|
isDisabled={!hasSelectedFiles}
|
||||||
onChange={this.onQualityChange}
|
onChange={this.onQualityChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null
|
|
||||||
}
|
|
||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={isConfirmDeleteModalOpen}
|
isOpen={isConfirmDeleteModalOpen}
|
||||||
@@ -208,7 +203,7 @@ class BookFileEditorTableContent extends Component {
|
|||||||
onConfirm={this.onConfirmDelete}
|
onConfirm={this.onConfirmDelete}
|
||||||
onCancel={this.onConfirmDeleteModalClose}
|
onCancel={this.onConfirmDeleteModalClose}
|
||||||
/>
|
/>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ class BookshelfFooter extends Component {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
{translate('CountAuthorsSelected', { selectedCount })}
|
{selectedCount} Author(s) Selected
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SpinnerButton
|
<SpinnerButton
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class CalendarConnector extends Component {
|
|||||||
gotoCalendarToday
|
gotoCalendarToday
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
registerPagePopulator(this.repopulate, ['bookFileUpdated', 'bookFileDeleted']);
|
registerPagePopulator(this.repopulate);
|
||||||
|
|
||||||
if (useCurrentPage) {
|
if (useCurrentPage) {
|
||||||
fetchCalendar();
|
fetchCalendar();
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
.description {
|
||||||
|
line-height: $lineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
line-height: $lineHeight;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
|
|||||||
@@ -25,10 +25,6 @@
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointMedium) {
|
@media only screen and (max-width: $breakpointMedium) {
|
||||||
.image {
|
.image {
|
||||||
height: 250px;
|
height: 250px;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ interface CssExports {
|
|||||||
'image': string;
|
'image': string;
|
||||||
'imageContainer': string;
|
'imageContainer': string;
|
||||||
'message': string;
|
'message': string;
|
||||||
'version': string;
|
|
||||||
}
|
}
|
||||||
export const cssExports: CssExports;
|
export const cssExports: CssExports;
|
||||||
export default cssExports;
|
export default cssExports;
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import styles from './ErrorBoundaryError.css';
|
||||||
|
|
||||||
|
function ErrorBoundaryError(props) {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
messageClassName,
|
||||||
|
detailsClassName,
|
||||||
|
message,
|
||||||
|
error,
|
||||||
|
info
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<div className={messageClassName}>
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.imageContainer}>
|
||||||
|
<img
|
||||||
|
className={styles.image}
|
||||||
|
src={`${window.Readarr.urlBase}/Content/Images/error.png`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<details className={detailsClassName}>
|
||||||
|
{
|
||||||
|
error &&
|
||||||
|
<div>
|
||||||
|
{error.toString()}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className={styles.info}>
|
||||||
|
{info.componentStack}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorBoundaryError.propTypes = {
|
||||||
|
className: PropTypes.string.isRequired,
|
||||||
|
messageClassName: PropTypes.string.isRequired,
|
||||||
|
detailsClassName: PropTypes.string.isRequired,
|
||||||
|
message: PropTypes.string.isRequired,
|
||||||
|
error: PropTypes.object.isRequired,
|
||||||
|
info: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorBoundaryError.defaultProps = {
|
||||||
|
className: styles.container,
|
||||||
|
messageClassName: styles.message,
|
||||||
|
detailsClassName: styles.details,
|
||||||
|
message: 'There was an error loading this content'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ErrorBoundaryError;
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import StackTrace from 'stacktrace-js';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './ErrorBoundaryError.css';
|
|
||||||
|
|
||||||
interface ErrorBoundaryErrorProps {
|
|
||||||
className: string;
|
|
||||||
messageClassName: string;
|
|
||||||
detailsClassName: string;
|
|
||||||
message: string;
|
|
||||||
error: Error;
|
|
||||||
info: {
|
|
||||||
componentStack: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
|
|
||||||
const {
|
|
||||||
className = styles.container,
|
|
||||||
messageClassName = styles.message,
|
|
||||||
detailsClassName = styles.details,
|
|
||||||
message = translate('ErrorLoadingContent'),
|
|
||||||
error,
|
|
||||||
info,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const [detailedError, setDetailedError] = useState<
|
|
||||||
StackTrace.StackFrame[] | null
|
|
||||||
>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (error) {
|
|
||||||
StackTrace.fromError(error).then((de) => {
|
|
||||||
setDetailedError(de);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setDetailedError(null);
|
|
||||||
}
|
|
||||||
}, [error, setDetailedError]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<div className={messageClassName}>{message}</div>
|
|
||||||
|
|
||||||
<div className={styles.imageContainer}>
|
|
||||||
<img
|
|
||||||
className={styles.image}
|
|
||||||
src={`${window.Readarr.urlBase}/Content/Images/error.png`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<details className={detailsClassName}>
|
|
||||||
{error ? <div>{error.message}</div> : null}
|
|
||||||
|
|
||||||
{detailedError ? (
|
|
||||||
detailedError.map((d, index) => {
|
|
||||||
return (
|
|
||||||
<div key={index}>
|
|
||||||
{` at ${d.functionName} (${d.fileName}:${d.lineNumber}:${d.columnNumber})`}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<div>{info.componentStack}</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{
|
|
||||||
<div className={styles.version}>
|
|
||||||
Version: {window.Readarr.version}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ErrorBoundaryError;
|
|
||||||
@@ -206,11 +206,9 @@ class FilterBuilderRow extends Component {
|
|||||||
const selectedFilterBuilderProp = this.selectedFilterBuilderProp;
|
const selectedFilterBuilderProp = this.selectedFilterBuilderProp;
|
||||||
|
|
||||||
const keyOptions = filterBuilderProps.map((availablePropFilter) => {
|
const keyOptions = filterBuilderProps.map((availablePropFilter) => {
|
||||||
const { name, label } = availablePropFilter;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: name,
|
key: availablePropFilter.name,
|
||||||
value: typeof label === 'function' ? label() : label
|
value: availablePropFilter.label
|
||||||
};
|
};
|
||||||
}).sort((a, b) => a.value.localeCompare(b.value));
|
}).sort((a, b) => a.value.localeCompare(b.value));
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,6 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--inputHoverBackgroundColor);
|
background-color: var(--inputHoverBackgroundColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.isDisabled {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.optionCheck {
|
.optionCheck {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class NumberInput extends Component {
|
|||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
const { value } = this.props;
|
const { value } = this.props;
|
||||||
|
|
||||||
if (!isNaN(value) && value !== prevProps.value && !this.state.isFocused) {
|
if (value !== prevProps.value && !this.state.isFocused) {
|
||||||
this.setState({
|
this.setState({
|
||||||
value: value == null ? '' : value.toString()
|
value: value == null ? '' : value.toString()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class SelectInput extends Component {
|
|||||||
value={key}
|
value={key}
|
||||||
{...otherOptionProps}
|
{...otherOptionProps}
|
||||||
>
|
>
|
||||||
{typeof optionValue === 'function' ? optionValue() : optionValue}
|
{optionValue}
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@@ -75,7 +75,7 @@ SelectInput.propTypes = {
|
|||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabledClassName: PropTypes.string,
|
disabledClassName: PropTypes.string,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.func]).isRequired,
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isDisabled: PropTypes.bool,
|
isDisabled: PropTypes.bool,
|
||||||
hasError: PropTypes.bool,
|
hasError: PropTypes.bool,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class Icon extends PureComponent {
|
|||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={containerClassName}
|
className={containerClassName}
|
||||||
title={typeof title === 'function' ? title() : title}
|
title={title}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</span>
|
</span>
|
||||||
@@ -58,7 +58,7 @@ Icon.propTypes = {
|
|||||||
name: PropTypes.object.isRequired,
|
name: PropTypes.object.isRequired,
|
||||||
kind: PropTypes.string.isRequired,
|
kind: PropTypes.string.isRequired,
|
||||||
size: PropTypes.number.isRequired,
|
size: PropTypes.number.isRequired,
|
||||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
title: PropTypes.string,
|
||||||
isSpinning: PropTypes.bool.isRequired,
|
isSpinning: PropTypes.bool.isRequired,
|
||||||
fixedWidth: PropTypes.bool.isRequired
|
fixedWidth: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ class SpinnerErrorButton extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
kind,
|
|
||||||
isSpinning,
|
isSpinning,
|
||||||
error,
|
error,
|
||||||
children,
|
children,
|
||||||
@@ -113,7 +112,7 @@ class SpinnerErrorButton extends Component {
|
|||||||
const showIcon = wasSuccessful || hasWarning || hasError;
|
const showIcon = wasSuccessful || hasWarning || hasError;
|
||||||
|
|
||||||
let iconName = icons.CHECK;
|
let iconName = icons.CHECK;
|
||||||
let iconKind = kind === kinds.PRIMARY ? kinds.DEFAULT : kinds.SUCCESS;
|
let iconKind = kinds.SUCCESS;
|
||||||
|
|
||||||
if (hasWarning) {
|
if (hasWarning) {
|
||||||
iconName = icons.WARNING;
|
iconName = icons.WARNING;
|
||||||
@@ -127,7 +126,6 @@ class SpinnerErrorButton extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SpinnerButton
|
<SpinnerButton
|
||||||
kind={kind}
|
|
||||||
isSpinning={isSpinning}
|
isSpinning={isSpinning}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
@@ -156,7 +154,6 @@ class SpinnerErrorButton extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SpinnerErrorButton.propTypes = {
|
SpinnerErrorButton.propTypes = {
|
||||||
kind: PropTypes.oneOf(kinds.all),
|
|
||||||
isSpinning: PropTypes.bool.isRequired,
|
isSpinning: PropTypes.bool.isRequired,
|
||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
children: PropTypes.node.isRequired
|
children: PropTypes.node.isRequired
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ class InlineMarkdown extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
data,
|
data
|
||||||
blockClassName
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// For now only replace links or code blocks (not both)
|
// For now only replace links or code blocks (not both)
|
||||||
@@ -48,7 +47,7 @@ class InlineMarkdown extends Component {
|
|||||||
markdownBlocks.push(data.substr(endIndex, match.index - endIndex));
|
markdownBlocks.push(data.substr(endIndex, match.index - endIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
markdownBlocks.push(<code key={`code-${match.index}`} className={blockClassName ?? null}>{match[0].substring(1, match[0].length - 1)}</code>);
|
markdownBlocks.push(<code key={`code-${match.index}`}>{match[0].substring(1, match[0].length - 1)}</code>);
|
||||||
endIndex = match.index + match[0].length;
|
endIndex = match.index + match[0].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +66,7 @@ class InlineMarkdown extends Component {
|
|||||||
|
|
||||||
InlineMarkdown.propTypes = {
|
InlineMarkdown.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
data: PropTypes.string,
|
data: PropTypes.string
|
||||||
blockClassName: PropTypes.string
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InlineMarkdown;
|
export default InlineMarkdown;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class FilterMenuContent extends Component {
|
|||||||
selectedFilterKey={selectedFilterKey}
|
selectedFilterKey={selectedFilterKey}
|
||||||
onPress={onFilterSelect}
|
onPress={onFilterSelect}
|
||||||
>
|
>
|
||||||
{typeof filter.label === 'function' ? filter.label() : filter.label}
|
{filter.label}
|
||||||
</FilterMenuItem>
|
</FilterMenuItem>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ function ErrorPage(props) {
|
|||||||
const {
|
const {
|
||||||
version,
|
version,
|
||||||
isLocalStorageSupported,
|
isLocalStorageSupported,
|
||||||
translationsError,
|
hasTranslationsError,
|
||||||
authorError,
|
authorError,
|
||||||
customFiltersError,
|
customFiltersError,
|
||||||
tagsError,
|
tagsError,
|
||||||
@@ -21,8 +21,8 @@ function ErrorPage(props) {
|
|||||||
|
|
||||||
if (!isLocalStorageSupported) {
|
if (!isLocalStorageSupported) {
|
||||||
errorMessage = 'Local Storage is not supported or disabled. A plugin or private browsing may have disabled it.';
|
errorMessage = 'Local Storage is not supported or disabled. A plugin or private browsing may have disabled it.';
|
||||||
} else if (translationsError) {
|
} else if (hasTranslationsError) {
|
||||||
errorMessage = getErrorMessage(translationsError, 'Failed to load translations from API');
|
errorMessage = 'Failed to load translations from API';
|
||||||
} else if (authorError) {
|
} else if (authorError) {
|
||||||
errorMessage = getErrorMessage(authorError, 'Failed to load author from API');
|
errorMessage = getErrorMessage(authorError, 'Failed to load author from API');
|
||||||
} else if (customFiltersError) {
|
} else if (customFiltersError) {
|
||||||
@@ -55,7 +55,7 @@ function ErrorPage(props) {
|
|||||||
ErrorPage.propTypes = {
|
ErrorPage.propTypes = {
|
||||||
version: PropTypes.string.isRequired,
|
version: PropTypes.string.isRequired,
|
||||||
isLocalStorageSupported: PropTypes.bool.isRequired,
|
isLocalStorageSupported: PropTypes.bool.isRequired,
|
||||||
translationsError: PropTypes.object,
|
hasTranslationsError: PropTypes.bool.isRequired,
|
||||||
authorError: PropTypes.object,
|
authorError: PropTypes.object,
|
||||||
customFiltersError: PropTypes.object,
|
customFiltersError: PropTypes.object,
|
||||||
tagsError: PropTypes.object,
|
tagsError: PropTypes.object,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { Component } from 'react';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { fetchTranslations, saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
|
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
|
||||||
import { fetchAuthor } from 'Store/Actions/authorActions';
|
import { fetchAuthor } from 'Store/Actions/authorActions';
|
||||||
import { fetchBooks } from 'Store/Actions/bookActions';
|
import { fetchBooks } from 'Store/Actions/bookActions';
|
||||||
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
|
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
|
||||||
@@ -52,7 +52,6 @@ const selectIsPopulated = createSelector(
|
|||||||
(state) => state.settings.metadataProfiles.isPopulated,
|
(state) => state.settings.metadataProfiles.isPopulated,
|
||||||
(state) => state.settings.importLists.isPopulated,
|
(state) => state.settings.importLists.isPopulated,
|
||||||
(state) => state.system.status.isPopulated,
|
(state) => state.system.status.isPopulated,
|
||||||
(state) => state.app.translations.isPopulated,
|
|
||||||
(
|
(
|
||||||
customFiltersIsPopulated,
|
customFiltersIsPopulated,
|
||||||
tagsIsPopulated,
|
tagsIsPopulated,
|
||||||
@@ -61,8 +60,7 @@ const selectIsPopulated = createSelector(
|
|||||||
qualityProfilesIsPopulated,
|
qualityProfilesIsPopulated,
|
||||||
metadataProfilesIsPopulated,
|
metadataProfilesIsPopulated,
|
||||||
importListsIsPopulated,
|
importListsIsPopulated,
|
||||||
systemStatusIsPopulated,
|
systemStatusIsPopulated
|
||||||
translationsIsPopulated
|
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
customFiltersIsPopulated &&
|
customFiltersIsPopulated &&
|
||||||
@@ -72,8 +70,7 @@ const selectIsPopulated = createSelector(
|
|||||||
qualityProfilesIsPopulated &&
|
qualityProfilesIsPopulated &&
|
||||||
metadataProfilesIsPopulated &&
|
metadataProfilesIsPopulated &&
|
||||||
importListsIsPopulated &&
|
importListsIsPopulated &&
|
||||||
systemStatusIsPopulated &&
|
systemStatusIsPopulated
|
||||||
translationsIsPopulated
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -87,7 +84,6 @@ const selectErrors = createSelector(
|
|||||||
(state) => state.settings.metadataProfiles.error,
|
(state) => state.settings.metadataProfiles.error,
|
||||||
(state) => state.settings.importLists.error,
|
(state) => state.settings.importLists.error,
|
||||||
(state) => state.system.status.error,
|
(state) => state.system.status.error,
|
||||||
(state) => state.app.translations.error,
|
|
||||||
(
|
(
|
||||||
customFiltersError,
|
customFiltersError,
|
||||||
tagsError,
|
tagsError,
|
||||||
@@ -96,8 +92,7 @@ const selectErrors = createSelector(
|
|||||||
qualityProfilesError,
|
qualityProfilesError,
|
||||||
metadataProfilesError,
|
metadataProfilesError,
|
||||||
importListsError,
|
importListsError,
|
||||||
systemStatusError,
|
systemStatusError
|
||||||
translationsError
|
|
||||||
) => {
|
) => {
|
||||||
const hasError = !!(
|
const hasError = !!(
|
||||||
customFiltersError ||
|
customFiltersError ||
|
||||||
@@ -107,8 +102,7 @@ const selectErrors = createSelector(
|
|||||||
qualityProfilesError ||
|
qualityProfilesError ||
|
||||||
metadataProfilesError ||
|
metadataProfilesError ||
|
||||||
importListsError ||
|
importListsError ||
|
||||||
systemStatusError ||
|
systemStatusError
|
||||||
translationsError
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -120,8 +114,7 @@ const selectErrors = createSelector(
|
|||||||
qualityProfilesError,
|
qualityProfilesError,
|
||||||
metadataProfilesError,
|
metadataProfilesError,
|
||||||
importListsError,
|
importListsError,
|
||||||
systemStatusError,
|
systemStatusError
|
||||||
translationsError
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -183,9 +176,6 @@ function createMapDispatchToProps(dispatch, props) {
|
|||||||
dispatchFetchStatus() {
|
dispatchFetchStatus() {
|
||||||
dispatch(fetchStatus());
|
dispatch(fetchStatus());
|
||||||
},
|
},
|
||||||
dispatchFetchTranslations() {
|
|
||||||
dispatch(fetchTranslations());
|
|
||||||
},
|
|
||||||
onResize(dimensions) {
|
onResize(dimensions) {
|
||||||
dispatch(saveDimensions(dimensions));
|
dispatch(saveDimensions(dimensions));
|
||||||
},
|
},
|
||||||
@@ -220,7 +210,6 @@ class PageConnector extends Component {
|
|||||||
this.props.dispatchFetchImportLists();
|
this.props.dispatchFetchImportLists();
|
||||||
this.props.dispatchFetchUISettings();
|
this.props.dispatchFetchUISettings();
|
||||||
this.props.dispatchFetchStatus();
|
this.props.dispatchFetchStatus();
|
||||||
this.props.dispatchFetchTranslations();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +225,7 @@ class PageConnector extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
hasTranslationsError,
|
||||||
isPopulated,
|
isPopulated,
|
||||||
hasError,
|
hasError,
|
||||||
dispatchFetchAuthor,
|
dispatchFetchAuthor,
|
||||||
@@ -247,15 +237,15 @@ class PageConnector extends Component {
|
|||||||
dispatchFetchImportLists,
|
dispatchFetchImportLists,
|
||||||
dispatchFetchUISettings,
|
dispatchFetchUISettings,
|
||||||
dispatchFetchStatus,
|
dispatchFetchStatus,
|
||||||
dispatchFetchTranslations,
|
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (hasError || !this.state.isLocalStorageSupported) {
|
if (hasTranslationsError || hasError || !this.state.isLocalStorageSupported) {
|
||||||
return (
|
return (
|
||||||
<ErrorPage
|
<ErrorPage
|
||||||
{...this.state}
|
{...this.state}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
|
hasTranslationsError={hasTranslationsError}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -276,6 +266,7 @@ class PageConnector extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PageConnector.propTypes = {
|
PageConnector.propTypes = {
|
||||||
|
hasTranslationsError: PropTypes.bool.isRequired,
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
hasError: PropTypes.bool.isRequired,
|
hasError: PropTypes.bool.isRequired,
|
||||||
isSidebarVisible: PropTypes.bool.isRequired,
|
isSidebarVisible: PropTypes.bool.isRequired,
|
||||||
@@ -289,7 +280,6 @@ PageConnector.propTypes = {
|
|||||||
dispatchFetchImportLists: PropTypes.func.isRequired,
|
dispatchFetchImportLists: PropTypes.func.isRequired,
|
||||||
dispatchFetchUISettings: PropTypes.func.isRequired,
|
dispatchFetchUISettings: PropTypes.func.isRequired,
|
||||||
dispatchFetchStatus: PropTypes.func.isRequired,
|
dispatchFetchStatus: PropTypes.func.isRequired,
|
||||||
dispatchFetchTranslations: PropTypes.func.isRequired,
|
|
||||||
onSidebarVisibleChange: PropTypes.func.isRequired
|
onSidebarVisibleChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,28 +21,28 @@ const SIDEBAR_WIDTH = parseInt(dimensions.sidebarWidth);
|
|||||||
const links = [
|
const links = [
|
||||||
{
|
{
|
||||||
iconName: icons.AUTHOR_CONTINUING,
|
iconName: icons.AUTHOR_CONTINUING,
|
||||||
title: () => translate('Library'),
|
title: 'Library',
|
||||||
to: '/',
|
to: '/',
|
||||||
alias: '/authors',
|
alias: '/authors',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: () => translate('Authors'),
|
title: 'Authors',
|
||||||
to: '/authors'
|
to: '/authors'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Books'),
|
title: 'Books',
|
||||||
to: '/books'
|
to: '/books'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('AddNew'),
|
title: 'Add New',
|
||||||
to: '/add/search'
|
to: '/add/search'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Bookshelf'),
|
title: 'Bookshelf',
|
||||||
to: '/shelf'
|
to: '/shelf'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('UnmappedFiles'),
|
title: 'Unmapped Files',
|
||||||
to: '/unmapped'
|
to: '/unmapped'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -50,26 +50,26 @@ const links = [
|
|||||||
|
|
||||||
{
|
{
|
||||||
iconName: icons.CALENDAR,
|
iconName: icons.CALENDAR,
|
||||||
title: () => translate('Calendar'),
|
title: 'Calendar',
|
||||||
to: '/calendar'
|
to: '/calendar'
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
iconName: icons.ACTIVITY,
|
iconName: icons.ACTIVITY,
|
||||||
title: () => translate('Activity'),
|
title: 'Activity',
|
||||||
to: '/activity/queue',
|
to: '/activity/queue',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: () => translate('Queue'),
|
title: 'Queue',
|
||||||
to: '/activity/queue',
|
to: '/activity/queue',
|
||||||
statusComponent: QueueStatusConnector
|
statusComponent: QueueStatusConnector
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('History'),
|
title: 'History',
|
||||||
to: '/activity/history'
|
to: '/activity/history'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Blocklist'),
|
title: 'Blocklist',
|
||||||
to: '/activity/blocklist'
|
to: '/activity/blocklist'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -77,15 +77,15 @@ const links = [
|
|||||||
|
|
||||||
{
|
{
|
||||||
iconName: icons.WARNING,
|
iconName: icons.WARNING,
|
||||||
title: () => translate('Wanted'),
|
title: 'Wanted',
|
||||||
to: '/wanted/missing',
|
to: '/wanted/missing',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: () => translate('Missing'),
|
title: 'Missing',
|
||||||
to: '/wanted/missing'
|
to: '/wanted/missing'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('CutoffUnmet'),
|
title: 'Cutoff Unmet',
|
||||||
to: '/wanted/cutoffunmet'
|
to: '/wanted/cutoffunmet'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -93,55 +93,55 @@ const links = [
|
|||||||
|
|
||||||
{
|
{
|
||||||
iconName: icons.SETTINGS,
|
iconName: icons.SETTINGS,
|
||||||
title: () => translate('Settings'),
|
title: 'Settings',
|
||||||
to: '/settings',
|
to: '/settings',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: () => translate('MediaManagement'),
|
title: 'Media Management',
|
||||||
to: '/settings/mediamanagement'
|
to: '/settings/mediamanagement'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Profiles'),
|
title: 'Profiles',
|
||||||
to: '/settings/profiles'
|
to: '/settings/profiles'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Quality'),
|
title: 'Quality',
|
||||||
to: '/settings/quality'
|
to: '/settings/quality'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('CustomFormats'),
|
title: translate('CustomFormats'),
|
||||||
to: '/settings/customformats'
|
to: '/settings/customformats'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Indexers'),
|
title: translate('Indexers'),
|
||||||
to: '/settings/indexers'
|
to: '/settings/indexers'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('DownloadClients'),
|
title: 'Download Clients',
|
||||||
to: '/settings/downloadclients'
|
to: '/settings/downloadclients'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('ImportLists'),
|
title: 'Import Lists',
|
||||||
to: '/settings/importlists'
|
to: '/settings/importlists'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Connect'),
|
title: 'Connect',
|
||||||
to: '/settings/connect'
|
to: '/settings/connect'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Metadata'),
|
title: 'Metadata',
|
||||||
to: '/settings/metadata'
|
to: '/settings/metadata'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Tags'),
|
title: 'Tags',
|
||||||
to: '/settings/tags'
|
to: '/settings/tags'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('General'),
|
title: 'General',
|
||||||
to: '/settings/general'
|
to: '/settings/general'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Ui'),
|
title: 'UI',
|
||||||
to: '/settings/ui'
|
to: '/settings/ui'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -149,32 +149,32 @@ const links = [
|
|||||||
|
|
||||||
{
|
{
|
||||||
iconName: icons.SYSTEM,
|
iconName: icons.SYSTEM,
|
||||||
title: () => translate('System'),
|
title: 'System',
|
||||||
to: '/system/status',
|
to: '/system/status',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: () => translate('Status'),
|
title: 'Status',
|
||||||
to: '/system/status',
|
to: '/system/status',
|
||||||
statusComponent: HealthStatusConnector
|
statusComponent: HealthStatusConnector
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Tasks'),
|
title: 'Tasks',
|
||||||
to: '/system/tasks'
|
to: '/system/tasks'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Backup'),
|
title: 'Backup',
|
||||||
to: '/system/backup'
|
to: '/system/backup'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Updates'),
|
title: 'Updates',
|
||||||
to: '/system/updates'
|
to: '/system/updates'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('Events'),
|
title: 'Events',
|
||||||
to: '/system/events'
|
to: '/system/events'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: () => translate('LogFiles'),
|
title: 'Log Files',
|
||||||
to: '/system/logs/files'
|
to: '/system/logs/files'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class PageSidebarItem extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<span className={isChildItem ? styles.noIcon : null}>
|
<span className={isChildItem ? styles.noIcon : null}>
|
||||||
{typeof title === 'function' ? title() : title}
|
{title}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -88,7 +88,7 @@ class PageSidebarItem extends Component {
|
|||||||
|
|
||||||
PageSidebarItem.propTypes = {
|
PageSidebarItem.propTypes = {
|
||||||
iconName: PropTypes.object,
|
iconName: PropTypes.object,
|
||||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
to: PropTypes.string.isRequired,
|
to: PropTypes.string.isRequired,
|
||||||
isActive: PropTypes.bool,
|
isActive: PropTypes.bool,
|
||||||
isActiveParent: PropTypes.bool,
|
isActiveParent: PropTypes.bool,
|
||||||
|
|||||||
@@ -202,8 +202,6 @@ class SignalRConnector extends Component {
|
|||||||
this.props.dispatchUpdateItem({ section, ...body.resource });
|
this.props.dispatchUpdateItem({ section, ...body.resource });
|
||||||
} else if (body.action === 'deleted') {
|
} else if (body.action === 'deleted') {
|
||||||
this.props.dispatchRemoveItem({ section, id: body.resource.id });
|
this.props.dispatchRemoveItem({ section, id: body.resource.id });
|
||||||
|
|
||||||
repopulatePage('bookFileDeleted');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repopulate the page to handle recently imported file
|
// Repopulate the page to handle recently imported file
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
type PropertyFunction<T> = () => T;
|
|
||||||
|
|
||||||
interface Column {
|
interface Column {
|
||||||
name: string;
|
name: string;
|
||||||
label: string | PropertyFunction<string> | React.ReactNode;
|
label: string | React.ReactNode;
|
||||||
columnLabel?: string;
|
columnLabel?: string;
|
||||||
isSortable?: boolean;
|
isSortable?: boolean;
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ function Table(props) {
|
|||||||
{...getTableHeaderCellProps(otherProps)}
|
{...getTableHeaderCellProps(otherProps)}
|
||||||
{...column}
|
{...column}
|
||||||
>
|
>
|
||||||
{typeof column.label === 'function' ? column.label() : column.label}
|
{column.label}
|
||||||
</TableHeaderCell>
|
</TableHeaderCell>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ class TableHeaderCell extends Component {
|
|||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
name,
|
name,
|
||||||
label,
|
|
||||||
columnLabel,
|
columnLabel,
|
||||||
isSortable,
|
isSortable,
|
||||||
isVisible,
|
isVisible,
|
||||||
@@ -54,8 +53,7 @@ class TableHeaderCell extends Component {
|
|||||||
{...otherProps}
|
{...otherProps}
|
||||||
component="th"
|
component="th"
|
||||||
className={className}
|
className={className}
|
||||||
label={typeof label === 'function' ? label() : label}
|
title={columnLabel}
|
||||||
title={typeof columnLabel === 'function' ? columnLabel() : columnLabel}
|
|
||||||
onPress={this.onPress}
|
onPress={this.onPress}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -79,8 +77,7 @@ class TableHeaderCell extends Component {
|
|||||||
TableHeaderCell.propTypes = {
|
TableHeaderCell.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.node]),
|
columnLabel: PropTypes.string,
|
||||||
columnLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
||||||
isSortable: PropTypes.bool,
|
isSortable: PropTypes.bool,
|
||||||
isVisible: PropTypes.bool,
|
isVisible: PropTypes.bool,
|
||||||
isModifiable: PropTypes.bool,
|
isModifiable: PropTypes.bool,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function TableOptionsColumn(props) {
|
|||||||
isDisabled={isModifiable === false}
|
isDisabled={isModifiable === false}
|
||||||
onChange={onVisibleChange}
|
onChange={onVisibleChange}
|
||||||
/>
|
/>
|
||||||
{typeof label === 'function' ? label() : label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@ function TableOptionsColumn(props) {
|
|||||||
|
|
||||||
TableOptionsColumn.propTypes = {
|
TableOptionsColumn.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
isVisible: PropTypes.bool.isRequired,
|
isVisible: PropTypes.bool.isRequired,
|
||||||
isModifiable: PropTypes.bool.isRequired,
|
isModifiable: PropTypes.bool.isRequired,
|
||||||
index: PropTypes.number.isRequired,
|
index: PropTypes.number.isRequired,
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class TableOptionsColumnDragSource extends Component {
|
|||||||
|
|
||||||
<TableOptionsColumn
|
<TableOptionsColumn
|
||||||
name={name}
|
name={name}
|
||||||
label={typeof label === 'function' ? label() : label}
|
label={label}
|
||||||
isVisible={isVisible}
|
isVisible={isVisible}
|
||||||
isModifiable={isModifiable}
|
isModifiable={isModifiable}
|
||||||
index={index}
|
index={index}
|
||||||
@@ -138,7 +138,7 @@ class TableOptionsColumnDragSource extends Component {
|
|||||||
|
|
||||||
TableOptionsColumnDragSource.propTypes = {
|
TableOptionsColumnDragSource.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
isVisible: PropTypes.bool.isRequired,
|
isVisible: PropTypes.bool.isRequired,
|
||||||
isModifiable: PropTypes.bool.isRequired,
|
isModifiable: PropTypes.bool.isRequired,
|
||||||
index: PropTypes.number.isRequired,
|
index: PropTypes.number.isRequired,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { tooltipPositions } from 'Helpers/Props';
|
|
||||||
import Tooltip from './Tooltip';
|
import Tooltip from './Tooltip';
|
||||||
import styles from './Popover.css';
|
import styles from './Popover.css';
|
||||||
|
|
||||||
@@ -31,13 +30,8 @@ function Popover(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Popover.propTypes = {
|
Popover.propTypes = {
|
||||||
className: PropTypes.string,
|
|
||||||
bodyClassName: PropTypes.string,
|
|
||||||
anchor: PropTypes.node.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
body: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
|
body: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired
|
||||||
position: PropTypes.oneOf(tooltipPositions.all),
|
|
||||||
canFlip: PropTypes.bool
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Popover;
|
export default Popover;
|
||||||
|
|||||||
@@ -15,5 +15,5 @@
|
|||||||
"start_url": "../../../../",
|
"start_url": "../../../../",
|
||||||
"theme_color": "#3a3f51",
|
"theme_color": "#3a3f51",
|
||||||
"background_color": "#3a3f51",
|
"background_color": "#3a3f51",
|
||||||
"display": "minimal-ui"
|
"display": "standalone"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
|
||||||
|
|
||||||
// This file contains some helpers for power users in a browser console
|
|
||||||
|
|
||||||
let hasWarned = false;
|
|
||||||
|
|
||||||
function checkActivationWarning() {
|
|
||||||
if (!hasWarned) {
|
|
||||||
console.log('Activated ReadarrApi console helpers.');
|
|
||||||
console.warn('Be warned: There will be no further confirmation checks.');
|
|
||||||
hasWarned = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function attachAsyncActions(promise) {
|
|
||||||
promise.filter = function() {
|
|
||||||
const args = arguments;
|
|
||||||
const res = this.then((d) => d.filter(...args));
|
|
||||||
attachAsyncActions(res);
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
promise.map = function() {
|
|
||||||
const args = arguments;
|
|
||||||
const res = this.then((d) => d.map(...args));
|
|
||||||
attachAsyncActions(res);
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
promise.all = function() {
|
|
||||||
const res = this.then((d) => Promise.all(d));
|
|
||||||
attachAsyncActions(res);
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
promise.forEach = function(action) {
|
|
||||||
const res = this.then((d) => Promise.all(d.map(action)));
|
|
||||||
attachAsyncActions(res);
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class ResourceApi {
|
|
||||||
constructor(api, url) {
|
|
||||||
this.api = api;
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
single(id) {
|
|
||||||
return this.api.fetch(`${this.url}/${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
all() {
|
|
||||||
return this.api.fetch(this.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
filter(pred) {
|
|
||||||
return this.all().filter(pred);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(resource) {
|
|
||||||
return this.api.fetch(`${this.url}/${resource.id}`, { method: 'PUT', data: resource });
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(resource) {
|
|
||||||
if (typeof resource === 'object' && resource !== null && resource.id) {
|
|
||||||
resource = resource.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!resource || !Number.isInteger(resource)) {
|
|
||||||
throw Error('Invalid resource', resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.api.fetch(`${this.url}/${resource}`, { method: 'DELETE' });
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(url, options) {
|
|
||||||
return this.api.fetch(`${this.url}${url}`, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConsoleApi {
|
|
||||||
constructor() {
|
|
||||||
this.author = new ResourceApi(this, '/author');
|
|
||||||
}
|
|
||||||
|
|
||||||
resource(url) {
|
|
||||||
return new ResourceApi(this, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(url, options) {
|
|
||||||
checkActivationWarning();
|
|
||||||
|
|
||||||
options = options || {};
|
|
||||||
|
|
||||||
const req = {
|
|
||||||
url,
|
|
||||||
method: options.method || 'GET'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (options.data) {
|
|
||||||
req.dataType = 'json';
|
|
||||||
req.data = JSON.stringify(options.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const promise = createAjaxRequest(req).request;
|
|
||||||
|
|
||||||
promise.fail((xhr) => {
|
|
||||||
console.error(`Failed to fetch ${url}`, xhr);
|
|
||||||
});
|
|
||||||
|
|
||||||
attachAsyncActions(promise);
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.ReadarrApi = new ConsoleApi();
|
|
||||||
|
|
||||||
export default ConsoleApi;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
enum TooltipPosition {
|
|
||||||
Top = 'top',
|
|
||||||
Right = 'right',
|
|
||||||
Bottom = 'bottom',
|
|
||||||
Left = 'left',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TooltipPosition;
|
|
||||||
@@ -6,7 +6,6 @@ export const BOOKSHELF = 'bookshelf';
|
|||||||
export const KEY_VALUE_LIST = 'keyValueList';
|
export const KEY_VALUE_LIST = 'keyValueList';
|
||||||
export const MONITOR_BOOKS_SELECT = 'monitorBooksSelect';
|
export const MONITOR_BOOKS_SELECT = 'monitorBooksSelect';
|
||||||
export const MONITOR_NEW_ITEMS_SELECT = 'monitorNewItemsSelect';
|
export const MONITOR_NEW_ITEMS_SELECT = 'monitorNewItemsSelect';
|
||||||
export const FLOAT = 'float';
|
|
||||||
export const NUMBER = 'number';
|
export const NUMBER = 'number';
|
||||||
export const OAUTH = 'oauth';
|
export const OAUTH = 'oauth';
|
||||||
export const PASSWORD = 'password';
|
export const PASSWORD = 'password';
|
||||||
@@ -35,7 +34,6 @@ export const all = [
|
|||||||
KEY_VALUE_LIST,
|
KEY_VALUE_LIST,
|
||||||
MONITOR_BOOKS_SELECT,
|
MONITOR_BOOKS_SELECT,
|
||||||
MONITOR_NEW_ITEMS_SELECT,
|
MONITOR_NEW_ITEMS_SELECT,
|
||||||
FLOAT,
|
|
||||||
NUMBER,
|
NUMBER,
|
||||||
OAUTH,
|
OAUTH,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ const columns = [
|
|||||||
name: 'customFormats',
|
name: 'customFormats',
|
||||||
label: React.createElement(Icon, {
|
label: React.createElement(Icon, {
|
||||||
name: icons.INTERACTIVE,
|
name: icons.INTERACTIVE,
|
||||||
title: () => translate('CustomFormat')
|
title: translate('CustomFormat')
|
||||||
}),
|
}),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
@@ -91,9 +91,9 @@ const filterExistingFilesOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const importModeOptions = [
|
const importModeOptions = [
|
||||||
{ key: 'chooseImportMode', value: () => translate('ChooseImportMethod'), disabled: true },
|
{ key: 'chooseImportMode', value: translate('ChooseImportMethod'), disabled: true },
|
||||||
{ key: 'move', value: () => translate('MoveFiles') },
|
{ key: 'move', value: translate('MoveFiles') },
|
||||||
{ key: 'copy', value: () => translate('HardlinkCopyFiles') }
|
{ key: 'copy', value: translate('HardlinkCopyFiles') }
|
||||||
];
|
];
|
||||||
|
|
||||||
const SELECT = 'select';
|
const SELECT = 'select';
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import Table from 'Components/Table/Table';
|
import Table from 'Components/Table/Table';
|
||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import { icons, kinds, sortDirections } from 'Helpers/Props';
|
import { icons, sortDirections } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import InteractiveSearchRow from './InteractiveSearchRow';
|
import InteractiveSearchRow from './InteractiveSearchRow';
|
||||||
import styles from './InteractiveSearch.css';
|
import styles from './InteractiveSearch.css';
|
||||||
@@ -57,7 +56,7 @@ const columns = [
|
|||||||
name: 'customFormatScore',
|
name: 'customFormatScore',
|
||||||
label: React.createElement(Icon, {
|
label: React.createElement(Icon, {
|
||||||
name: icons.SCORE,
|
name: icons.SCORE,
|
||||||
title: () => translate('CustomFormatScore')
|
title: translate('CustomFormatScore')
|
||||||
}),
|
}),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
@@ -113,17 +112,17 @@ function InteractiveSearch(props) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && isPopulated && !totalReleasesCount ?
|
!isFetching && isPopulated && !totalReleasesCount ?
|
||||||
<Alert kind={kinds.INFO}>
|
<div className={styles.blankpad}>
|
||||||
{translate('NoResultsFound')}
|
No results found
|
||||||
</Alert> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!!totalReleasesCount && isPopulated && !items.length ?
|
!!totalReleasesCount && isPopulated && !items.length ?
|
||||||
<Alert kind={kinds.WARNING}>
|
<div className={styles.blankpad}>
|
||||||
{translate('AllResultsAreHiddenByTheAppliedFilter')}
|
All results are hidden by the applied filter
|
||||||
</Alert> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +157,7 @@ function InteractiveSearch(props) {
|
|||||||
{
|
{
|
||||||
totalReleasesCount !== items.length && !!items.length ?
|
totalReleasesCount !== items.length && !!items.length ?
|
||||||
<div className={styles.filteredMessage}>
|
<div className={styles.filteredMessage}>
|
||||||
{translate('SomeResultsAreHiddenByTheAppliedFilter')}
|
Some results are hidden by the applied filter
|
||||||
</div> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,18 +32,6 @@ function getDownloadIcon(isGrabbing, isGrabbed, grabError) {
|
|||||||
return icons.DOWNLOAD;
|
return icons.DOWNLOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDownloadKind(isGrabbed, grabError, downloadAllowed) {
|
|
||||||
if (isGrabbed) {
|
|
||||||
return kinds.SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (grabError || !downloadAllowed) {
|
|
||||||
return kinds.DANGER;
|
|
||||||
}
|
|
||||||
|
|
||||||
return kinds.DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDownloadTooltip(isGrabbing, isGrabbed, grabError) {
|
function getDownloadTooltip(isGrabbing, isGrabbed, grabError) {
|
||||||
if (isGrabbing) {
|
if (isGrabbing) {
|
||||||
return '';
|
return '';
|
||||||
@@ -224,7 +212,7 @@ class InteractiveSearchRow extends Component {
|
|||||||
{
|
{
|
||||||
<SpinnerIconButton
|
<SpinnerIconButton
|
||||||
name={getDownloadIcon(isGrabbing, isGrabbed, grabError)}
|
name={getDownloadIcon(isGrabbing, isGrabbed, grabError)}
|
||||||
kind={getDownloadKind(isGrabbed, grabError, downloadAllowed)}
|
kind={grabError || !downloadAllowed ? kinds.DANGER : kinds.DEFAULT}
|
||||||
title={getDownloadTooltip(isGrabbing, isGrabbed, grabError)}
|
title={getDownloadTooltip(isGrabbing, isGrabbed, grabError)}
|
||||||
isSpinning={isGrabbing}
|
isSpinning={isGrabbing}
|
||||||
onPress={downloadAllowed ? this.onGrabPress : this.onConfirmGrabPress}
|
onPress={downloadAllowed ? this.onGrabPress : this.onConfirmGrabPress}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class CustomFormat extends Component {
|
|||||||
isOpen={this.state.isDeleteCustomFormatModalOpen}
|
isOpen={this.state.isDeleteCustomFormatModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteCustomFormat')}
|
title={translate('DeleteCustomFormat')}
|
||||||
message={translate('DeleteCustomFormatMessageText', { name })}
|
message={translate('DeleteCustomFormatMessageText', [name])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
isSpinning={isDeleting}
|
isSpinning={isDeleting}
|
||||||
onConfirm={this.onConfirmDeleteCustomFormat}
|
onConfirm={this.onConfirmDeleteCustomFormat}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class Specification extends Component {
|
|||||||
isOpen={this.state.isDeleteSpecificationModalOpen}
|
isOpen={this.state.isDeleteSpecificationModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteCondition')}
|
title={translate('DeleteCondition')}
|
||||||
message={translate('DeleteConditionMessageText', { name })}
|
message={translate('DeleteConditionMessageText', [name])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={this.onConfirmDeleteSpecification}
|
onConfirm={this.onConfirmDeleteSpecification}
|
||||||
onCancel={this.onDeleteSpecificationModalClose}
|
onCancel={this.onDeleteSpecificationModalClose}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class DownloadClient extends Component {
|
|||||||
isOpen={this.state.isDeleteDownloadClientModalOpen}
|
isOpen={this.state.isDeleteDownloadClientModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteDownloadClient')}
|
title={translate('DeleteDownloadClient')}
|
||||||
message={translate('DeleteDownloadClientMessageText', { name })}
|
message={translate('DeleteDownloadClientMessageText', [name])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={this.onConfirmDeleteDownloadClient}
|
onConfirm={this.onConfirmDeleteDownloadClient}
|
||||||
onCancel={this.onDeleteDownloadClientModalClose}
|
onCancel={this.onDeleteDownloadClientModalClose}
|
||||||
|
|||||||
+4
-20
@@ -27,25 +27,9 @@ interface ManageDownloadClientsEditModalContentProps {
|
|||||||
const NO_CHANGE = 'noChange';
|
const NO_CHANGE = 'noChange';
|
||||||
|
|
||||||
const enableOptions = [
|
const enableOptions = [
|
||||||
{
|
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
|
||||||
key: NO_CHANGE,
|
{ key: 'enabled', value: translate('Enabled') },
|
||||||
get value() {
|
{ key: 'disabled', value: translate('Disabled') },
|
||||||
return translate('NoChange');
|
|
||||||
},
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'enabled',
|
|
||||||
get value() {
|
|
||||||
return translate('Enabled');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'disabled',
|
|
||||||
get value() {
|
|
||||||
return translate('Disabled');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function ManageDownloadClientsEditModalContent(
|
function ManageDownloadClientsEditModalContent(
|
||||||
@@ -180,7 +164,7 @@ function ManageDownloadClientsEditModalContent(
|
|||||||
|
|
||||||
<ModalFooter className={styles.modalFooter}>
|
<ModalFooter className={styles.modalFooter}>
|
||||||
<div className={styles.selected}>
|
<div className={styles.selected}>
|
||||||
{translate('CountDownloadClientsSelected', { selectedCount })}
|
{translate('CountDownloadClientsSelected', [selectedCount])}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
+9
-9
@@ -36,37 +36,37 @@ type OnSelectedChangeCallback = React.ComponentProps<
|
|||||||
const COLUMNS = [
|
const COLUMNS = [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: () => translate('Name'),
|
label: translate('Name'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'implementation',
|
name: 'implementation',
|
||||||
label: () => translate('Implementation'),
|
label: translate('Implementation'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enable',
|
name: 'enable',
|
||||||
label: () => translate('Enabled'),
|
label: translate('Enabled'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'priority',
|
name: 'priority',
|
||||||
label: () => translate('Priority'),
|
label: translate('Priority'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'removeCompletedDownloads',
|
name: 'removeCompletedDownloads',
|
||||||
label: () => translate('RemoveCompleted'),
|
label: translate('RemoveCompleted'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'removeFailedDownloads',
|
name: 'removeFailedDownloads',
|
||||||
label: () => translate('RemoveFailed'),
|
label: translate('RemoveFailed'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
@@ -286,9 +286,9 @@ function ManageDownloadClientsModalContent(
|
|||||||
isOpen={isDeleteModalOpen}
|
isOpen={isDeleteModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteSelectedDownloadClients')}
|
title={translate('DeleteSelectedDownloadClients')}
|
||||||
message={translate('DeleteSelectedDownloadClientsMessageText', {
|
message={translate('DeleteSelectedDownloadClientsMessageText', [
|
||||||
count: selectedIds.length,
|
selectedIds.length,
|
||||||
})}
|
])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={onConfirmDelete}
|
onConfirm={onConfirmDelete}
|
||||||
onCancel={onDeleteModalClose}
|
onCancel={onDeleteModalClose}
|
||||||
|
|||||||
+3
-18
@@ -72,24 +72,9 @@ function TagsModalContent(props: TagsModalContentProps) {
|
|||||||
}, [tags, applyTags, onApplyTagsPress]);
|
}, [tags, applyTags, onApplyTagsPress]);
|
||||||
|
|
||||||
const applyTagsOptions = [
|
const applyTagsOptions = [
|
||||||
{
|
{ key: 'add', value: translate('Add') },
|
||||||
key: 'add',
|
{ key: 'remove', value: translate('Remove') },
|
||||||
get value() {
|
{ key: 'replace', value: translate('Replace') },
|
||||||
return translate('Add');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'remove',
|
|
||||||
get value() {
|
|
||||||
return translate('Remove');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'replace',
|
|
||||||
get value() {
|
|
||||||
return translate('Replace');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
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 FieldSet from 'Components/FieldSet';
|
import FieldSet from 'Components/FieldSet';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
|
||||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import EditRemotePathMappingModalConnector from './EditRemotePathMappingModalConnector';
|
import EditRemotePathMappingModalConnector from './EditRemotePathMappingModalConnector';
|
||||||
import RemotePathMapping from './RemotePathMapping';
|
import RemotePathMapping from './RemotePathMapping';
|
||||||
@@ -52,11 +50,6 @@ class RemotePathMappings extends Component {
|
|||||||
errorMessage={translate('UnableToLoadRemotePathMappings')}
|
errorMessage={translate('UnableToLoadRemotePathMappings')}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Alert kind={kinds.INFO}>
|
|
||||||
<InlineMarkdown data={translate('RemotePathMappingsInfo', { app: 'Readarr', wikiLink: 'https://wiki.servarr.com/readarr/settings#remote-path-mappings' })} />
|
|
||||||
</Alert>
|
|
||||||
|
|
||||||
<div className={styles.remotePathMappingsHeader}>
|
<div className={styles.remotePathMappingsHeader}>
|
||||||
<div className={styles.host}>
|
<div className={styles.host}>
|
||||||
{translate('Host')}
|
{translate('Host')}
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ class GeneralSettings extends Component {
|
|||||||
isResettingApiKey,
|
isResettingApiKey,
|
||||||
isWindows,
|
isWindows,
|
||||||
isWindowsService,
|
isWindowsService,
|
||||||
|
isDocker,
|
||||||
mode,
|
mode,
|
||||||
packageUpdateMechanism,
|
packageUpdateMechanism,
|
||||||
onInputChange,
|
onInputChange,
|
||||||
@@ -170,6 +171,7 @@ class GeneralSettings extends Component {
|
|||||||
settings={settings}
|
settings={settings}
|
||||||
isWindows={isWindows}
|
isWindows={isWindows}
|
||||||
packageUpdateMechanism={packageUpdateMechanism}
|
packageUpdateMechanism={packageUpdateMechanism}
|
||||||
|
isDocker={isDocker}
|
||||||
onInputChange={onInputChange}
|
onInputChange={onInputChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -212,6 +214,7 @@ GeneralSettings.propTypes = {
|
|||||||
hasSettings: PropTypes.bool.isRequired,
|
hasSettings: PropTypes.bool.isRequired,
|
||||||
isWindows: PropTypes.bool.isRequired,
|
isWindows: PropTypes.bool.isRequired,
|
||||||
isWindowsService: PropTypes.bool.isRequired,
|
isWindowsService: PropTypes.bool.isRequired,
|
||||||
|
isDocker: PropTypes.bool.isRequired,
|
||||||
mode: PropTypes.string.isRequired,
|
mode: PropTypes.string.isRequired,
|
||||||
packageUpdateMechanism: PropTypes.string.isRequired,
|
packageUpdateMechanism: PropTypes.string.isRequired,
|
||||||
onInputChange: PropTypes.func.isRequired,
|
onInputChange: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ function createMapStateToProps() {
|
|||||||
isResettingApiKey,
|
isResettingApiKey,
|
||||||
isWindows: systemStatus.isWindows,
|
isWindows: systemStatus.isWindows,
|
||||||
isWindowsService: systemStatus.isWindows && systemStatus.mode === 'service',
|
isWindowsService: systemStatus.isWindows && systemStatus.mode === 'service',
|
||||||
|
isDocker: systemStatus.isDocker,
|
||||||
mode: systemStatus.mode,
|
mode: systemStatus.mode,
|
||||||
packageUpdateMechanism: systemStatus.packageUpdateMechanism,
|
packageUpdateMechanism: systemStatus.packageUpdateMechanism,
|
||||||
...sectionSettings
|
...sectionSettings
|
||||||
|
|||||||
@@ -8,17 +8,12 @@ import { inputTypes, sizes } from 'Helpers/Props';
|
|||||||
import titleCase from 'Utilities/String/titleCase';
|
import titleCase from 'Utilities/String/titleCase';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const branchValues = [
|
|
||||||
'master',
|
|
||||||
'develop',
|
|
||||||
'nightly'
|
|
||||||
];
|
|
||||||
|
|
||||||
function UpdateSettings(props) {
|
function UpdateSettings(props) {
|
||||||
const {
|
const {
|
||||||
advancedSettings,
|
advancedSettings,
|
||||||
settings,
|
settings,
|
||||||
isWindows,
|
isWindows,
|
||||||
|
isDocker,
|
||||||
packageUpdateMechanism,
|
packageUpdateMechanism,
|
||||||
onInputChange
|
onInputChange
|
||||||
} = props;
|
} = props;
|
||||||
@@ -49,21 +44,32 @@ function UpdateSettings(props) {
|
|||||||
|
|
||||||
updateOptions.push({ key: 'script', value: 'Script' });
|
updateOptions.push({ key: 'script', value: 'Script' });
|
||||||
|
|
||||||
|
if (isDocker) {
|
||||||
|
return (
|
||||||
|
<FieldSet legend={translate('Updates')}>
|
||||||
|
<div>
|
||||||
|
{translate('UpdatingIsDisabledInsideADockerContainerUpdateTheContainerImageInstead')}
|
||||||
|
</div>
|
||||||
|
</FieldSet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldSet legend={translate('Updates')}>
|
<FieldSet legend={translate('Updates')}>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
advancedSettings={advancedSettings}
|
advancedSettings={advancedSettings}
|
||||||
isAdvanced={true}
|
isAdvanced={true}
|
||||||
>
|
>
|
||||||
<FormLabel>{translate('Branch')}</FormLabel>
|
<FormLabel>
|
||||||
|
{translate('Branch')}
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.AUTO_COMPLETE}
|
type={inputTypes.TEXT}
|
||||||
name="branch"
|
name="branch"
|
||||||
helpText={usingExternalUpdateMechanism ? translate('UsingExternalUpdateMechanismBranchUsedByExternalUpdateMechanism') : translate('UsingExternalUpdateMechanismBranchToUseToUpdateReadarr')}
|
helpText={usingExternalUpdateMechanism ? translate('UsingExternalUpdateMechanismBranchUsedByExternalUpdateMechanism') : translate('UsingExternalUpdateMechanismBranchToUseToUpdateReadarr')}
|
||||||
helpLink="https://wiki.servarr.com/readarr/faq#how-do-I-update-my-readarr"
|
helpLink="https://wiki.servarr.com/readarr/faq#how-do-I-update-my-readarr"
|
||||||
{...branch}
|
{...branch}
|
||||||
values={branchValues}
|
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
readOnly={usingExternalUpdateMechanism}
|
readOnly={usingExternalUpdateMechanism}
|
||||||
/>
|
/>
|
||||||
@@ -77,13 +83,14 @@ function UpdateSettings(props) {
|
|||||||
isAdvanced={true}
|
isAdvanced={true}
|
||||||
size={sizes.MEDIUM}
|
size={sizes.MEDIUM}
|
||||||
>
|
>
|
||||||
<FormLabel>{translate('Automatic')}</FormLabel>
|
<FormLabel>
|
||||||
|
{translate('Automatic')}
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="updateAutomatically"
|
name="updateAutomatically"
|
||||||
helpText={translate('UpdateAutomaticallyHelpText')}
|
helpText={translate('UpdateAutomaticallyHelpText')}
|
||||||
helpTextWarning={updateMechanism.value === 'docker' ? translate('AutomaticUpdatesDisabledDocker', { appName: 'Readarr' }) : undefined}
|
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...updateAutomatically}
|
{...updateAutomatically}
|
||||||
/>
|
/>
|
||||||
@@ -93,7 +100,9 @@ function UpdateSettings(props) {
|
|||||||
advancedSettings={advancedSettings}
|
advancedSettings={advancedSettings}
|
||||||
isAdvanced={true}
|
isAdvanced={true}
|
||||||
>
|
>
|
||||||
<FormLabel>{translate('Mechanism')}</FormLabel>
|
<FormLabel>
|
||||||
|
{translate('Mechanism')}
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.SELECT}
|
type={inputTypes.SELECT}
|
||||||
@@ -112,7 +121,9 @@ function UpdateSettings(props) {
|
|||||||
advancedSettings={advancedSettings}
|
advancedSettings={advancedSettings}
|
||||||
isAdvanced={true}
|
isAdvanced={true}
|
||||||
>
|
>
|
||||||
<FormLabel>{translate('ScriptPath')}</FormLabel>
|
<FormLabel>
|
||||||
|
{translate('ScriptPath')}
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.TEXT}
|
type={inputTypes.TEXT}
|
||||||
@@ -133,6 +144,7 @@ UpdateSettings.propTypes = {
|
|||||||
advancedSettings: PropTypes.bool.isRequired,
|
advancedSettings: PropTypes.bool.isRequired,
|
||||||
settings: PropTypes.object.isRequired,
|
settings: PropTypes.object.isRequired,
|
||||||
isWindows: PropTypes.bool.isRequired,
|
isWindows: PropTypes.bool.isRequired,
|
||||||
|
isDocker: PropTypes.bool.isRequired,
|
||||||
packageUpdateMechanism: PropTypes.string.isRequired,
|
packageUpdateMechanism: PropTypes.string.isRequired,
|
||||||
onInputChange: PropTypes.func.isRequired
|
onInputChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,13 +8,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
@add-mixin truncate;
|
flex: 1 0 300px;
|
||||||
|
|
||||||
flex: 0 1 600px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.foreignId {
|
.foreignId {
|
||||||
flex: 0 0 100px;
|
flex: 0 0 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.foreignId {
|
||||||
flex: 0 1 600px;
|
flex: 0 0 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.foreignId {
|
.name {
|
||||||
flex: 0 0 100px;
|
flex: 1 0 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addImportListExclusion {
|
.addImportListExclusion {
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class ImportList extends Component {
|
|||||||
isOpen={this.state.isDeleteImportListModalOpen}
|
isOpen={this.state.isDeleteImportListModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteImportList')}
|
title={translate('DeleteImportList')}
|
||||||
message={translate('DeleteImportListMessageText', { name })}
|
message={translate('DeleteImportListMessageText', [name])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={this.onConfirmDeleteImportList}
|
onConfirm={this.onConfirmDeleteImportList}
|
||||||
onCancel={this.onDeleteImportListModalClose}
|
onCancel={this.onDeleteImportListModalClose}
|
||||||
|
|||||||
+4
-20
@@ -27,25 +27,9 @@ interface ManageImportListsEditModalContentProps {
|
|||||||
const NO_CHANGE = 'noChange';
|
const NO_CHANGE = 'noChange';
|
||||||
|
|
||||||
const autoAddOptions = [
|
const autoAddOptions = [
|
||||||
{
|
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
|
||||||
key: NO_CHANGE,
|
{ key: 'enabled', value: translate('Enabled') },
|
||||||
get value() {
|
{ key: 'disabled', value: translate('Disabled') },
|
||||||
return translate('NoChange');
|
|
||||||
},
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'enabled',
|
|
||||||
get value() {
|
|
||||||
return translate('Enabled');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'disabled',
|
|
||||||
get value() {
|
|
||||||
return translate('Disabled');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function ManageImportListsEditModalContent(
|
function ManageImportListsEditModalContent(
|
||||||
@@ -184,7 +168,7 @@ function ManageImportListsEditModalContent(
|
|||||||
|
|
||||||
<ModalFooter className={styles.modalFooter}>
|
<ModalFooter className={styles.modalFooter}>
|
||||||
<div className={styles.selected}>
|
<div className={styles.selected}>
|
||||||
{translate('CountImportListsSelected', { selectedCount })}
|
{translate('CountImportListsSelected', [selectedCount])}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
+10
-10
@@ -36,43 +36,43 @@ type OnSelectedChangeCallback = React.ComponentProps<
|
|||||||
const COLUMNS = [
|
const COLUMNS = [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: () => translate('Name'),
|
label: translate('Name'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'implementation',
|
name: 'implementation',
|
||||||
label: () => translate('Implementation'),
|
label: translate('Implementation'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'qualityProfileId',
|
name: 'qualityProfileId',
|
||||||
label: () => translate('QualityProfile'),
|
label: translate('QualityProfile'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'metadataProfileId',
|
name: 'metadataProfileId',
|
||||||
label: () => translate('MetadataProfile'),
|
label: translate('MetadataProfile'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'rootFolderPath',
|
name: 'rootFolderPath',
|
||||||
label: () => translate('RootFolder'),
|
label: translate('RootFolder'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enableAutomaticAdd',
|
name: 'enableAutomaticAdd',
|
||||||
label: () => translate('AutoAdd'),
|
label: translate('AutoAdd'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'tags',
|
name: 'tags',
|
||||||
label: () => translate('Tags'),
|
label: translate('Tags'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
@@ -283,9 +283,9 @@ function ManageImportListsModalContent(
|
|||||||
isOpen={isDeleteModalOpen}
|
isOpen={isDeleteModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteSelectedImportLists')}
|
title={translate('DeleteSelectedImportLists')}
|
||||||
message={translate('DeleteSelectedImportListsMessageText', {
|
message={translate('DeleteSelectedImportListsMessageText', [
|
||||||
count: selectedIds.length,
|
selectedIds.length,
|
||||||
})}
|
])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={onConfirmDelete}
|
onConfirm={onConfirmDelete}
|
||||||
onCancel={onDeleteModalClose}
|
onCancel={onDeleteModalClose}
|
||||||
|
|||||||
@@ -70,24 +70,9 @@ function TagsModalContent(props: TagsModalContentProps) {
|
|||||||
}, [tags, applyTags, onApplyTagsPress]);
|
}, [tags, applyTags, onApplyTagsPress]);
|
||||||
|
|
||||||
const applyTagsOptions = [
|
const applyTagsOptions = [
|
||||||
{
|
{ key: 'add', value: translate('Add') },
|
||||||
key: 'add',
|
{ key: 'remove', value: translate('Remove') },
|
||||||
get value() {
|
{ key: 'replace', value: translate('Replace') },
|
||||||
return translate('Add');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'remove',
|
|
||||||
get value() {
|
|
||||||
return translate('Remove');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'replace',
|
|
||||||
get value() {
|
|
||||||
return translate('Replace');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class Indexer extends Component {
|
|||||||
isOpen={this.state.isDeleteIndexerModalOpen}
|
isOpen={this.state.isDeleteIndexerModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteIndexer')}
|
title={translate('DeleteIndexer')}
|
||||||
message={translate('DeleteIndexerMessageText', { name })}
|
message={translate('DeleteIndexerMessageText', [name])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={this.onConfirmDeleteIndexer}
|
onConfirm={this.onConfirmDeleteIndexer}
|
||||||
onCancel={this.onDeleteIndexerModalClose}
|
onCancel={this.onDeleteIndexerModalClose}
|
||||||
|
|||||||
+4
-20
@@ -27,25 +27,9 @@ interface ManageIndexersEditModalContentProps {
|
|||||||
const NO_CHANGE = 'noChange';
|
const NO_CHANGE = 'noChange';
|
||||||
|
|
||||||
const enableOptions = [
|
const enableOptions = [
|
||||||
{
|
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
|
||||||
key: NO_CHANGE,
|
{ key: 'enabled', value: translate('Enabled') },
|
||||||
get value() {
|
{ key: 'disabled', value: translate('Disabled') },
|
||||||
return translate('NoChange');
|
|
||||||
},
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'enabled',
|
|
||||||
get value() {
|
|
||||||
return translate('Enabled');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'disabled',
|
|
||||||
get value() {
|
|
||||||
return translate('Disabled');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function ManageIndexersEditModalContent(
|
function ManageIndexersEditModalContent(
|
||||||
@@ -178,7 +162,7 @@ function ManageIndexersEditModalContent(
|
|||||||
|
|
||||||
<ModalFooter className={styles.modalFooter}>
|
<ModalFooter className={styles.modalFooter}>
|
||||||
<div className={styles.selected}>
|
<div className={styles.selected}>
|
||||||
{translate('CountIndexersSelected', { selectedCount })}
|
{translate('CountIndexersSelected', [selectedCount])}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -36,43 +36,43 @@ type OnSelectedChangeCallback = React.ComponentProps<
|
|||||||
const COLUMNS = [
|
const COLUMNS = [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: () => translate('Name'),
|
label: translate('Name'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'implementation',
|
name: 'implementation',
|
||||||
label: () => translate('Implementation'),
|
label: translate('Implementation'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enableRss',
|
name: 'enableRss',
|
||||||
label: () => translate('EnableRSS'),
|
label: translate('EnableRSS'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enableAutomaticSearch',
|
name: 'enableAutomaticSearch',
|
||||||
label: () => translate('EnableAutomaticSearch'),
|
label: translate('EnableAutomaticSearch'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enableInteractiveSearch',
|
name: 'enableInteractiveSearch',
|
||||||
label: () => translate('EnableInteractiveSearch'),
|
label: translate('EnableInteractiveSearch'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'priority',
|
name: 'priority',
|
||||||
label: () => translate('Priority'),
|
label: translate('Priority'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'tags',
|
name: 'tags',
|
||||||
label: () => translate('Tags'),
|
label: translate('Tags'),
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
},
|
},
|
||||||
@@ -281,9 +281,9 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
|
|||||||
isOpen={isDeleteModalOpen}
|
isOpen={isDeleteModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteSelectedIndexers')}
|
title={translate('DeleteSelectedIndexers')}
|
||||||
message={translate('DeleteSelectedIndexersMessageText', {
|
message={translate('DeleteSelectedIndexersMessageText', [
|
||||||
count: selectedIds.length,
|
selectedIds.length,
|
||||||
})}
|
])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={onConfirmDelete}
|
onConfirm={onConfirmDelete}
|
||||||
onCancel={onDeleteModalClose}
|
onCancel={onDeleteModalClose}
|
||||||
|
|||||||
@@ -70,24 +70,9 @@ function TagsModalContent(props: TagsModalContentProps) {
|
|||||||
}, [tags, applyTags, onApplyTagsPress]);
|
}, [tags, applyTags, onApplyTagsPress]);
|
||||||
|
|
||||||
const applyTagsOptions = [
|
const applyTagsOptions = [
|
||||||
{
|
{ key: 'add', value: translate('Add') },
|
||||||
key: 'add',
|
{ key: 'remove', value: translate('Remove') },
|
||||||
get value() {
|
{ key: 'replace', value: translate('Replace') },
|
||||||
return translate('Add');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'remove',
|
|
||||||
get value() {
|
|
||||||
return translate('Remove');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'replace',
|
|
||||||
get value() {
|
|
||||||
return translate('Replace');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -212,24 +212,26 @@ class MediaManagement extends Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
{
|
{
|
||||||
settings.importExtraFiles.value ?
|
settings.importExtraFiles.value &&
|
||||||
<FormGroup
|
<FormGroup
|
||||||
advancedSettings={advancedSettings}
|
advancedSettings={advancedSettings}
|
||||||
isAdvanced={true}
|
isAdvanced={true}
|
||||||
>
|
>
|
||||||
<FormLabel>{translate('ImportExtraFiles')}</FormLabel>
|
<FormLabel>
|
||||||
|
{translate('ImportExtraFiles')}
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.TEXT}
|
type={inputTypes.TEXT}
|
||||||
name="extraFileExtensions"
|
name="extraFileExtensions"
|
||||||
helpTexts={[
|
helpTexts={[
|
||||||
translate('ExtraFileExtensionsHelpText'),
|
translate('ExtraFileExtensionsHelpTexts1'),
|
||||||
translate('ExtraFileExtensionsHelpTextsExamples')
|
translate('ExtraFileExtensionsHelpTexts2')
|
||||||
]}
|
]}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...settings.extraFileExtensions}
|
{...settings.extraFileExtensions}
|
||||||
/>
|
/>
|
||||||
</FormGroup> : null
|
</FormGroup>
|
||||||
}
|
}
|
||||||
</FieldSet>
|
</FieldSet>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class RootFolder extends Component {
|
|||||||
isOpen={this.state.isDeleteRootFolderModalOpen}
|
isOpen={this.state.isDeleteRootFolderModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteRootFolder')}
|
title={translate('DeleteRootFolder')}
|
||||||
message={translate('DeleteRootFolderMessageText', { name })}
|
message={translate('DeleteRootFolderMessageText', [name])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={this.onConfirmDeleteRootFolder}
|
onConfirm={this.onConfirmDeleteRootFolder}
|
||||||
onCancel={this.onDeleteRootFolderModalClose}
|
onCancel={this.onDeleteRootFolderModalClose}
|
||||||
|
|||||||
@@ -11,51 +11,16 @@ import { inputTypes, kinds } from 'Helpers/Props';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const writeAudioTagOptions = [
|
const writeAudioTagOptions = [
|
||||||
{
|
{ key: 'no', value: translate('WriteTagsNo') },
|
||||||
key: 'no',
|
{ key: 'sync', value: translate('WriteTagsSync') },
|
||||||
get value() {
|
{ key: 'allFiles', value: translate('WriteTagsAll') },
|
||||||
return translate('WriteTagsNo');
|
{ key: 'newFiles', value: translate('WriteTagsNew') }
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'sync',
|
|
||||||
get value() {
|
|
||||||
return translate('WriteTagsSync');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'allFiles',
|
|
||||||
get value() {
|
|
||||||
return translate('WriteTagsAll');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'newFiles',
|
|
||||||
get value() {
|
|
||||||
return translate('WriteTagsNew');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const writeBookTagOptions = [
|
const writeBookTagOptions = [
|
||||||
{
|
{ key: 'sync', value: translate('WriteTagsSync') },
|
||||||
key: 'sync',
|
{ key: 'allFiles', value: translate('WriteTagsAll') },
|
||||||
get value() {
|
{ key: 'newFiles', value: translate('WriteTagsNew') }
|
||||||
return translate('WriteTagsSync');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'allFiles',
|
|
||||||
get value() {
|
|
||||||
return translate('WriteTagsAll');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'newFiles',
|
|
||||||
get value() {
|
|
||||||
return translate('WriteTagsNew');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function MetadataProvider(props) {
|
function MetadataProvider(props) {
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ class Notification extends Component {
|
|||||||
onReleaseImport,
|
onReleaseImport,
|
||||||
onUpgrade,
|
onUpgrade,
|
||||||
onRename,
|
onRename,
|
||||||
onAuthorAdded,
|
|
||||||
onAuthorDelete,
|
onAuthorDelete,
|
||||||
onBookDelete,
|
onBookDelete,
|
||||||
onBookFileDelete,
|
onBookFileDelete,
|
||||||
@@ -74,7 +73,6 @@ class Notification extends Component {
|
|||||||
supportsOnReleaseImport,
|
supportsOnReleaseImport,
|
||||||
supportsOnUpgrade,
|
supportsOnUpgrade,
|
||||||
supportsOnRename,
|
supportsOnRename,
|
||||||
supportsOnAuthorAdded,
|
|
||||||
supportsOnAuthorDelete,
|
supportsOnAuthorDelete,
|
||||||
supportsOnBookDelete,
|
supportsOnBookDelete,
|
||||||
supportsOnBookFileDelete,
|
supportsOnBookFileDelete,
|
||||||
@@ -138,14 +136,6 @@ class Notification extends Component {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
supportsOnAuthorAdded && onAuthorAdded ?
|
|
||||||
<Label kind={kinds.SUCCESS}>
|
|
||||||
{translate('OnAuthorAdded')}
|
|
||||||
</Label> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
supportsOnAuthorDelete && onAuthorDelete ?
|
supportsOnAuthorDelete && onAuthorDelete ?
|
||||||
<Label kind={kinds.SUCCESS}>
|
<Label kind={kinds.SUCCESS}>
|
||||||
@@ -237,7 +227,7 @@ class Notification extends Component {
|
|||||||
isOpen={this.state.isDeleteNotificationModalOpen}
|
isOpen={this.state.isDeleteNotificationModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DeleteNotification')}
|
title={translate('DeleteNotification')}
|
||||||
message={translate('DeleteNotificationMessageText', { name })}
|
message={translate('DeleteNotificationMessageText', [name])}
|
||||||
confirmLabel={translate('Delete')}
|
confirmLabel={translate('Delete')}
|
||||||
onConfirm={this.onConfirmDeleteNotification}
|
onConfirm={this.onConfirmDeleteNotification}
|
||||||
onCancel={this.onDeleteNotificationModalClose}
|
onCancel={this.onDeleteNotificationModalClose}
|
||||||
@@ -254,7 +244,6 @@ Notification.propTypes = {
|
|||||||
onReleaseImport: PropTypes.bool.isRequired,
|
onReleaseImport: PropTypes.bool.isRequired,
|
||||||
onUpgrade: PropTypes.bool.isRequired,
|
onUpgrade: PropTypes.bool.isRequired,
|
||||||
onRename: PropTypes.bool.isRequired,
|
onRename: PropTypes.bool.isRequired,
|
||||||
onAuthorAdded: PropTypes.bool.isRequired,
|
|
||||||
onAuthorDelete: PropTypes.bool.isRequired,
|
onAuthorDelete: PropTypes.bool.isRequired,
|
||||||
onBookDelete: PropTypes.bool.isRequired,
|
onBookDelete: PropTypes.bool.isRequired,
|
||||||
onBookFileDelete: PropTypes.bool.isRequired,
|
onBookFileDelete: PropTypes.bool.isRequired,
|
||||||
@@ -268,7 +257,6 @@ Notification.propTypes = {
|
|||||||
supportsOnReleaseImport: PropTypes.bool.isRequired,
|
supportsOnReleaseImport: PropTypes.bool.isRequired,
|
||||||
supportsOnUpgrade: PropTypes.bool.isRequired,
|
supportsOnUpgrade: PropTypes.bool.isRequired,
|
||||||
supportsOnRename: PropTypes.bool.isRequired,
|
supportsOnRename: PropTypes.bool.isRequired,
|
||||||
supportsOnAuthorAdded: PropTypes.bool.isRequired,
|
|
||||||
supportsOnAuthorDelete: PropTypes.bool.isRequired,
|
supportsOnAuthorDelete: PropTypes.bool.isRequired,
|
||||||
supportsOnBookDelete: PropTypes.bool.isRequired,
|
supportsOnBookDelete: PropTypes.bool.isRequired,
|
||||||
supportsOnBookFileDelete: PropTypes.bool.isRequired,
|
supportsOnBookFileDelete: PropTypes.bool.isRequired,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user