Compare commits

..

1 Commits

Author SHA1 Message Date
ta264 dddcb89c5b Allow build script to produce packages on non-windows OSes
(cherry picked from commit 45ab2b2409fd11c556c1374aec8d27aec898c4fc)
2022-04-29 11:01:27 +00:00
1908 changed files with 16104 additions and 42833 deletions
+4 -13
View File
@@ -36,18 +36,9 @@ dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _ dotnet_naming_style.instance_field_style.required_prefix = _
# Prefer "var" everywhere # Prefer "var" everywhere
csharp_style_var_for_built_in_types = true csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true csharp_style_var_elsewhere = true:suggestion
# Prefer "out" variables to be declared inline
csharp_style_inlined_variable_declaration = true
# Using directive is unnecessary.
dotnet_diagnostic.IDE0005.severity = error
# Use var instead of explicit type
dotnet_diagnostic.IDE0007.severity = error
# Inline variable declaration
dotnet_diagnostic.IDE0018.severity = error
# Stylecop Rules # Stylecop Rules
dotnet_diagnostic.SA0001.severity = none dotnet_diagnostic.SA0001.severity = none
@@ -275,7 +266,7 @@ dotnet_diagnostic.CA5397.severity = suggestion
[*.{js,html,hbs,less,css,ts,tsx}] [*.{js,html,js,hbs,less,css}]
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
+9
View File
@@ -0,0 +1,9 @@
{
"paths": [
"frontend/src/**/*.js"
],
"ignored": [
"**/node_modules/**/*"
],
"port": 5004
}
+2 -11
View File
@@ -5,9 +5,9 @@ body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Is there an existing issue for this? label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the bug you encountered. If a bug exists and is closed note that it may only be fixed in an unstable branch. description: Please search to see if an issue already exists for the bug you encountered.
options: options:
- label: I have searched the existing open and closed issues - label: I have searched the existing issues
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
@@ -42,14 +42,12 @@ body:
- **Docker Install**: Yes - **Docker Install**: Yes
- **Using Reverse Proxy**: No - **Using Reverse Proxy**: No
- **Browser**: Firefox 90 (If UI related) - **Browser**: Firefox 90 (If UI related)
- **Database**: Sqlite 3.36.0
value: | value: |
- OS: - OS:
- Readarr: - Readarr:
- Docker Install: - Docker Install:
- Using Reverse Proxy: - Using Reverse Proxy:
- Browser: - Browser:
- Database:
render: markdown render: markdown
validations: validations:
required: true required: true
@@ -73,10 +71,3 @@ body:
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering! Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
validations: validations:
required: true required: true
- type: checkboxes
attributes:
label: Trace Logs have been provided as applicable. Reports may be closed if the required logs are not provided.
description: Trace logs are generally required for all bug reports and contain `trace`. Info logs are invalid for bug reports and do not contain `debug` nor `trace`
options:
- label: I have read and followed the steps in the wiki link above and provided the required trace logs - the logs contain `trace` - that are relevant and show this issue.
required: true
+2 -2
View File
@@ -5,9 +5,9 @@ body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Is there an existing issue for this? label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the feature you are requesting. If a request exists and is closed note that it may only be fixed in an unstable branch. description: Please search to see if an issue already exists for the feature you are requesting.
options: options:
- label: I have searched the existing open and closed issues - label: I have searched the existing issues
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
-28
View File
@@ -1,28 +0,0 @@
'Area: API':
- src/Readarr.Api.V1/**/*
'Area: Db-migration':
- src/NzbDrone.Core/Datastore/Migration/*
'Area: Download Clients':
- src/NzbDrone.Core/Download/Clients/**/*
'Area: Import Lists':
- src/NzbDrone.Core/ImportLists/**/*
'Area: Indexer':
- src/NzbDrone.Core/Indexers/**/*
'Area: Notifications':
- src/NzbDrone.Core/Notifications/**/*
'Area: Organizer':
- src/NzbDrone.Core/Organizer/**/*
'Area: Parser':
- src/NzbDrone.Core/Parser/**/*
'Area: UI':
- frontend/**/*
- package.json
- yarn.lock
+41
View File
@@ -0,0 +1,41 @@
name: Sync issue to Azure DevOps work item
on:
issues:
types:
[opened, edited, deleted, closed, reopened, labeled, unlabeled, assigned]
concurrency: azuresync-${{ github.event.issue.number }}
jobs:
alert:
runs-on: ubuntu-latest
steps:
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == true }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Readarr"
ado_wit: "Bug"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == false }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Readarr"
ado_wit: "User Story"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100
-12
View File
@@ -1,12 +0,0 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
+6 -6
View File
@@ -9,13 +9,13 @@ jobs:
lock: lock:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v4 - uses: dessant/lock-threads@v2
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}
issue-inactive-days: '90' issue-lock-inactive-days: '90'
exclude-issue-created-before: '' issue-exclude-created-before: ''
exclude-any-issue-labels: '' issue-exclude-labels: ''
add-issue-labels: '' issue-lock-labels: ''
issue-comment: '' issue-lock-comment: ''
issue-lock-reason: 'resolved' issue-lock-reason: 'resolved'
process-only: '' process-only: ''
+2 -13
View File
@@ -8,7 +8,7 @@ jobs:
support: support:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/support-requests@v3 - uses: dessant/support-requests@v2
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}
support-label: 'Type: Support' support-label: 'Type: Support'
@@ -18,15 +18,4 @@ jobs:
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) or [Subreddit](https://reddit.com/r/readarr)
close-issue: true close-issue: true
lock-issue: false lock-issue: false
- uses: dessant/support-requests@v3
with:
github-token: ${{ github.token }}
support-label: 'Status: Logs Needed'
issue-comment: >
:wave: @{issue-author}, In order to help you further we'll need to see logs.
You'll need to enable trace logging and replicate the problem that you encountered.
Guidance on how to enable trace logging can be found in
our [troubleshooting guide](https://wiki.servarr.com/readarr/troubleshooting#logging-and-log-files).
close-issue: false
lock-issue: false
-3
View File
@@ -148,6 +148,3 @@ _temp_*/**/*
## Merge any idea folder ## Merge any idea folder
*/**/.idea */**/.idea
*.iml *.iml
# API doc generation
.config/
-132
View File
@@ -1,132 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<development@readarr.com>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
-9
View File
@@ -62,15 +62,6 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
[![Mega Sponsors List](https://opencollective.com/Readarr/tiers/mega-sponsor.svg?width=890)](https://opencollective.com/readarr#mega-sponsor) [![Mega Sponsors List](https://opencollective.com/Readarr/tiers/mega-sponsor.svg?width=890)](https://opencollective.com/readarr#mega-sponsor)
## DigitalOcean
This project is also supported by DigitalOcean
<p>
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
### License ### License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) * [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
-8
View File
@@ -1,8 +0,0 @@
# Security Policy
## Reporting a Vulnerability
Please report (suspected) security vulnerabilities on Discord (preferred) to
any of the Servarr Dev role holders (red names) or via email: development@servarr.com. You will receive a response from
us within 72 hours. If the issue is confirmed, we will release a patch as soon
as possible depending on complexity/severity.
+73 -265
View File
@@ -9,14 +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.2.4' majorVersion: '0.1.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.408' dotnetVersion: '6.0.201'
nodeVersion: '16.X'
innoVersion: '6.2.0' innoVersion: '6.2.0'
windowsImage: 'windows-2022' windowsImage: 'windows-2022'
linuxImage: 'ubuntu-20.04' linuxImage: 'ubuntu-20.04'
@@ -67,13 +66,16 @@ stages:
inputs: inputs:
version: $(dotnetVersion) version: $(dotnetVersion)
- bash: | - bash: |
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}" BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props" echo $BUNDLEDVERSIONS
grep osx-x64 $BUNDLEDVERSIONS
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS echo "BSD already enabled"
else
echo "Enabling BSD support"
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64/' $BUNDLEDVERSIONS
fi fi
displayName: Extra Platform Support displayName: Enable FreeBSD Support
- task: Cache@2 - task: Cache@2
inputs: inputs:
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props' key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
@@ -85,27 +87,29 @@ stages:
NUGET_PACKAGES: $(nugetCacheFolder) NUGET_PACKAGES: $(nugetCacheFolder)
- powershell: Get-ChildItem _output\net6.0*,_output\*.Update\* -Recurse | Where { $_.Fullname -notlike "*\publish\*" -and $_.attributes -notlike "*directory*" } | Remove-Item - powershell: Get-ChildItem _output\net6.0*,_output\*.Update\* -Recurse | Where { $_.Fullname -notlike "*\publish\*" -and $_.attributes -notlike "*directory*" } | Remove-Item
displayName: Clean up intermediate output displayName: Clean up intermediate output
- publish: $(outputFolder) - task: PublishPipelineArtifact@1
artifact: '$(osName)Backend' inputs:
path: $(outputFolder)
artifact: '$(osName)Backend'
artifactType: 'pipeline'
parallel: true
parallelCount: 100
displayName: Publish Backend displayName: Publish Backend
- publish: '$(testsFolder)/net6.0/win-x64/publish' - publish: '$(testsFolder)/net6.0/win-x64/publish'
artifact: win-x64-tests artifact: WindowsCoreTests
displayName: Publish win-x64 Test Package displayName: Publish Windows Test Package
- publish: '$(testsFolder)/net6.0/linux-x64/publish' - publish: '$(testsFolder)/net6.0/linux-x64/publish'
artifact: linux-x64-tests artifact: LinuxCoreTests
displayName: Publish linux-x64 Test Package displayName: Publish Linux 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' - publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
artifact: linux-musl-x64-tests artifact: LinuxMuslCoreTests
displayName: Publish linux-musl-x64 Test Package displayName: Publish Linux Musl Test Package
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish' - publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
artifact: freebsd-x64-tests artifact: FreebsdCoreTests
displayName: Publish freebsd-x64 Test Package displayName: Publish FreeBSD Test Package
- publish: '$(testsFolder)/net6.0/osx-x64/publish' - publish: '$(testsFolder)/net6.0/osx-x64/publish'
artifact: osx-x64-tests artifact: MacCoreTests
displayName: Publish osx-x64 Test Package displayName: Publish MacOS Test Package
- stage: Build_Backend_Other - stage: Build_Backend_Other
displayName: Build Backend (Other OS) displayName: Build Backend (Other OS)
@@ -137,29 +141,25 @@ stages:
inputs: inputs:
version: $(dotnetVersion) version: $(dotnetVersion)
- bash: | - bash: |
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}" BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props" echo $BUNDLEDVERSIONS
grep osx-x64 $BUNDLEDVERSIONS
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS echo "BSD already enabled"
else
echo "Enabling BSD support"
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64/' $BUNDLEDVERSIONS
fi fi
displayName: Extra Platform Support displayName: Enable FreeBSD Support
- task: Cache@2 - task: Cache@2
inputs: inputs:
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props' key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
path: $(nugetCacheFolder) path: $(nugetCacheFolder)
displayName: Cache NuGet packages displayName: Cache NuGet packages
- bash: ./build.sh --backend --enable-extra-platforms - bash: ./build.sh --backend --enable-bsd
displayName: Build Readarr Backend displayName: Build Readarr Backend
env: env:
NUGET_PACKAGES: $(nugetCacheFolder) NUGET_PACKAGES: $(nugetCacheFolder)
- bash: |
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
find ${TESTSFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
displayName: Clean up intermediate output
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- stage: Build_Frontend - stage: Build_Frontend
displayName: Frontend displayName: Frontend
@@ -183,7 +183,7 @@ stages:
- task: NodeTool@0 - task: NodeTool@0
displayName: Set Node.js version displayName: Set Node.js version
inputs: inputs:
versionSpec: $(nodeVersion) versionSpec: '12.x'
- checkout: self - checkout: self
submodules: true submodules: true
fetchDepth: 1 fetchDepth: 1
@@ -262,35 +262,35 @@ stages:
artifactName: WindowsFrontend artifactName: WindowsFrontend
targetPath: _output targetPath: _output
displayName: Fetch Frontend displayName: Fetch Frontend
- bash: ./build.sh --packages --enable-extra-platforms - bash: ./build.sh --packages --enable-bsd
displayName: Create Packages displayName: Create Packages
- bash: | - bash: |
find . -name "Readarr" -exec chmod a+x {} \; find . -name "Readarr" -exec chmod a+x {} \;
find . -name "Readarr.Update" -exec chmod a+x {} \; find . -name "Readarr.Update" -exec chmod a+x {} \;
displayName: Set executable bits displayName: Set executable bits
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create win-x64 zip displayName: Create Windows Core zip
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).windows-core-x64.zip' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).windows-core-x64.zip'
archiveType: 'zip' archiveType: 'zip'
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/win-x64/net6.0 rootFolderOrFile: $(artifactsFolder)/win-x64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create win-x86 zip displayName: Create Windows x86 Core zip
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).windows-core-x86.zip' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).windows-core-x86.zip'
archiveType: 'zip' archiveType: 'zip'
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/win-x86/net6.0 rootFolderOrFile: $(artifactsFolder)/win-x86/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create osx-x64 app displayName: Create MacOS x64 Core app
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).osx-app-core-x64.zip' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).osx-app-core-x64.zip'
archiveType: 'zip' archiveType: 'zip'
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/osx-x64-app/net6.0 rootFolderOrFile: $(artifactsFolder)/osx-x64-app/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create osx-x64 tar displayName: Create MacOS x64 Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).osx-core-x64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).osx-core-x64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -298,14 +298,14 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/osx-x64/net6.0 rootFolderOrFile: $(artifactsFolder)/osx-x64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create osx-arm64 app displayName: Create MacOS arm64 Core app
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).osx-app-core-arm64.zip' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).osx-app-core-arm64.zip'
archiveType: 'zip' archiveType: 'zip'
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/osx-arm64-app/net6.0 rootFolderOrFile: $(artifactsFolder)/osx-arm64-app/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create osx-arm64 tar displayName: Create MacOS arm64 Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).osx-core-arm64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).osx-core-arm64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -313,7 +313,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/osx-arm64/net6.0 rootFolderOrFile: $(artifactsFolder)/osx-arm64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-x64 tar displayName: Create Linux Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-core-x64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-core-x64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -321,7 +321,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-x64/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-x64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-musl-x64 tar displayName: Create Linux Musl Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-musl-core-x64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-musl-core-x64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -329,15 +329,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-x86 tar displayName: Create ARM32 Linux Core tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-core-x86.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-x86/net6.0
- task: ArchiveFiles@2
displayName: Create linux-arm tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-core-arm.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-core-arm.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -345,7 +337,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-arm/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-arm/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-musl-arm tar displayName: Create ARM32 Linux Musl Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-musl-core-arm.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-musl-core-arm.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -353,7 +345,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-musl-arm/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-arm64 tar displayName: Create Linux arm64 Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-core-arm64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-core-arm64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -361,7 +353,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-arm64/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-arm64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-musl-arm64 tar displayName: Create ARM64 Linux Musl Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-musl-core-arm64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).linux-musl-core-arm64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -369,7 +361,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create freebsd-x64 tar displayName: Create FreeBSD Core Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).freebsd-core-x64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(buildName).freebsd-core-x64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -382,7 +374,7 @@ stages:
- bash: | - bash: |
echo "Uploading source maps to sentry" echo "Uploading source maps to sentry"
curl -sL https://sentry.io/get-cli/ | bash curl -sL https://sentry.io/get-cli/ | bash
RELEASENAME="Readarr@${READARRVERSION}-${BUILD_SOURCEBRANCHNAME}" RELEASENAME="${READARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
sentry-cli releases new --finalize -p readarr -p readarr-ui -p readarr-update "${RELEASENAME}" sentry-cli releases new --finalize -p readarr -p readarr-ui -p readarr-update "${RELEASENAME}"
sentry-cli releases -p readarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite sentry-cli releases -p readarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
sentry-cli releases set-commits --auto "${RELEASENAME}" sentry-cli releases set-commits --auto "${RELEASENAME}"
@@ -421,22 +413,22 @@ stages:
matrix: matrix:
MacCore: MacCore:
osName: 'Mac' osName: 'Mac'
testName: 'osx-x64' testName: 'MacCore'
poolName: 'Azure Pipelines' poolName: 'Azure Pipelines'
imageName: ${{ variables.macImage }} imageName: ${{ variables.macImage }}
WindowsCore: WindowsCore:
osName: 'Windows' osName: 'Windows'
testName: 'win-x64' testName: 'WindowsCore'
poolName: 'Azure Pipelines' poolName: 'Azure Pipelines'
imageName: ${{ variables.windowsImage }} imageName: ${{ variables.windowsImage }}
LinuxCore: LinuxCore:
osName: 'Linux' osName: 'Linux'
testName: 'linux-x64' testName: 'LinuxCore'
poolName: 'Azure Pipelines' poolName: 'Azure Pipelines'
imageName: ${{ variables.linuxImage }} imageName: ${{ variables.linuxImage }}
FreebsdCore: FreebsdCore:
osName: 'Linux' osName: 'Linux'
testName: 'freebsd-x64' testName: 'FreebsdCore'
poolName: 'FreeBSD' poolName: 'FreeBSD'
imageName: imageName:
@@ -455,7 +447,7 @@ stages:
displayName: Download Test Artifact displayName: Download Test Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: '$(testName)-tests' artifactName: '$(testName)Tests'
targetPath: $(testsFolder) targetPath: $(testsFolder)
- powershell: Set-Service SCardSvr -StartupType Manual - powershell: Set-Service SCardSvr -StartupType Manual
displayName: Enable Windows Test Service displayName: Enable Windows Test Service
@@ -483,12 +475,8 @@ stages:
matrix: matrix:
alpine: alpine:
testName: 'Musl Net Core' testName: 'Musl Net Core'
artifactName: linux-musl-x64-tests artifactName: LinuxMuslCoreTests
containerImage: ghcr.io/servarr/testimages:alpine containerImage: ghcr.io/servarr/testimages:alpine
linux-x86:
testName: 'linux-x86'
artifactName: linux-x86-tests
containerImage: ghcr.io/servarr/testimages:linux-x86
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: ${{ variables.linuxImage }}
@@ -499,15 +487,9 @@ stages:
steps: steps:
- task: UseDotNet@2 - task: UseDotNet@2
displayName: 'Install .NET' displayName: 'Install .net core'
inputs: inputs:
version: $(dotnetVersion) version: $(dotnetVersion)
condition: and(succeeded(), ne(variables['testName'], 'linux-x86'))
- bash: |
SDKURL=$(curl -s https://api.github.com/repos/Servarr/dotnet-linux-x86/releases | jq -rc '.[].assets[].browser_download_url' | grep sdk-${DOTNETVERSION}.*gz$)
curl -fsSL $SDKURL | tar xzf - -C /opt/dotnet
displayName: 'Install .NET'
condition: and(succeeded(), eq(variables['testName'], 'linux-x86'))
- checkout: none - checkout: none
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Test Artifact displayName: Download Test Artifact
@@ -530,57 +512,6 @@ stages:
testResultsFiles: '**/TestResult.xml' testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(testName) Unit Tests' testRunTitle: '$(testName) Unit Tests'
failTaskOnFailedTests: true failTaskOnFailedTests: true
- job: Unit_LinuxCore_Postgres
displayName: Unit Native LinuxCore with Postgres Database
variables:
pattern: 'Readarr.*.linux-core-x64.tar.gz'
artifactName: LinuxCoreTests
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: 'linux-x64-Tests'
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=postgres14 \
-e POSTGRES_PASSWORD=readarr \
-e POSTGRES_USER=readarr \
-p 5432:5432/tcp \
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
postgres:14
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 Postgres Unit Tests'
failTaskOnFailedTests: true
- stage: Integration - stage: Integration
displayName: Integration displayName: Integration
@@ -592,17 +523,17 @@ stages:
matrix: matrix:
MacCore: MacCore:
osName: 'Mac' osName: 'Mac'
testName: 'osx-x64' testName: 'MacCore'
imageName: ${{ variables.macImage }} imageName: ${{ variables.macImage }}
pattern: 'Readarr.*.osx-core-x64.tar.gz' pattern: 'Readarr.*.osx-core-x64.tar.gz'
WindowsCore: WindowsCore:
osName: 'Windows' osName: 'Windows'
testName: 'win-x64' testName: 'WindowsCore'
imageName: ${{ variables.windowsImage }} imageName: ${{ variables.windowsImage }}
pattern: 'Readarr.*.windows-core-x64.zip' pattern: 'Readarr.*.windows-core-x64.zip'
LinuxCore: LinuxCore:
osName: 'Linux' osName: 'Linux'
testName: 'linux-x64' testName: 'LinuxCore'
imageName: ${{ variables.linuxImage }} imageName: ${{ variables.linuxImage }}
pattern: 'Readarr.*.linux-core-x64.tar.gz' pattern: 'Readarr.*.linux-core-x64.tar.gz'
@@ -619,7 +550,7 @@ stages:
displayName: Download Test Artifact displayName: Download Test Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: '$(testName)-tests' artifactName: '$(testName)Tests'
targetPath: $(testsFolder) targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Build Artifact displayName: Download Build Artifact
@@ -649,66 +580,6 @@ stages:
failTaskOnFailedTests: true failTaskOnFailedTests: true
displayName: Publish Test Results displayName: Publish Test Results
- job: Integration_LinuxCore_Postgres
displayName: Integration Native LinuxCore with Postgres Database
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=postgres14 \
-e POSTGRES_PASSWORD=readarr \
-e POSTGRES_USER=readarr \
-p 5432:5432/tcp \
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
postgres:14
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 Postgres Database Integration Tests'
failTaskOnFailedTests: true
displayName: Publish Test Results
- job: Integration_FreeBSD - job: Integration_FreeBSD
displayName: Integration Native FreeBSD displayName: Integration Native FreeBSD
workspace: workspace:
@@ -724,14 +595,14 @@ stages:
displayName: Download Test Artifact displayName: Download Test Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: 'freebsd-x64-tests' artifactName: 'FreebsdCoreTests'
targetPath: $(testsFolder) targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Build Artifact displayName: Download Build Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: Packages artifactName: Packages
itemPattern: '**/$(pattern)' itemPattern: '/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory) targetPath: $(Build.ArtifactStagingDirectory)
- bash: | - bash: |
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
@@ -758,15 +629,11 @@ stages:
strategy: strategy:
matrix: matrix:
alpine: alpine:
testName: 'linux-musl-x64' testName: 'Musl Net Core'
artifactName: linux-musl-x64-tests artifactName: LinuxMuslCoreTests
containerImage: ghcr.io/servarr/testimages:alpine containerImage: ghcr.io/servarr/testimages:alpine
pattern: 'Readarr.*.linux-musl-core-x64.tar.gz' pattern: 'Readarr.*.linux-musl-core-x64.tar.gz'
linux-x86:
testName: 'linux-x86'
artifactName: linux-x86-tests
containerImage: ghcr.io/servarr/testimages:linux-x86
pattern: 'Readarr.*.linux-core-x86.tar.gz'
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: ${{ variables.linuxImage }}
@@ -776,15 +643,9 @@ stages:
steps: steps:
- task: UseDotNet@2 - task: UseDotNet@2
displayName: 'Install .NET' displayName: 'Install .net core'
inputs: inputs:
version: $(dotnetVersion) version: $(dotnetVersion)
condition: and(succeeded(), ne(variables['testName'], 'linux-x86'))
- bash: |
SDKURL=$(curl -s https://api.github.com/repos/Servarr/dotnet-linux-x86/releases | jq -rc '.[].assets[].browser_download_url' | grep sdk-${DOTNETVERSION}.*gz$)
curl -fsSL $SDKURL | tar xzf - -C /opt/dotnet
displayName: 'Install .NET'
condition: and(succeeded(), eq(variables['testName'], 'linux-x86'))
- checkout: none - checkout: none
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Test Artifact displayName: Download Test Artifact
@@ -830,17 +691,14 @@ stages:
matrix: matrix:
Linux: Linux:
osName: 'Linux' osName: 'Linux'
artifactName: 'linux-x64'
imageName: ${{ variables.linuxImage }} imageName: ${{ variables.linuxImage }}
pattern: 'Readarr.*.linux-core-x64.tar.gz' pattern: 'Readarr.*.linux-core-x64.tar.gz'
Mac: Mac:
osName: 'Mac' osName: 'Mac'
artifactName: 'osx-x64'
imageName: ${{ variables.macImage }} imageName: ${{ variables.macImage }}
pattern: 'Readarr.*.osx-core-x64.tar.gz' pattern: 'Readarr.*.osx-core-x64.tar.gz'
Windows: Windows:
osName: 'Windows' osName: 'Windows'
artifactName: 'win-x64'
imageName: ${{ variables.windowsImage }} imageName: ${{ variables.windowsImage }}
pattern: 'Readarr.*.windows-core-x64.zip' pattern: 'Readarr.*.windows-core-x64.zip'
@@ -857,7 +715,7 @@ stages:
displayName: Download Test Artifact displayName: Download Test Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: '$(artifactName)-tests' artifactName: '$(osName)CoreTests'
targetPath: $(testsFolder) targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Build Artifact displayName: Download Build Artifact
@@ -918,7 +776,7 @@ stages:
- task: NodeTool@0 - task: NodeTool@0
displayName: Set Node.js version displayName: Set Node.js version
inputs: inputs:
versionSpec: $(nodeVersion) versionSpec: '12.x'
- checkout: self - checkout: self
submodules: true submodules: true
fetchDepth: 1 fetchDepth: 1
@@ -956,55 +814,6 @@ stages:
cliProjectVersion: '$(readarrVersion)' cliProjectVersion: '$(readarrVersion)'
cliSources: './frontend' cliSources: './frontend'
- task: SonarCloudAnalyze@1 - task: SonarCloudAnalyze@1
- job: Api_Docs
displayName: API Docs
condition: |
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
pool:
vmImage: ${{ variables.windowsImage }}
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: self
submodules: true
persistCredentials: true
fetchDepth: 1
- bash: ./docs.sh Windows
displayName: Create openapi.json
- bash: |
git config --global user.email "development@lidarr.audio"
git config --global user.name "Servarr"
git checkout -b api-docs
git add .
git status
if git status | grep modified
then
git commit -am 'Automated API Docs update [skip ci]'
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"}'
else
echo "No changes since last run"
fi
displayName: Commit API Doc Change
continueOnError: true
env:
GITHUBTOKEN: $(githubToken)
- task: CopyFiles@2
displayName: 'Copy openapi.json to: $(Build.ArtifactStagingDirectory)'
inputs:
SourceFolder: '$(Build.SourcesDirectory)'
Contents: |
**/*openapi.json
TargetFolder: '$(Build.ArtifactStagingDirectory)/api_docs'
- publish: $(Build.ArtifactStagingDirectory)/api_docs
artifact: 'APIDocs'
displayName: Publish API Docs Bundle
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
- job: Analyze_Backend - job: Analyze_Backend
displayName: Backend displayName: Backend
@@ -1100,4 +909,3 @@ stages:
SYSTEM_ACCESSTOKEN: $(System.AccessToken) SYSTEM_ACCESSTOKEN: $(System.AccessToken)
DISCORDCHANNELID: $(discordChannelId) DISCORDCHANNELID: $(discordChannelId)
DISCORDWEBHOOKKEY: $(discordWebhookKey) DISCORDWEBHOOKKEY: $(discordWebhookKey)
DISCORDTHREADID: $(discordThreadId)
+17 -33
View File
@@ -27,22 +27,15 @@ UpdateVersionNumber()
fi fi
} }
EnableExtraPlatformsInSDK() EnableBsdSupport()
{ {
SDK_PATH=$(dotnet --list-sdks | grep -P '6\.\d\.\d+' | head -1 | sed 's/\(6\.[0-9]*\.[0-9]*\).*\[\(.*\)\]/\2\/\1/g') #todo enable sdk with
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props" #SDK_PATH=$(dotnet --list-sdks | grep -P '5\.\d\.\d+' | head -1 | sed 's/\(5\.[0-9]*\.[0-9]*\).*\[\(.*\)\]/\2\/\1/g')
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then # BUNDLED_VERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
echo "Extra platforms already enabled"
else
echo "Enabling extra platform support"
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
fi
}
EnableExtraPlatforms()
{
if grep -qv freebsd-x64 src/Directory.Build.props; then if grep -qv freebsd-x64 src/Directory.Build.props; then
sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64;linux-x86</RuntimeIdentifiers>^g" src/Directory.Build.props sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64</RuntimeIdentifiers>^g" src/Directory.Build.props
sed -i'' -e "s^<ExcludedRuntimeFrameworkPairs>\(.*\)</ExcludedRuntimeFrameworkPairs>^<ExcludedRuntimeFrameworkPairs>\1;freebsd-x64:net472</ExcludedRuntimeFrameworkPairs>^g" src/Directory.Build.props
fi fi
} }
@@ -205,7 +198,10 @@ PackageWindows()
local folder=$artifactsFolder/$runtime/$framework/Readarr local folder=$artifactsFolder/$runtime/$framework/Readarr
PackageFiles "$folder" "$framework" "$runtime" PackageFiles "$folder" "$framework" "$runtime"
cp -r $outputFolder/$framework-windows/$runtime/publish/* $folder
if [ "$os" = "windows" ]; then
cp -r $outputFolder/$framework-windows/$runtime/publish/* $folder
fi
echo "Removing Readarr.Mono" echo "Removing Readarr.Mono"
rm -f $folder/Readarr.Mono.* rm -f $folder/Readarr.Mono.*
@@ -299,8 +295,7 @@ if [ $# -eq 0 ]; then
PACKAGES=YES PACKAGES=YES
INSTALLER=NO INSTALLER=NO
LINT=YES LINT=YES
ENABLE_EXTRA_PLATFORMS=NO ENABLE_BSD=NO
ENABLE_EXTRA_PLATFORMS_IN_SDK=NO
fi fi
while [[ $# -gt 0 ]] while [[ $# -gt 0 ]]
@@ -312,12 +307,8 @@ case $key in
BACKEND=YES BACKEND=YES
shift # past argument shift # past argument
;; ;;
--enable-bsd|--enable-extra-platforms) --enable-bsd)
ENABLE_EXTRA_PLATFORMS=YES ENABLE_BSD=YES
shift # past argument
;;
--enable-extra-platforms-in-sdk)
ENABLE_EXTRA_PLATFORMS_IN_SDK=YES
shift # past argument shift # past argument
;; ;;
-r|--runtime) -r|--runtime)
@@ -361,17 +352,12 @@ esac
done done
set -- "${POSITIONAL[@]}" # restore positional parameters set -- "${POSITIONAL[@]}" # restore positional parameters
if [ "$ENABLE_EXTRA_PLATFORMS_IN_SDK" = "YES" ];
then
EnableExtraPlatformsInSDK
fi
if [ "$BACKEND" = "YES" ]; if [ "$BACKEND" = "YES" ];
then then
UpdateVersionNumber UpdateVersionNumber
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ]; if [ "$ENABLE_BSD" = "YES" ];
then then
EnableExtraPlatforms EnableBsdSupport
fi fi
Build Build
if [[ -z "$RID" || -z "$FRAMEWORK" ]]; if [[ -z "$RID" || -z "$FRAMEWORK" ]];
@@ -381,10 +367,9 @@ then
PackageTests "net6.0" "linux-x64" PackageTests "net6.0" "linux-x64"
PackageTests "net6.0" "linux-musl-x64" PackageTests "net6.0" "linux-musl-x64"
PackageTests "net6.0" "osx-x64" PackageTests "net6.0" "osx-x64"
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ]; if [ "$ENABLE_BSD" = "YES" ];
then then
PackageTests "net6.0" "freebsd-x64" PackageTests "net6.0" "freebsd-x64"
PackageTests "net6.0" "linux-x86"
fi fi
else else
PackageTests "$FRAMEWORK" "$RID" PackageTests "$FRAMEWORK" "$RID"
@@ -423,10 +408,9 @@ then
Package "net6.0" "linux-musl-arm" Package "net6.0" "linux-musl-arm"
Package "net6.0" "osx-x64" Package "net6.0" "osx-x64"
Package "net6.0" "osx-arm64" Package "net6.0" "osx-arm64"
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ]; if [ "$ENABLE_BSD" = "YES" ];
then then
Package "net6.0" "freebsd-x64" Package "net6.0" "freebsd-x64"
Package "net6.0" "linux-x86"
fi fi
else else
Package "$FRAMEWORK" "$RID" Package "$FRAMEWORK" "$RID"
-38
View File
@@ -1,38 +0,0 @@
PLATFORM=$1
if [ "$PLATFORM" = "Windows" ]; then
RUNTIME="win-x64"
elif [ "$PLATFORM" = "Linux" ]; then
RUNTIME="linux-x64"
elif [ "$PLATFORM" = "Mac" ]; then
RUNTIME="osx-x64"
else
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
exit 1
fi
outputFolder='_output'
testPackageFolder='_tests'
rm -rf $outputFolder
rm -rf $testPackageFolder
slnFile=src/Readarr.sln
platform=Posix
dotnet clean $slnFile -c Debug
dotnet clean $slnFile -c Release
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
dotnet new tool-manifest
dotnet tool install --version 6.5.0 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Readarr.Api.V1/openapi.json "$outputFolder/net6.0/$RUNTIME/Readarr.console.dll" v1 &
sleep 45
kill %1
exit 0
+25
View File
@@ -0,0 +1,25 @@
{
"remove-empty-rulesets": true,
"always-semicolon": true,
"color-case": "lower",
"block-indent": " ",
"color-shorthand": false,
"element-case": "lower",
"eof-newline": true,
"leading-zero": true,
"quotes": "double",
"sort-order-fallback": "abc",
"space-before-colon": "",
"space-after-colon": " ",
"space-before-combinator": " ",
"space-after-combinator": " ",
"space-between-declarations": "\n",
"space-before-opening-brace": " ",
"space-after-opening-brace": "\n",
"space-after-selector-delimiter": " ",
"space-before-selector-delimiter": "",
"space-before-closing-brace": "\n",
"strip-spaces": true,
"tab-size": true,
"unitless-zero": false
}
+335
View File
@@ -0,0 +1,335 @@
{
"indent": {
"value": " ",
"FunctionExpression": 1,
"ArrayExpression": 1,
"ObjectExpression": 1
},
"lineBreak": {
"value": "\n",
"before": {
"ArrayPatternClosing": 0,
"ArrayPatternComma": 0,
"ArrayPatternOpening": 0,
"ArrowFunctionExpressionArrow": 0,
"ArrowFunctionExpressionClosingBrace": ">=1",
"ArrowFunctionExpressionOpeningBrace": 0,
"AssignmentExpression": ">=1",
"AssignmentOperator": 0,
"BlockStatement": 0,
"BreakKeyword": ">=1",
"CallExpression": -1,
"CallExpressionClosingParentheses": -1,
"CallExpressionOpeningParentheses": 0,
"CatchClosingBrace": ">=1",
"CatchKeyword": 0,
"CatchOpeningBrace": 0,
"ClassDeclaration": ">=1",
"ClassDeclarationClosingBrace": ">=1",
"ClassDeclarationOpeningBrace": 0,
"ConditionalExpression": ">=1",
"DeleteOperator": ">=1",
"DoWhileStatement": ">=1",
"DoWhileStatementClosingBrace": ">=1",
"DoWhileStatementOpeningBrace": 0,
"ElseIfStatement": 0,
"ElseIfStatementClosingBrace": ">=1",
"ElseIfStatementOpeningBrace": 0,
"ElseStatement": 0,
"ElseStatementClosingBrace": ">=1",
"ElseStatementOpeningBrace": 0,
"EmptyStatement": -1,
"EndOfFile": -1,
"FinallyClosingBrace": ">=1",
"FinallyKeyword": -1,
"FinallyOpeningBrace": 0,
"ForInStatement": ">=1",
"ForInStatementClosingBrace": ">=1",
"ForInStatementExpressionClosing": 0,
"ForInStatementExpressionOpening": 0,
"ForInStatementOpeningBrace": 0,
"ForStatement": ">=1",
"ForStatementClosingBrace": ">=1",
"ForStatementExpressionClosing": "<2",
"ForStatementExpressionOpening": 0,
"ForStatementOpeningBrace": 0,
"FunctionDeclaration": ">=1",
"FunctionDeclarationClosingBrace": ">=1",
"FunctionDeclarationOpeningBrace": 0,
"FunctionExpression": 0,
"FunctionExpressionClosingBrace": 1,
"FunctionExpressionOpeningBrace":0,
"IIFEClosingParentheses": 0,
"IfStatement": ">=1",
"IfStatementClosingBrace": ">=1",
"IfStatementOpeningBrace": 0,
"LogicalExpression": -1,
"MemberExpressionClosing": 0,
"MemberExpressionOpening": 0,
"MemberExpressionPeriod": -1,
"MethodDefinition": ">=1",
"ObjectExpressionClosingBrace": "<=1",
"ObjectPatternClosingBrace": 0,
"ObjectPatternComma": 0,
"ObjectPatternOpeningBrace": 0,
"ParameterDefault": 0,
"Property": "<=2",
"PropertyValue": 0,
"ReturnStatement": -1,
"SwitchClosingBrace": ">=1",
"SwitchOpeningBrace": 0,
"ThisExpression": -1,
"ThrowStatement": ">=1",
"TryClosingBrace": ">=1",
"TryKeyword": -1,
"TryOpeningBrace": 0,
"VariableDeclaration": ">=1",
"VariableDeclarationSemiColon": 0,
"VariableDeclarationWithoutInit": ">=1",
"VariableName": ">=1",
"VariableValue": 0,
"WhileStatement": ">=1",
"WhileStatementClosingBrace": ">=1",
"WhileStatementOpeningBrace": 0
},
"after": {
"ArrayPatternClosing": 0,
"ArrayPatternComma": 0,
"ArrayPatternOpening": 0,
"ArrowFunctionExpressionArrow": 0,
"ArrowFunctionExpressionClosingBrace": -1,
"ArrowFunctionExpressionOpeningBrace": ">=1",
"AssignmentExpression": ">=1",
"AssignmentOperator": 0,
"BlockStatement": 0,
"BreakKeyword": -1,
"CallExpression": -1,
"CallExpressionClosingParentheses": -1,
"CallExpressionOpeningParentheses": -1,
"CatchClosingBrace": ">=0",
"CatchKeyword": 0,
"CatchOpeningBrace": ">=1",
"ClassDeclaration": ">=1",
"ClassDeclarationClosingBrace": ">=1",
"ClassDeclarationOpeningBrace": ">=1",
"ConditionalExpression": ">=1",
"DeleteOperator": ">=1",
"DoWhileStatement": ">=1",
"DoWhileStatementClosingBrace": 0,
"DoWhileStatementOpeningBrace": ">=1",
"ElseIfStatement": ">=1",
"ElseIfStatementClosingBrace": ">=1",
"ElseIfStatementOpeningBrace": ">=1",
"ElseStatement": ">=1",
"ElseStatementClosingBrace": ">=1",
"ElseStatementOpeningBrace": ">=1",
"EmptyStatement": -1,
"FinallyClosingBrace": ">=1",
"FinallyKeyword": -1,
"FinallyOpeningBrace": ">=1",
"ForInStatement": ">=1",
"ForInStatementClosingBrace": ">=1",
"ForInStatementExpressionClosing": -1,
"ForInStatementExpressionOpening": "<2",
"ForInStatementOpeningBrace": ">=1",
"ForStatement": ">=1",
"ForStatementClosingBrace": ">=1",
"ForStatementExpressionClosing": -1,
"ForStatementExpressionOpening": "<2",
"ForStatementOpeningBrace": ">=1",
"FunctionDeclaration": ">=1",
"FunctionDeclarationClosingBrace": ">=1",
"FunctionDeclarationOpeningBrace": ">=1",
"FunctionExpression": 0,
"FunctionExpressionClosingBrace": -1,
"FunctionExpressionOpeningBrace": 1,
"IIFEOpeningParentheses": 0,
"IfStatement": ">=1",
"IfStatementClosingBrace": ">=1",
"IfStatementOpeningBrace": ">=1",
"LogicalExpression": -1,
"MemberExpressionClosing": 0,
"MemberExpressionOpening": 0,
"MemberExpressionPeriod": 0,
"MethodDefinition": ">=1",
"ObjectExpressionOpeningBrace": "<=1",
"ObjectPatternClosingBrace": 0,
"ObjectPatternComma": 0,
"ObjectPatternOpeningBrace": 0,
"ParameterDefault": 0,
"Property": -1,
"PropertyName": 0,
"ReturnStatement": -1,
"SwitchCaseColon": ">=1",
"SwitchClosingBrace": ">=1",
"SwitchOpeningBrace": ">=1",
"ThisExpression": 0,
"ThrowStatement": ">=1",
"TryClosingBrace": 0,
"TryKeyword": -1,
"TryOpeningBrace": ">=1",
"VariableDeclaration": ">=1",
"VariableDeclarationSemiColon": ">=1",
"VariableValue": -1,
"WhileStatement": ">=1",
"WhileStatementClosingBrace": ">=1",
"WhileStatementOpeningBrace": ">=1"
}
},
"whiteSpace": {
"value": " ",
"removeTrailing": 1,
"before": {
"ArgumentComma": 0,
"ArgumentList": 0,
"ArgumentListArrayExpression": 0,
"ArgumentListFunctionExpression": 1,
"ArgumentListObjectExpression": 0,
"ArrayExpressionClosing": 0,
"ArrayExpressionComma": 0,
"ArrayExpressionOpening": 1,
"AssignmentOperator": 1,
"BinaryExpression": 0,
"BinaryExpressionOperator": 1,
"BlockComment": 1,
"CallExpression": 1,
"CatchClosingBrace": 1,
"CatchKeyword": 1,
"CatchOpeningBrace": 1,
"CatchParameterList": 0,
"CommaOperator": 0,
"ConditionalExpressionAlternate": 1,
"ConditionalExpressionConsequent": 1,
"DoWhileStatementClosingBrace": 1,
"DoWhileStatementConditional": 1,
"DoWhileStatementOpeningBrace": 1,
"ElseIfStatementClosingBrace": 1,
"ElseIfStatementOpeningBrace": 1,
"ElseStatementClosingBrace": 1,
"ElseStatementOpeningBrace": 1,
"EmptyStatement": 0,
"ExpressionClosingParentheses": 0,
"FinallyClosingBrace": 1,
"FinallyKeyword": -1,
"FinallyOpeningBrace": 1,
"ForInStatement": 1,
"ForInStatementClosingBrace": 1,
"ForInStatementExpressionClosing": 0,
"ForInStatementExpressionOpening": 1,
"ForInStatementOpeningBrace": 1,
"ForStatement": 1,
"ForStatementClosingBrace": 1,
"ForStatementExpressionClosing": 0,
"ForStatementExpressionOpening": 1,
"ForStatementOpeningBrace": 1,
"ForStatementSemicolon": 0,
"FunctionDeclarationClosingBrace": 1,
"FunctionDeclarationOpeningBrace": 1,
"FunctionExpressionClosingBrace": 1,
"FunctionExpressionOpeningBrace": 1,
"IfStatementClosingBrace": 1,
"IfStatementConditionalClosing": 0,
"IfStatementConditionalOpening": 1,
"IfStatementOpeningBrace": 1,
"LineComment": 1,
"LogicalExpressionOperator": 1,
"MemberExpressionClosing": 0,
"ObjectExpressionClosingBrace": 1,
"ParameterComma": 0,
"ParameterList": 0,
"Property": 1,
"PropertyName": 1,
"PropertyValue": 1,
"SwitchDiscriminantClosing": 0,
"SwitchDiscriminantOpening": 1,
"ThrowKeyword": 1,
"TryClosingBrace": 1,
"TryKeyword": -1,
"TryOpeningBrace": 1,
"UnaryExpressionOperator": 0,
"VariableName": 1,
"VariableValue": 1,
"WhileStatementClosingBrace": 1,
"WhileStatementConditionalClosing": 0,
"WhileStatementConditionalOpening": 1,
"WhileStatementOpeningBrace": 1
},
"after": {
"ArgumentComma": 1,
"ArgumentList": 0,
"ArgumentListArrayExpression": 1,
"ArgumentListFunctionExpression": 1,
"ArgumentListObjectExpression": 0,
"ArrayExpressionClosing": 0,
"ArrayExpressionComma": 1,
"ArrayExpressionOpening": 0,
"AssignmentOperator": 1,
"BinaryExpression": 0,
"BinaryExpressionOperator": 1,
"BlockComment": 1,
"CallExpression": 0,
"CatchClosingBrace": 1,
"CatchKeyword": 1,
"CatchOpeningBrace": 1,
"CatchParameterList": 0,
"CommaOperator": 1,
"ConditionalExpressionConsequent": 1,
"ConditionalExpressionTest": 1,
"DoWhileStatementBody": 1,
"DoWhileStatementClosingBrace": 1,
"DoWhileStatementOpeningBrace": 1,
"ElseIfStatementClosingBrace": 1,
"ElseIfStatementOpeningBrace": 1,
"ElseStatementClosingBrace": 1,
"ElseStatementOpeningBrace": 1,
"EmptyStatement": 0,
"ExpressionOpeningParentheses": 0,
"FinallyClosingBrace": 1,
"FinallyKeyword": -1,
"FinallyOpeningBrace": 1,
"ForInStatement": 1,
"ForInStatementClosingBrace": 1,
"ForInStatementExpressionClosing": 1,
"ForInStatementExpressionOpening": 0,
"ForInStatementOpeningBrace": 1,
"ForStatement": 1,
"ForStatementClosingBrace": 1,
"ForStatementExpressionClosing": 1,
"ForStatementExpressionOpening": 0,
"ForStatementOpeningBrace": 1,
"ForStatementSemicolon": 1,
"FunctionDeclarationClosingBrace": 0,
"FunctionDeclarationOpeningBrace": 0,
"FunctionExpressionClosingBrace": 0,
"FunctionExpressionOpeningBrace": 0,
"FunctionName": 0,
"FunctionReservedWord": 0,
"IfStatementClosingBrace": 1,
"IfStatementConditionalClosing": 0,
"IfStatementConditionalOpening": 0,
"IfStatementOpeningBrace": 1,
"LogicalExpressionOperator": 1,
"MemberExpressionOpening": 0,
"ObjectExpressionClosingBrace": 0,
"ObjectExpressionOpeningBrace": 1,
"ParameterComma": 1,
"ParameterList": 0,
"PropertyName": 0,
"PropertyValue": 0,
"SwitchDiscriminantClosing": 1,
"SwitchDiscriminantOpening": 0,
"ThrowKeyword": 1,
"TryClosingBrace": 1,
"TryKeyword": -1,
"TryOpeningBrace": 1,
"UnaryExpressionOperator": 0,
"VariableName": 1,
"WhileStatementClosingBrace": 1,
"WhileStatementConditionalClosing": 1,
"WhileStatementConditionalOpening": 0,
"WhileStatementOpeningBrace": 1
}
}
}
-1
View File
@@ -1,2 +1 @@
**/JsLibraries/** **/JsLibraries/**
**/*.css.d.ts
+8 -68
View File
@@ -1,21 +1,14 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require('fs'); const fs = require('fs');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin').configs.recommended;
const frontendFolder = __dirname;
const dirs = fs const dirs = fs
.readdirSync(path.join(frontendFolder, 'src'), { withFileTypes: true }) .readdirSync('frontend/src', { withFileTypes: true })
.filter((dirent) => dirent.isDirectory()) .filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name) .map((dirent) => dirent.name)
.join('|'); .join('|');
module.exports = { const frontendFolder = __dirname;
root: true,
module.exports = {
parser: '@babel/eslint-parser', parser: '@babel/eslint-parser',
env: { env: {
@@ -35,7 +28,7 @@ module.exports = {
ecmaVersion: 6, ecmaVersion: 6,
sourceType: 'module', sourceType: 'module',
babelOptions: { babelOptions: {
configFile: `${frontendFolder}/babel.config.js` configFile: `${frontendFolder}/babel.config.js`,
}, },
ecmaFeatures: { ecmaFeatures: {
modules: true, modules: true,
@@ -46,11 +39,8 @@ module.exports = {
plugins: [ plugins: [
'filenames', 'filenames',
'react', 'react',
'react-hooks',
'simple-import-sort', 'simple-import-sort',
'import', 'import'
'@typescript-eslint',
'prettier'
], ],
settings: { settings: {
@@ -233,7 +223,7 @@ module.exports = {
'consistent-this': ['error', 'self'], 'consistent-this': ['error', 'self'],
'eol-last': 'error', 'eol-last': 'error',
'func-names': 'off', 'func-names': 'off',
'func-style': ['error', 'declaration', { allowArrowFunctions: true }], 'func-style': ['error', 'declaration'],
indent: ['error', 2, { SwitchCase: 1 }], indent: ['error', 2, { SwitchCase: 1 }],
'key-spacing': ['error', { beforeColon: false, afterColon: true }], 'key-spacing': ['error', { beforeColon: false, afterColon: true }],
'keyword-spacing': ['error', { before: true, after: true }], 'keyword-spacing': ['error', { before: true, after: true }],
@@ -318,15 +308,11 @@ module.exports = {
'react/react-in-jsx-scope': 2, 'react/react-in-jsx-scope': 2,
'react/self-closing-comp': 2, 'react/self-closing-comp': 2,
'react/sort-comp': 2, 'react/sort-comp': 2,
'react/jsx-wrap-multilines': 2, 'react/jsx-wrap-multilines': 2
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error'
}, },
overrides: [ overrides: [
{ {
files: [ files: ['*.js'],
'*.js'
],
rules: { rules: {
'simple-import-sort/imports': [ 'simple-import-sort/imports': [
'error', 'error',
@@ -341,52 +327,6 @@ module.exports = {
} }
] ]
} }
},
{
files: [
'*.ts',
'*.tsx'
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json'
},
extends: [
'prettier'
],
rules: Object.assign(typescriptEslintRecommended.rules, {
'no-shadow': 'off',
// These should be enabled after cleaning things up
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'react/prop-types': 'off',
'prettier/prettier': 'error',
'simple-import-sort/imports': [
'error',
{
groups: [
// Packages
// Absolute Paths
// Relative Paths
// Css
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
]
}
]
})
},
{
files: [
'*.css.d.ts'
],
rules: {
'filenames/match-exported': 'off',
'init-declarations': 'off',
'prettier/prettier': 'off'
}
} }
] ]
}; };
+12
View File
@@ -0,0 +1,12 @@
{
"js": {
"indent_size": 2,
"indent_char": " ",
"indent_level": 2,
"indent_with_tabs": false,
"preserve_newlines": true,
"brace_style": "collapse",
"max_preserve_newlines": 2,
"jslint_happy": true
}
}
-10
View File
@@ -1,10 +0,0 @@
# Ignore everything recursively
*
# But not the .ts files
!*.ts*
*css.d.ts
# Check subdirectories too
!*/
-6
View File
@@ -1,6 +0,0 @@
{
"arrowParens": "always",
"endOfLine": "auto",
"singleQuote": true,
"trailingComma": "es5"
}
+83 -13
View File
@@ -1,12 +1,12 @@
{ {
"plugins": [ "plugins": [
"stylelint-order" "stylelint-order"
], ],
"ignoreFiles": [ "ignoreFiles": [
"frontend/src/Styles/scaffolding.css", "frontend/src/Styles/scaffolding.css",
"**/*.js" "**/*.js"
], ],
"rules": { "rules": {
"at-rule-empty-line-before": [ "at-rule-empty-line-before": [
"always", "always",
{ {
@@ -15,6 +15,9 @@
] ]
} }
], ],
"at-rule-name-case": "lower",
"at-rule-name-newline-after": "always-multi-line",
"at-rule-name-space-after": "always",
"at-rule-no-unknown": [ "at-rule-no-unknown": [
true, true,
{ {
@@ -25,36 +28,83 @@
} }
], ],
"at-rule-no-vendor-prefix": true, "at-rule-no-vendor-prefix": true,
"at-rule-semicolon-newline-after": "always",
"at-rule-semicolon-space-before": "never",
"block-closing-brace-empty-line-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always",
"block-closing-brace-space-after": "always-single-line",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true, "block-no-empty": true,
"block-opening-brace-newline-after": "always",
"block-opening-brace-newline-before": "never-single-line",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-hex-length": "short", "color-hex-length": "short",
"color-named": "never", "color-named": "never",
"color-no-invalid-hex": true, "color-no-invalid-hex": true,
"comment-whitespace-inside": "always", "comment-whitespace-inside": "always",
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": [ "declaration-block-no-duplicate-properties": [
true, true,
{ {
"ignoreProperties": [ "ignoreProperties": [
"composes" "composes"
] ]
} }
], ],
"declaration-block-no-redundant-longhand-properties": true, "declaration-block-no-redundant-longhand-properties": true,
"declaration-block-no-shorthand-property-overrides": true, "declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always",
"declaration-block-semicolon-newline-before": "never-multi-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1, "declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-space-after": "always",
"declaration-colon-space-before": "never",
"font-family-name-quotes": "always-unless-keyword", "font-family-name-quotes": "always-unless-keyword",
"function-calc-no-unspaced-operator": true, "function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "never-multi-line",
"function-comma-newline-before": "never-multi-line",
"function-comma-space-after": "always",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true, "function-linear-gradient-no-nonstandard-direction": true,
"function-name-case": "lower", "function-name-case": "lower",
"function-parentheses-newline-inside": "never-multi-line",
"function-parentheses-space-inside": "never",
"function-url-quotes": "always", "function-url-quotes": "always",
"function-url-scheme-disallowed-list": [ "function-url-scheme-disallowed-list": [
"data" "data"
], ],
"function-whitespace-after": "always",
"indentation": 2,
"keyframe-declaration-no-important": true, "keyframe-declaration-no-important": true,
"length-zero-no-unit": true, "length-zero-no-unit": true,
"max-empty-lines": 1,
"max-line-length": [
100,
{
"ignore": [
"non-comments"
]
}
],
"max-nesting-depth": 2, "max-nesting-depth": 2,
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-name-case": "lower",
"media-feature-name-no-vendor-prefix": true, "media-feature-name-no-vendor-prefix": true,
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"no-empty-source": true, "no-empty-source": true,
"no-eol-whitespace": true,
"no-extra-semicolons": true,
"no-invalid-double-slash-comments": true, "no-invalid-double-slash-comments": true,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"number-no-trailing-zeros": true,
"order/order": [ "order/order": [
"custom-properties", "custom-properties",
"dollar-variables", "dollar-variables",
@@ -82,7 +132,6 @@
"right", "right",
"bottom", "bottom",
"left", "left",
"inset",
"z-index", "z-index",
"display", "display",
"visibility", "visibility",
@@ -294,33 +343,54 @@
] ]
} }
], ],
"property-case": "lower",
"property-no-vendor-prefix": true, "property-no-vendor-prefix": true,
"rule-empty-line-before": [ "rule-empty-line-before": [
"always", "always",
{ {
"except": [ "except": [
"first-nested" "first-nested"
], ],
"ignore": [ "ignore": [
"after-comment" "after-comment"
] ]
} }
], ],
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-attribute-quotes": "never", "selector-attribute-quotes": "never",
"selector-class-pattern": "^[A-Za-z0-9]+$", "selector-class-pattern": "^[A-Za-z0-9]+$",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-descendant-combinator-no-non-space": true,
"selector-list-comma-newline-after": "always",
"selector-list-comma-newline-before": "never-multi-line",
"selector-list-comma-space-before": "never",
"selector-max-attribute": 0, "selector-max-attribute": 0,
"selector-max-class": 3, "selector-max-class": 3,
"selector-max-compound-selectors": 3, "selector-max-compound-selectors": 3,
"selector-max-empty-lines": 0,
"selector-max-id": 0, "selector-max-id": 0,
"selector-max-universal": 0, "selector-max-universal": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-colon-notation": "double", "selector-pseudo-element-colon-notation": "double",
"selector-pseudo-element-no-unknown": true, "selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower", "selector-type-case": "lower",
"selector-type-no-unknown": true, "selector-type-no-unknown": true,
"shorthand-property-no-redundant-values": true, "shorthand-property-no-redundant-values": true,
"string-no-newline": true, "string-no-newline": true,
"string-quotes": "single",
"time-min-milliseconds": 100, "time-min-milliseconds": 100,
"unit-case": "lower",
"unit-no-unknown": true, "unit-no-unknown": true,
"value-list-comma-newline-after": "never-multi-line",
"value-list-comma-newline-before": "never-multi-line",
"value-list-comma-space-after": "always",
"value-list-comma-space-before": "never",
"value-list-max-empty-lines": 0,
"value-no-vendor-prefix": true "value-no-vendor-prefix": true
} }
} }
-7
View File
@@ -1,7 +0,0 @@
{
"recommendations": [
"stylelint.vscode-stylelint",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
-23
View File
@@ -1,23 +0,0 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.insertFinalNewline": true,
"files.exclude": {
"**/node_modules": true,
"**/*.d.css": true
},
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"typescript.preferences.quoteStyle": "single",
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
}
+2 -4
View File
@@ -17,8 +17,7 @@ module.exports = {
env: { env: {
development: { development: {
presets: [ presets: [
['@babel/preset-react', { development: true }], ['@babel/preset-react', { development: true }]
'@babel/preset-typescript'
], ],
plugins: [ plugins: [
'babel-plugin-inline-classnames' 'babel-plugin-inline-classnames'
@@ -26,8 +25,7 @@ module.exports = {
}, },
production: { production: {
presets: [ presets: [
'@babel/preset-react', '@babel/preset-react'
'@babel/preset-typescript'
], ],
plugins: [ plugins: [
'babel-plugin-transform-react-remove-prop-types' 'babel-plugin-transform-react-remove-prop-types'
+20 -32
View File
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path'); const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const FileManagerPlugin = require('filemanager-webpack-plugin'); const FileManagerPlugin = require('filemanager-webpack-plugin');
@@ -6,7 +5,6 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const LiveReloadPlugin = require('webpack-livereload-plugin'); const LiveReloadPlugin = require('webpack-livereload-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = (env) => { module.exports = (env) => {
const uiFolder = 'UI'; const uiFolder = 'UI';
@@ -40,19 +38,13 @@ module.exports = (env) => {
}, },
resolve: { resolve: {
extensions: [
'.ts',
'.tsx',
'.js'
],
modules: [ modules: [
srcFolder, srcFolder,
path.join(srcFolder, 'Shims'), path.join(srcFolder, 'Shims'),
'node_modules' 'node_modules'
], ],
alias: { alias: {
jquery: 'jquery/dist/jquery.min', jquery: 'jquery/src/jquery'
'react-middle-truncate': 'react-middle-truncate/lib/react-middle-truncate'
}, },
fallback: { fallback: {
buffer: false, buffer: false,
@@ -67,23 +59,23 @@ module.exports = (env) => {
output: { output: {
path: distFolder, path: distFolder,
publicPath: '/', publicPath: '/',
filename: '[name]-[contenthash].js', filename: '[name].js',
sourceMapFilename: '[file].map' sourceMapFilename: '[file].map'
}, },
optimization: { optimization: {
moduleIds: 'deterministic', moduleIds: 'deterministic',
chunkIds: isProduction ? 'deterministic' : 'named' chunkIds: 'named',
splitChunks: {
chunks: 'initial',
name: 'vendors'
}
}, },
performance: { performance: {
hints: false hints: false
}, },
experiments: {
topLevelAwait: true
},
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
__DEV__: !isProduction, __DEV__: !isProduction,
@@ -138,8 +130,6 @@ module.exports = (env) => {
} }
}), }),
new ForkTsCheckerWebpackPlugin(),
new LiveReloadPlugin() new LiveReloadPlugin()
], ],
@@ -163,7 +153,7 @@ module.exports = (env) => {
} }
}, },
{ {
test: [/\.jsx?$/, /\.tsx?$/], test: /\.js?$/,
exclude: /(node_modules|JsLibraries)/, exclude: /(node_modules|JsLibraries)/,
use: [ use: [
{ {
@@ -194,7 +184,6 @@ module.exports = (env) => {
exclude: /(node_modules|globals.css)/, exclude: /(node_modules|globals.css)/,
use: [ use: [
{ loader: MiniCssExtractPlugin.loader }, { loader: MiniCssExtractPlugin.loader },
{ loader: 'css-modules-typescript-loader' },
{ {
loader: 'css-loader', loader: 'css-loader',
options: { options: {
@@ -263,19 +252,18 @@ module.exports = (env) => {
config.resolve.alias['react-dom$'] = 'react-dom/profiling'; config.resolve.alias['react-dom$'] = 'react-dom/profiling';
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling'; config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
config.optimization = { config.optimization.minimizer = [
minimize: true, new TerserPlugin({
minimizer: [ cache: true,
new TerserPlugin({ parallel: true,
terserOptions: { sourceMap: true, // Must be set to true if using source-maps in production
sourceMap: true, // Must be set to true if using source-maps in production terserOptions: {
mangle: false, mangle: false,
keep_classnames: true, keep_classnames: true,
keep_fnames: true keep_fnames: true
} }
}) })
] ];
};
} }
return config; return config;
@@ -1,4 +1,3 @@
// eslint-disable-next-line filenames/match-exported
const loaderUtils = require('loader-utils'); const loaderUtils = require('loader-utils');
module.exports = function cssVariablesLoader(source) { module.exports = function cssVariablesLoader(source) {
+1
View File
@@ -1,6 +1,7 @@
const reload = require('require-nocache')(module); const reload = require('require-nocache')(module);
const cssVarsFiles = [ const cssVarsFiles = [
'./src/Styles/Variables/colors',
'./src/Styles/Variables/dimensions', './src/Styles/Variables/dimensions',
'./src/Styles/Variables/fonts', './src/Styles/Variables/fonts',
'./src/Styles/Variables/animations', './src/Styles/Variables/animations',
+4
View File
@@ -0,0 +1,4 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.insertFinalNewline": true
}
+11 -12
View File
@@ -1,6 +1,5 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ConfirmModal from 'Components/Modal/ConfirmModal'; import ConfirmModal from 'Components/Modal/ConfirmModal';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
@@ -62,33 +61,33 @@ class Blocklist extends Component {
getSelectedIds = () => { getSelectedIds = () => {
return getSelectedIds(this.state.selectedState); return getSelectedIds(this.state.selectedState);
}; }
// //
// Listeners // Listeners
onSelectAllChange = ({ value }) => { onSelectAllChange = ({ value }) => {
this.setState(selectAll(this.state.selectedState, value)); this.setState(selectAll(this.state.selectedState, value));
}; }
onSelectedChange = ({ id, value, shiftKey = false }) => { onSelectedChange = ({ id, value, shiftKey = false }) => {
this.setState((state) => { this.setState((state) => {
return toggleSelected(state, this.props.items, id, value, shiftKey); return toggleSelected(state, this.props.items, id, value, shiftKey);
}); });
}; }
onRemoveSelectedPress = () => { onRemoveSelectedPress = () => {
this.setState({ isConfirmRemoveModalOpen: true }); this.setState({ isConfirmRemoveModalOpen: true });
}; }
onRemoveSelectedConfirmed = () => { onRemoveSelectedConfirmed = () => {
this.props.onRemoveSelected(this.getSelectedIds()); this.props.onRemoveSelected(this.getSelectedIds());
this.setState({ isConfirmRemoveModalOpen: false }); this.setState({ isConfirmRemoveModalOpen: false });
}; }
onConfirmRemoveModalClose = () => { onConfirmRemoveModalClose = () => {
this.setState({ isConfirmRemoveModalOpen: false }); this.setState({ isConfirmRemoveModalOpen: false });
}; }
// //
// Render // Render
@@ -162,16 +161,16 @@ class Blocklist extends Component {
{ {
!isAnyFetching && !!error && !isAnyFetching && !!error &&
<Alert kind={kinds.DANGER}> <div>
{translate('UnableToLoadBlocklist')} {translate('UnableToLoadBlocklist')}
</Alert> </div>
} }
{ {
isAllPopulated && !error && !items.length && isAllPopulated && !error && !items.length &&
<Alert kind={kinds.INFO}> <div>
{translate('NoHistoryBlocklist')} {translate('NoHistoryBlocklist')}
</Alert> </div>
} }
{ {
@@ -215,7 +214,7 @@ class Blocklist extends Component {
isOpen={isConfirmRemoveModalOpen} isOpen={isConfirmRemoveModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title={translate('RemoveSelected')} title={translate('RemoveSelected')}
message={translate('RemoveSelectedItemBlocklistMessageText')} message={translate('RemoveSelectedMessageText')}
confirmLabel={translate('RemoveSelected')} confirmLabel={translate('RemoveSelected')}
onConfirm={this.onRemoveSelectedConfirmed} onConfirm={this.onRemoveSelectedConfirmed}
onCancel={this.onConfirmRemoveModalClose} onCancel={this.onConfirmRemoveModalClose}
@@ -68,37 +68,37 @@ class BlocklistConnector extends Component {
repopulate = () => { repopulate = () => {
this.props.fetchBlocklist(); this.props.fetchBlocklist();
}; }
// //
// Listeners // Listeners
onFirstPagePress = () => { onFirstPagePress = () => {
this.props.gotoBlocklistFirstPage(); this.props.gotoBlocklistFirstPage();
}; }
onPreviousPagePress = () => { onPreviousPagePress = () => {
this.props.gotoBlocklistPreviousPage(); this.props.gotoBlocklistPreviousPage();
}; }
onNextPagePress = () => { onNextPagePress = () => {
this.props.gotoBlocklistNextPage(); this.props.gotoBlocklistNextPage();
}; }
onLastPagePress = () => { onLastPagePress = () => {
this.props.gotoBlocklistLastPage(); this.props.gotoBlocklistLastPage();
}; }
onPageSelect = (page) => { onPageSelect = (page) => {
this.props.gotoBlocklistPage({ page }); this.props.gotoBlocklistPage({ page });
}; }
onRemoveSelected = (ids) => { onRemoveSelected = (ids) => {
this.props.removeBlocklistItems({ ids }); this.props.removeBlocklistItems({ ids });
}; }
onSortPress = (sortKey) => { onSortPress = (sortKey) => {
this.props.setBlocklistSort({ sortKey }); this.props.setBlocklistSort({ sortKey });
}; }
onTableOptionChange = (payload) => { onTableOptionChange = (payload) => {
this.props.setBlocklistTableOption(payload); this.props.setBlocklistTableOption(payload);
@@ -106,11 +106,11 @@ class BlocklistConnector extends Component {
if (payload.pageSize) { if (payload.pageSize) {
this.props.gotoBlocklistFirstPage(); this.props.gotoBlocklistFirstPage();
} }
}; }
onClearBlocklistPress = () => { onClearBlocklistPress = () => {
this.props.executeCommand({ name: commandNames.CLEAR_BLOCKLIST }); this.props.executeCommand({ name: commandNames.CLEAR_BLOCKLIST });
}; }
// //
// Render // Render
-9
View File
@@ -1,9 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'actions': string;
'indexer': string;
'quality': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -1,7 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import AuthorNameLink from 'Author/AuthorNameLink'; import AuthorNameLink from 'Author/AuthorNameLink';
import BookFormats from 'Book/BookFormats';
import BookQuality from 'Book/BookQuality'; import BookQuality from 'Book/BookQuality';
import IconButton from 'Components/Link/IconButton'; import IconButton from 'Components/Link/IconButton';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector'; import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
@@ -31,11 +30,11 @@ class BlocklistRow extends Component {
onDetailsPress = () => { onDetailsPress = () => {
this.setState({ isDetailsModalOpen: true }); this.setState({ isDetailsModalOpen: true });
}; }
onDetailsModalClose = () => { onDetailsModalClose = () => {
this.setState({ isDetailsModalOpen: false }); this.setState({ isDetailsModalOpen: false });
}; }
// //
// Render // Render
@@ -46,7 +45,6 @@ class BlocklistRow extends Component {
author, author,
sourceTitle, sourceTitle,
quality, quality,
customFormats,
date, date,
protocol, protocol,
indexer, indexer,
@@ -112,16 +110,6 @@ class BlocklistRow extends Component {
); );
} }
if (name === 'customFormats') {
return (
<TableRowCell key={name}>
<BookFormats
formats={customFormats}
/>
</TableRowCell>
);
}
if (name === 'date') { if (name === 'date') {
return ( return (
<RelativeDateCellConnector <RelativeDateCellConnector
@@ -186,7 +174,6 @@ BlocklistRow.propTypes = {
author: PropTypes.object.isRequired, author: PropTypes.object.isRequired,
sourceTitle: PropTypes.string.isRequired, sourceTitle: PropTypes.string.isRequired,
quality: PropTypes.object.isRequired, quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
date: PropTypes.string.isRequired, date: PropTypes.string.isRequired,
protocol: PropTypes.string.isRequired, protocol: PropTypes.string.isRequired,
indexer: PropTypes.string, indexer: PropTypes.string,
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'description': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -9,7 +9,6 @@ import Link from 'Components/Link/Link';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import formatDateTime from 'Utilities/Date/formatDateTime'; import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge'; import formatAge from 'Utilities/Number/formatAge';
import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './HistoryDetails.css'; import styles from './HistoryDetails.css';
@@ -66,10 +65,8 @@ function HistoryDetails(props) {
const { const {
indexer, indexer,
releaseGroup, releaseGroup,
customFormatScore,
nzbInfoUrl, nzbInfoUrl,
downloadClient, downloadClient,
downloadClientName,
downloadId, downloadId,
age, age,
ageHours, ageHours,
@@ -77,8 +74,6 @@ function HistoryDetails(props) {
publishedDate publishedDate
} = data; } = data;
const downloadClientNameInfo = downloadClientName ?? downloadClient;
return ( return (
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
@@ -105,16 +100,7 @@ function HistoryDetails(props) {
} }
{ {
customFormatScore && customFormatScore !== '0' ? !!nzbInfoUrl &&
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatPreferredWordScore(customFormatScore)}
/> :
null
}
{
nzbInfoUrl ?
<span> <span>
<DescriptionListItemTitle> <DescriptionListItemTitle>
Info URL Info URL
@@ -123,17 +109,15 @@ function HistoryDetails(props) {
<DescriptionListItemDescription> <DescriptionListItemDescription>
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link> <Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
</DescriptionListItemDescription> </DescriptionListItemDescription>
</span> : </span>
null
} }
{ {
downloadClientNameInfo ? !!downloadClient &&
<DescriptionListItem <DescriptionListItem
title={translate('DownloadClient')} title={translate('DownloadClient')}
data={downloadClientNameInfo} data={downloadClient}
/> : />
null
} }
{ {
@@ -189,7 +173,6 @@ function HistoryDetails(props) {
if (eventType === 'bookFileImported') { if (eventType === 'bookFileImported') {
const { const {
customFormatScore,
droppedPath, droppedPath,
importedPath importedPath
} = data; } = data;
@@ -212,22 +195,12 @@ function HistoryDetails(props) {
} }
{ {
importedPath ? !!importedPath &&
<DescriptionListItem <DescriptionListItem
descriptionClassName={styles.description} descriptionClassName={styles.description}
title={translate('ImportedTo')} title={translate('ImportedTo')}
data={importedPath} data={importedPath}
/> : />
null
}
{
customFormatScore && customFormatScore !== '0' ?
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatPreferredWordScore(customFormatScore)}
/> :
null
} }
</DescriptionList> </DescriptionList>
); );
@@ -235,8 +208,7 @@ function HistoryDetails(props) {
if (eventType === 'bookFileDeleted') { if (eventType === 'bookFileDeleted') {
const { const {
reason, reason
customFormatScore
} = data; } = data;
let reasonMessage = ''; let reasonMessage = '';
@@ -266,15 +238,6 @@ function HistoryDetails(props) {
title={translate('Reason')} title={translate('Reason')}
data={reasonMessage} data={reasonMessage}
/> />
{
customFormatScore && customFormatScore !== '0' ?
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatPreferredWordScore(customFormatScore)}
/> :
null
}
</DescriptionList> </DescriptionList>
); );
} }
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'markAsFailedButton': string;
}
export const cssExports: CssExports;
export default cssExports;
+6 -7
View File
@@ -1,6 +1,5 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import FilterMenu from 'Components/Menu/FilterMenu'; import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
@@ -12,7 +11,7 @@ import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody'; import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import TablePager from 'Components/Table/TablePager'; import TablePager from 'Components/Table/TablePager';
import { align, icons, kinds } from 'Helpers/Props'; import { align, icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import HistoryRowConnector from './HistoryRowConnector'; import HistoryRowConnector from './HistoryRowConnector';
@@ -86,9 +85,9 @@ class History extends Component {
{ {
!isFetchingAny && hasError && !isFetchingAny && hasError &&
<Alert kind={kinds.DANGER}> <div>
{translate('UnableToLoadHistory')} {translate('UnableToLoadHistory')}
</Alert> </div>
} }
{ {
@@ -96,9 +95,9 @@ class History extends Component {
// wait for the books to populate because they are never coming. // wait for the books to populate because they are never coming.
isPopulated && !hasError && !items.length && isPopulated && !hasError && !items.length &&
<Alert kind={kinds.INFO}> <div>
{translate('NoHistory')} No history found
</Alert> </div>
} }
{ {
@@ -60,38 +60,38 @@ class HistoryConnector extends Component {
repopulate = () => { repopulate = () => {
this.props.fetchHistory(); this.props.fetchHistory();
}; }
// //
// Listeners // Listeners
onFirstPagePress = () => { onFirstPagePress = () => {
this.props.gotoHistoryFirstPage(); this.props.gotoHistoryFirstPage();
}; }
onPreviousPagePress = () => { onPreviousPagePress = () => {
this.props.gotoHistoryPreviousPage(); this.props.gotoHistoryPreviousPage();
}; }
onNextPagePress = () => { onNextPagePress = () => {
this.props.gotoHistoryNextPage(); this.props.gotoHistoryNextPage();
}; }
onLastPagePress = () => { onLastPagePress = () => {
this.props.gotoHistoryLastPage(); this.props.gotoHistoryLastPage();
}; }
onPageSelect = (page) => { onPageSelect = (page) => {
this.props.gotoHistoryPage({ page }); this.props.gotoHistoryPage({ page });
}; }
onSortPress = (sortKey) => { onSortPress = (sortKey) => {
this.props.setHistorySort({ sortKey }); this.props.setHistorySort({ sortKey });
}; }
onFilterSelect = (selectedFilterKey) => { onFilterSelect = (selectedFilterKey) => {
this.props.setHistoryFilter({ selectedFilterKey }); this.props.setHistoryFilter({ selectedFilterKey });
}; }
onTableOptionChange = (payload) => { onTableOptionChange = (payload) => {
this.props.setHistoryTableOption(payload); this.props.setHistoryTableOption(payload);
@@ -99,7 +99,7 @@ class HistoryConnector extends Component {
if (payload.pageSize) { if (payload.pageSize) {
this.props.gotoHistoryFirstPage(); this.props.gotoHistoryFirstPage();
} }
}; }
// //
// Render // Render
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'cell': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -10,12 +10,6 @@
width: 80px; width: 80px;
} }
.customFormatScore {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 55px;
}
.releaseGroup { .releaseGroup {
composes: cell from '~Components/Table/Cells/TableRowCell.css'; composes: cell from '~Components/Table/Cells/TableRowCell.css';
-11
View File
@@ -1,11 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'customFormatScore': string;
'details': string;
'downloadClient': string;
'indexer': string;
'releaseGroup': string;
}
export const cssExports: CssExports;
export default cssExports;
+3 -52
View File
@@ -1,16 +1,13 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import AuthorNameLink from 'Author/AuthorNameLink'; import AuthorNameLink from 'Author/AuthorNameLink';
import BookFormats from 'Book/BookFormats';
import BookQuality from 'Book/BookQuality'; import BookQuality from 'Book/BookQuality';
import BookTitleLink from 'Book/BookTitleLink'; import BookTitleLink from 'Book/BookTitleLink';
import IconButton from 'Components/Link/IconButton'; import IconButton from 'Components/Link/IconButton';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector'; import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow'; import TableRow from 'Components/Table/TableRow';
import Tooltip from 'Components/Tooltip/Tooltip'; import { icons } from 'Helpers/Props';
import { icons, tooltipPositions } from 'Helpers/Props';
import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore';
import HistoryDetailsModal from './Details/HistoryDetailsModal'; import HistoryDetailsModal from './Details/HistoryDetailsModal';
import HistoryEventTypeCell from './HistoryEventTypeCell'; import HistoryEventTypeCell from './HistoryEventTypeCell';
import styles from './HistoryRow.css'; import styles from './HistoryRow.css';
@@ -43,11 +40,11 @@ class HistoryRow extends Component {
onDetailsPress = () => { onDetailsPress = () => {
this.setState({ isDetailsModalOpen: true }); this.setState({ isDetailsModalOpen: true });
}; }
onDetailsModalClose = () => { onDetailsModalClose = () => {
this.setState({ isDetailsModalOpen: false }); this.setState({ isDetailsModalOpen: false });
}; }
// //
// Render // Render
@@ -57,8 +54,6 @@ class HistoryRow extends Component {
author, author,
book, book,
quality, quality,
customFormats,
customFormatScore,
qualityCutoffNotMet, qualityCutoffNotMet,
eventType, eventType,
sourceTitle, sourceTitle,
@@ -132,16 +127,6 @@ class HistoryRow extends Component {
); );
} }
if (name === 'customFormats') {
return (
<TableRowCell key={name}>
<BookFormats
formats={customFormats}
/>
</TableRowCell>
);
}
if (name === 'date') { if (name === 'date') {
return ( return (
<RelativeDateCellConnector <RelativeDateCellConnector
@@ -173,24 +158,6 @@ class HistoryRow extends Component {
); );
} }
if (name === 'customFormatScore') {
return (
<TableRowCell
key={name}
className={styles.customFormatScore}
>
<Tooltip
anchor={formatPreferredWordScore(
customFormatScore,
customFormats.length
)}
tooltip={<BookFormats formats={customFormats} />}
position={tooltipPositions.BOTTOM}
/>
</TableRowCell>
);
}
if (name === 'releaseGroup') { if (name === 'releaseGroup') {
return ( return (
<TableRowCell <TableRowCell
@@ -202,16 +169,6 @@ class HistoryRow extends Component {
); );
} }
if (name === 'sourceTitle') {
return (
<TableRowCell
key={name}
>
{sourceTitle}
</TableRowCell>
);
}
if (name === 'details') { if (name === 'details') {
return ( return (
<TableRowCell <TableRowCell
@@ -252,8 +209,6 @@ HistoryRow.propTypes = {
author: PropTypes.object.isRequired, author: PropTypes.object.isRequired,
book: PropTypes.object, book: PropTypes.object,
quality: PropTypes.object.isRequired, quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
customFormatScore: PropTypes.number.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired, qualityCutoffNotMet: PropTypes.bool.isRequired,
eventType: PropTypes.string.isRequired, eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired, sourceTitle: PropTypes.string.isRequired,
@@ -267,8 +222,4 @@ HistoryRow.propTypes = {
onMarkAsFailedPress: PropTypes.func.isRequired onMarkAsFailedPress: PropTypes.func.isRequired
}; };
HistoryRow.defaultProps = {
customFormats: []
};
export default HistoryRow; export default HistoryRow;
@@ -49,7 +49,7 @@ class HistoryRowConnector extends Component {
onMarkAsFailedPress = () => { onMarkAsFailedPress = () => {
this.props.markAsFailed({ id: this.props.id }); this.props.markAsFailed({ id: this.props.id });
}; }
// //
// Render // Render
@@ -1,13 +1,13 @@
.torrent { .torrent {
composes: label from '~Components/Label.css'; composes: label from '~Components/Label.css';
border-color: var(--torrentColor); border-color: $torrentColor;
background-color: var(--torrentColor); background-color: $torrentColor;
} }
.usenet { .usenet {
composes: label from '~Components/Label.css'; composes: label from '~Components/Label.css';
border-color: var(--usenetColor); border-color: $usenetColor;
background-color: var(--usenetColor); background-color: $usenetColor;
} }
-8
View File
@@ -1,8 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'torrent': string;
'usenet': string;
}
export const cssExports: CssExports;
export default cssExports;
+20 -35
View File
@@ -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 LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody'; import PageContentBody from 'Components/Page/PageContentBody';
@@ -13,7 +12,7 @@ import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody'; import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import TablePager from 'Components/Table/TablePager'; import TablePager from 'Components/Table/TablePager';
import { align, icons, kinds } from 'Helpers/Props'; import { align, icons } from 'Helpers/Props';
import getRemovedItems from 'Utilities/Object/getRemovedItems'; import getRemovedItems from 'Utilities/Object/getRemovedItems';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
@@ -76,23 +75,13 @@ class Queue extends Component {
return; return;
} }
const nextState = {};
if (prevProps.items !== items) {
nextState.items = items;
}
const selectedIds = this.getSelectedIds(); const selectedIds = this.getSelectedIds();
const isPendingSelected = _.some(this.props.items, (item) => { const isPendingSelected = _.some(this.props.items, (item) => {
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay'; return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
}); });
if (isPendingSelected !== this.state.isPendingSelected) { if (isPendingSelected !== this.state.isPendingSelected) {
nextState.isPendingSelected = isPendingSelected; this.setState({ isPendingSelected });
}
if (!_.isEmpty(nextState)) {
this.setState(nextState);
} }
} }
@@ -101,45 +90,45 @@ class Queue extends Component {
getSelectedIds = () => { getSelectedIds = () => {
return getSelectedIds(this.state.selectedState); return getSelectedIds(this.state.selectedState);
}; }
// //
// Listeners // Listeners
onQueueRowModalOpenOrClose = (isOpen) => { onQueueRowModalOpenOrClose = (isOpen) => {
this._shouldBlockRefresh = isOpen; this._shouldBlockRefresh = isOpen;
}; }
onSelectAllChange = ({ value }) => { onSelectAllChange = ({ value }) => {
this.setState(selectAll(this.state.selectedState, value)); this.setState(selectAll(this.state.selectedState, value));
}; }
onSelectedChange = ({ id, value, shiftKey = false }) => { onSelectedChange = ({ id, value, shiftKey = false }) => {
this.setState((state) => { this.setState((state) => {
return toggleSelected(state, this.props.items, id, value, shiftKey); return toggleSelected(state, this.props.items, id, value, shiftKey);
}); });
}; }
onGrabSelectedPress = () => { onGrabSelectedPress = () => {
this.props.onGrabSelectedPress(this.getSelectedIds()); this.props.onGrabSelectedPress(this.getSelectedIds());
}; }
onRemoveSelectedPress = () => { onRemoveSelectedPress = () => {
this.setState({ isConfirmRemoveModalOpen: true }, () => { this.setState({ isConfirmRemoveModalOpen: true }, () => {
this._shouldBlockRefresh = true; this._shouldBlockRefresh = true;
}); });
}; }
onRemoveSelectedConfirmed = (payload) => { onRemoveSelectedConfirmed = (payload) => {
this._shouldBlockRefresh = false; this._shouldBlockRefresh = false;
this.props.onRemoveSelectedPress({ ids: this.getSelectedIds(), ...payload }); this.props.onRemoveSelectedPress({ ids: this.getSelectedIds(), ...payload });
this.setState({ isConfirmRemoveModalOpen: false }); this.setState({ isConfirmRemoveModalOpen: false });
}; }
onConfirmRemoveModalClose = () => { onConfirmRemoveModalClose = () => {
this._shouldBlockRefresh = false; this._shouldBlockRefresh = false;
this.setState({ isConfirmRemoveModalOpen: false }); this.setState({ isConfirmRemoveModalOpen: false });
}; }
// //
// Render // Render
@@ -227,29 +216,26 @@ class Queue extends Component {
<PageContentBody> <PageContentBody>
{ {
isRefreshing && !isAllPopulated ? isRefreshing && !isAllPopulated &&
<LoadingIndicator /> : <LoadingIndicator />
null
} }
{ {
!isRefreshing && hasError ? !isRefreshing && hasError &&
<Alert kind={kinds.DANGER}> <div>
{translate('FailedToLoadQueue')} {translate('FailedToLoadQueue')}
</Alert> : </div>
null
} }
{ {
isAllPopulated && !hasError && !items.length ? isAllPopulated && !hasError && !items.length &&
<Alert kind={kinds.INFO}> <div>
{translate('QueueIsEmpty')} {translate('QueueIsEmpty')}
</Alert> : </div>
null
} }
{ {
isAllPopulated && !hasError && !!items.length ? isAllPopulated && !hasError && !!items.length &&
<div> <div>
<Table <Table
columns={columns} columns={columns}
@@ -284,8 +270,7 @@ class Queue extends Component {
isFetching={isRefreshing} isFetching={isRefreshing}
{...otherProps} {...otherProps}
/> />
</div> : </div>
null
} }
</PageContentBody> </PageContentBody>
+11 -11
View File
@@ -79,34 +79,34 @@ class QueueConnector extends Component {
repopulate = () => { repopulate = () => {
this.props.fetchQueue(); this.props.fetchQueue();
}; }
// //
// Listeners // Listeners
onFirstPagePress = () => { onFirstPagePress = () => {
this.props.gotoQueueFirstPage(); this.props.gotoQueueFirstPage();
}; }
onPreviousPagePress = () => { onPreviousPagePress = () => {
this.props.gotoQueuePreviousPage(); this.props.gotoQueuePreviousPage();
}; }
onNextPagePress = () => { onNextPagePress = () => {
this.props.gotoQueueNextPage(); this.props.gotoQueueNextPage();
}; }
onLastPagePress = () => { onLastPagePress = () => {
this.props.gotoQueueLastPage(); this.props.gotoQueueLastPage();
}; }
onPageSelect = (page) => { onPageSelect = (page) => {
this.props.gotoQueuePage({ page }); this.props.gotoQueuePage({ page });
}; }
onSortPress = (sortKey) => { onSortPress = (sortKey) => {
this.props.setQueueSort({ sortKey }); this.props.setQueueSort({ sortKey });
}; }
onTableOptionChange = (payload) => { onTableOptionChange = (payload) => {
this.props.setQueueTableOption(payload); this.props.setQueueTableOption(payload);
@@ -114,21 +114,21 @@ class QueueConnector extends Component {
if (payload.pageSize) { if (payload.pageSize) {
this.props.gotoQueueFirstPage(); this.props.gotoQueueFirstPage();
} }
}; }
onRefreshPress = () => { onRefreshPress = () => {
this.props.executeCommand({ this.props.executeCommand({
name: commandNames.REFRESH_MONITORED_DOWNLOADS name: commandNames.REFRESH_MONITORED_DOWNLOADS
}); });
}; }
onGrabSelectedPress = (ids) => { onGrabSelectedPress = (ids) => {
this.props.grabQueueItems({ ids }); this.props.grabQueueItems({ ids });
}; }
onRemoveSelectedPress = (payload) => { onRemoveSelectedPress = (payload) => {
this.props.removeQueueItems(payload); this.props.removeQueueItems(payload);
}; }
// //
// Render // Render
+1 -1
View File
@@ -42,7 +42,7 @@ class QueueOptions extends Component {
[name]: value [name]: value
}); });
}); });
}; }
// //
// Render // Render
-6
View File
@@ -16,12 +16,6 @@
width: 150px; width: 150px;
} }
.customFormatScore {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 55px;
}
.actions { .actions {
composes: cell from '~Components/Table/Cells/TableRowCell.css'; composes: cell from '~Components/Table/Cells/TableRowCell.css';
-11
View File
@@ -1,11 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'actions': string;
'customFormatScore': string;
'progress': string;
'protocol': string;
'quality': string;
}
export const cssExports: CssExports;
export default cssExports;
+5 -41
View File
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import ProtocolLabel from 'Activity/Queue/ProtocolLabel'; import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
import AuthorNameLink from 'Author/AuthorNameLink'; import AuthorNameLink from 'Author/AuthorNameLink';
import BookFormats from 'Book/BookFormats';
import BookQuality from 'Book/BookQuality'; import BookQuality from 'Book/BookQuality';
import BookTitleLink from 'Book/BookTitleLink'; import BookTitleLink from 'Book/BookTitleLink';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
@@ -14,11 +13,9 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow'; import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover'; import Popover from 'Components/Tooltip/Popover';
import Tooltip from 'Components/Tooltip/Tooltip';
import { icons, kinds, tooltipPositions } from 'Helpers/Props'; import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal'; import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import QueueStatusCell from './QueueStatusCell'; import QueueStatusCell from './QueueStatusCell';
import RemoveQueueItemModal from './RemoveQueueItemModal'; import RemoveQueueItemModal from './RemoveQueueItemModal';
@@ -44,7 +41,7 @@ class QueueRow extends Component {
onRemoveQueueItemPress = () => { onRemoveQueueItemPress = () => {
this.setState({ isRemoveQueueItemModalOpen: true }); this.setState({ isRemoveQueueItemModalOpen: true });
}; }
onRemoveQueueItemModalConfirmed = (blocklist, skipredownload) => { onRemoveQueueItemModalConfirmed = (blocklist, skipredownload) => {
const { const {
@@ -56,25 +53,25 @@ class QueueRow extends Component {
onRemoveQueueItemPress(blocklist, skipredownload); onRemoveQueueItemPress(blocklist, skipredownload);
this.setState({ isRemoveQueueItemModalOpen: false }); this.setState({ isRemoveQueueItemModalOpen: false });
}; }
onRemoveQueueItemModalClose = () => { onRemoveQueueItemModalClose = () => {
this.props.onQueueRowModalOpenOrClose(false); this.props.onQueueRowModalOpenOrClose(false);
this.setState({ isRemoveQueueItemModalOpen: false }); this.setState({ isRemoveQueueItemModalOpen: false });
}; }
onInteractiveImportPress = () => { onInteractiveImportPress = () => {
this.props.onQueueRowModalOpenOrClose(true); this.props.onQueueRowModalOpenOrClose(true);
this.setState({ isInteractiveImportModalOpen: true }); this.setState({ isInteractiveImportModalOpen: true });
}; }
onInteractiveImportModalClose = () => { onInteractiveImportModalClose = () => {
this.props.onQueueRowModalOpenOrClose(false); this.props.onQueueRowModalOpenOrClose(false);
this.setState({ isInteractiveImportModalOpen: false }); this.setState({ isInteractiveImportModalOpen: false });
}; }
// //
// Render // Render
@@ -92,8 +89,6 @@ class QueueRow extends Component {
author, author,
book, book,
quality, quality,
customFormats,
customFormatScore,
protocol, protocol,
indexer, indexer,
outputPath, outputPath,
@@ -215,34 +210,6 @@ class QueueRow extends Component {
); );
} }
if (name === 'customFormats') {
return (
<TableRowCell key={name}>
<BookFormats
formats={customFormats}
/>
</TableRowCell>
);
}
if (name === 'customFormatScore') {
return (
<TableRowCell
key={name}
className={styles.customFormatScore}
>
<Tooltip
anchor={formatPreferredWordScore(
customFormatScore,
customFormats.length
)}
tooltip={<BookFormats formats={customFormats} />}
position={tooltipPositions.BOTTOM}
/>
</TableRowCell>
);
}
if (name === 'protocol') { if (name === 'protocol') {
return ( return (
<TableRowCell key={name}> <TableRowCell key={name}>
@@ -412,8 +379,6 @@ QueueRow.propTypes = {
author: PropTypes.object, author: PropTypes.object,
book: PropTypes.object, book: PropTypes.object,
quality: PropTypes.object.isRequired, quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
customFormatScore: PropTypes.number.isRequired,
protocol: PropTypes.string.isRequired, protocol: PropTypes.string.isRequired,
indexer: PropTypes.string, indexer: PropTypes.string,
outputPath: PropTypes.string, outputPath: PropTypes.string,
@@ -438,7 +403,6 @@ QueueRow.propTypes = {
}; };
QueueRow.defaultProps = { QueueRow.defaultProps = {
customFormats: [],
isGrabbing: false, isGrabbing: false,
isRemoving: false isRemoving: false
}; };
@@ -41,11 +41,11 @@ class QueueRowConnector extends Component {
onGrabPress = () => { onGrabPress = () => {
this.props.grabQueueItem({ id: this.props.id }); this.props.grabQueueItem({ id: this.props.id });
}; }
onRemoveQueueItemPress = (payload) => { onRemoveQueueItemPress = (payload) => {
this.props.removeQueueItem({ id: this.props.id, ...payload }); this.props.removeQueueItem({ id: this.props.id, ...payload });
}; }
// //
// Render // Render
-7
View File
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'status': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -36,34 +36,34 @@ class RemoveQueueItemModal extends Component {
blocklist: false, blocklist: false,
skipredownload: false skipredownload: false
}); });
}; }
// //
// Listeners // Listeners
onRemoveChange = ({ value }) => { onRemoveChange = ({ value }) => {
this.setState({ remove: value }); this.setState({ remove: value });
}; }
onBlocklistChange = ({ value }) => { onBlocklistChange = ({ value }) => {
this.setState({ blocklist: value }); this.setState({ blocklist: value });
}; }
onSkipReDownloadChange = ({ value }) => { onSkipReDownloadChange = ({ value }) => {
this.setState({ skipredownload: value }); this.setState({ skipredownload: value });
}; }
onRemoveConfirmed = () => { onRemoveConfirmed = () => {
const state = this.state; const state = this.state;
this.resetState(); this.resetState();
this.props.onRemovePress(state); this.props.onRemovePress(state);
}; }
onModalClose = () => { onModalClose = () => {
this.resetState(); this.resetState();
this.props.onModalClose(); this.props.onModalClose();
}; }
// //
// Render // Render
@@ -124,7 +124,7 @@ class RemoveQueueItemModal extends Component {
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="blocklist" name="blocklist"
value={blocklist} value={blocklist}
helpText={translate('BlocklistReleaseHelpText')} helpText={translate('BlocklistHelpText')}
onChange={this.onBlocklistChange} onChange={this.onBlocklistChange}
/> />
</FormGroup> </FormGroup>
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'message': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -31,40 +31,40 @@ class RemoveQueueItemsModal extends Component {
// //
// Control // Control
resetState = function() { resetState = function() {
this.setState({ this.setState({
remove: true, remove: true,
blocklist: false, blocklist: false,
skipredownload: false skipredownload: false
}); });
}; }
// //
// Listeners // Listeners
onRemoveChange = ({ value }) => { onRemoveChange = ({ value }) => {
this.setState({ remove: value }); this.setState({ remove: value });
}; }
onBlocklistChange = ({ value }) => { onBlocklistChange = ({ value }) => {
this.setState({ blocklist: value }); this.setState({ blocklist: value });
}; }
onSkipReDownloadChange = ({ value }) => { onSkipReDownloadChange = ({ value }) => {
this.setState({ skipredownload: value }); this.setState({ skipredownload: value });
}; }
onRemoveConfirmed = () => { onRemoveConfirmed = () => {
const state = this.state; const state = this.state;
this.resetState(); this.resetState();
this.props.onRemovePress(state); this.props.onRemovePress(state);
}; }
onModalClose = () => { onModalClose = () => {
this.resetState(); this.resetState();
this.props.onModalClose(); this.props.onModalClose();
}; }
// //
// Render // Render
@@ -89,12 +89,12 @@ class RemoveQueueItemsModal extends Component {
onModalClose={this.onModalClose} onModalClose={this.onModalClose}
> >
<ModalHeader> <ModalHeader>
{selectedCount > 1 ? translate('RemoveSelectedItems') : translate('RemoveSelectedItem')} Remove Selected Item{selectedCount > 1 ? 's' : ''}
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
<div className={styles.message}> <div className={styles.message}>
{selectedCount > 1 ? translate('RemoveSelectedItemsQueueMessageText', selectedCount) : translate('RemoveSelectedItemQueueMessageText')} Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue?
</div> </div>
{ {
@@ -118,14 +118,14 @@ class RemoveQueueItemsModal extends Component {
<FormGroup> <FormGroup>
<FormLabel> <FormLabel>
{selectedCount > 1 ? translate('BlocklistReleases') : translate('BlocklistRelease')} Add Release{selectedCount > 1 ? 's' : ''} To Blocklist
</FormLabel> </FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="blocklist" name="blocklist"
value={blocklist} value={blocklist}
helpText={translate('BlocklistReleaseHelpText')} helpText={translate('BlocklistHelpText')}
onChange={this.onBlocklistChange} onChange={this.onBlocklistChange}
/> />
</FormGroup> </FormGroup>
@@ -150,14 +150,14 @@ class RemoveQueueItemsModal extends Component {
<ModalFooter> <ModalFooter>
<Button onPress={this.onModalClose}> <Button onPress={this.onModalClose}>
{translate('Close')} Close
</Button> </Button>
<Button <Button
kind={kinds.DANGER} kind={kinds.DANGER}
onPress={this.onRemoveConfirmed} onPress={this.onRemoveConfirmed}
> >
{translate('Remove')} Remove
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>
-7
View File
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'timeleft': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -8,17 +8,17 @@ function AuthorMonitorNewItemsOptionsPopoverContent() {
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
title={translate('AllBooks')} title={translate('AllBooks')}
data={translate('DataNewAllBooks')} data="Monitor all new books"
/> />
<DescriptionListItem <DescriptionListItem
title={translate('NewBooks')} title={translate('NewBooks')}
data={translate('DataNewBooks')} data="Monitor new books released after the newest existing book"
/> />
<DescriptionListItem <DescriptionListItem
title={translate('None')} title={translate('None')}
data={translate('DataNewNone')} data="Don't monitor any new books"
/> />
</DescriptionList> </DescriptionList>
); );
@@ -8,42 +8,42 @@ function AuthorMonitoringOptionsPopoverContent() {
return ( return (
<> <>
<Alert> <Alert>
{translate('MonitoringOptionsHelpText')} This is a one time adjustment to set which books are monitored
</Alert> </Alert>
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
title={translate('AllBooks')} title={translate('AllBooks')}
data={translate('DataAllBooks')} data="Monitor all books"
/> />
<DescriptionListItem <DescriptionListItem
title={translate('FutureBooks')} title={translate('FutureBooks')}
data={translate('DataFutureBooks')} data="Monitor books that have not released yet"
/> />
<DescriptionListItem <DescriptionListItem
title={translate('MissingBooks')} title={translate('MissingBooks')}
data={translate('DataMissingBooks')} data="Monitor books that do not have files or have not released yet"
/> />
<DescriptionListItem <DescriptionListItem
title={translate('ExistingBooks')} title={translate('ExistingBooks')}
data={translate('DataExistingBooks')} data="Monitor books that have files or have not released yet"
/> />
<DescriptionListItem <DescriptionListItem
title={translate('FirstBook')} title={translate('FirstBook')}
data={translate('DataFirstBook')} data="Monitor the first book. All other books will be ignored"
/> />
<DescriptionListItem <DescriptionListItem
title={translate('LatestBook')} title={translate('LatestBook')}
data={translate('DataLatestBook')} data="Monitor the latest book and future books"
/> />
<DescriptionListItem <DescriptionListItem
title={translate('None')} title={translate('None')}
data={translate('DataNone')} data="No books will be monitored"
/> />
</DescriptionList> </DescriptionList>
</> </>
+6 -10
View File
@@ -4,19 +4,16 @@ import React from 'react';
import DocumentTitle from 'react-document-title'; import DocumentTitle from 'react-document-title';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import PageConnector from 'Components/Page/PageConnector'; import PageConnector from 'Components/Page/PageConnector';
import ApplyTheme from './ApplyTheme';
import AppRoutes from './AppRoutes'; import AppRoutes from './AppRoutes';
function App({ store, history, hasTranslationsError }) { function App({ store, history }) {
return ( return (
<DocumentTitle title={window.Readarr.instanceName}> <DocumentTitle title="Readarr">
<Provider store={store}> <Provider store={store}>
<ConnectedRouter history={history}> <ConnectedRouter history={history}>
<ApplyTheme> <PageConnector>
<PageConnector hasTranslationsError={hasTranslationsError}> <AppRoutes app={App} />
<AppRoutes app={App} /> </PageConnector>
</PageConnector>
</ApplyTheme>
</ConnectedRouter> </ConnectedRouter>
</Provider> </Provider>
</DocumentTitle> </DocumentTitle>
@@ -25,8 +22,7 @@ function App({ store, history, hasTranslationsError }) {
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;
+2 -8
View File
@@ -13,7 +13,6 @@ import CalendarPageConnector from 'Calendar/CalendarPageConnector';
import NotFound from 'Components/NotFound'; import NotFound from 'Components/NotFound';
import Switch from 'Components/Router/Switch'; import Switch from 'Components/Router/Switch';
import AddNewItemConnector from 'Search/AddNewItemConnector'; import AddNewItemConnector from 'Search/AddNewItemConnector';
import CustomFormatSettingsConnector from 'Settings/CustomFormats/CustomFormatSettingsConnector';
import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector'; import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector';
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector'; import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector'; import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
@@ -23,7 +22,7 @@ import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementCo
import MetadataSettings from 'Settings/Metadata/MetadataSettings'; import MetadataSettings from 'Settings/Metadata/MetadataSettings';
import NotificationSettings from 'Settings/Notifications/NotificationSettings'; import NotificationSettings from 'Settings/Notifications/NotificationSettings';
import Profiles from 'Settings/Profiles/Profiles'; import Profiles from 'Settings/Profiles/Profiles';
import QualityConnector from 'Settings/Quality/QualityConnector'; import Quality from 'Settings/Quality/Quality';
import Settings from 'Settings/Settings'; import Settings from 'Settings/Settings';
import TagSettings from 'Settings/Tags/TagSettings'; import TagSettings from 'Settings/Tags/TagSettings';
import UISettingsConnector from 'Settings/UI/UISettingsConnector'; import UISettingsConnector from 'Settings/UI/UISettingsConnector';
@@ -173,12 +172,7 @@ function AppRoutes(props) {
<Route <Route
path="/settings/quality" path="/settings/quality"
component={QualityConnector} component={Quality}
/>
<Route
path="/settings/customformats"
component={CustomFormatSettingsConnector}
/> />
<Route <Route
-9
View File
@@ -1,9 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'changes': string;
'maintenance': string;
'version': string;
}
export const cssExports: CssExports;
export default cssExports;
+1 -40
View File
@@ -11,47 +11,9 @@ import UpdateChanges from 'System/Updates/UpdateChanges';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './AppUpdatedModalContent.css'; import styles from './AppUpdatedModalContent.css';
function mergeUpdates(items, version, prevVersion) {
let installedIndex = items.findIndex((u) => u.version === version);
let installedPreviouslyIndex = items.findIndex((u) => u.version === prevVersion);
if (installedIndex === -1) {
installedIndex = 0;
}
if (installedPreviouslyIndex === -1) {
installedPreviouslyIndex = items.length;
} else if (installedPreviouslyIndex === installedIndex && items.length) {
installedPreviouslyIndex += 1;
}
const appliedUpdates = items.slice(installedIndex, installedPreviouslyIndex);
if (!appliedUpdates.length) {
return null;
}
const appliedChanges = { new: [], fixed: [] };
appliedUpdates.forEach((u) => {
if (u.changes) {
appliedChanges.new.push(... u.changes.new);
appliedChanges.fixed.push(... u.changes.fixed);
}
});
const mergedUpdate = Object.assign({}, appliedUpdates[0], { changes: appliedChanges });
if (!appliedChanges.new.length && !appliedChanges.fixed.length) {
mergedUpdate.changes = null;
}
return mergedUpdate;
}
function AppUpdatedModalContent(props) { function AppUpdatedModalContent(props) {
const { const {
version, version,
prevVersion,
isPopulated, isPopulated,
error, error,
items, items,
@@ -59,7 +21,7 @@ function AppUpdatedModalContent(props) {
onModalClose onModalClose
} = props; } = props;
const update = mergeUpdates(items, version, prevVersion); const update = items[0];
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
@@ -129,7 +91,6 @@ function AppUpdatedModalContent(props) {
AppUpdatedModalContent.propTypes = { AppUpdatedModalContent.propTypes = {
version: PropTypes.string.isRequired, version: PropTypes.string.isRequired,
prevVersion: PropTypes.string,
isPopulated: PropTypes.bool.isRequired, isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object, error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
@@ -8,9 +8,8 @@ import AppUpdatedModalContent from './AppUpdatedModalContent';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state) => state.app.version, (state) => state.app.version,
(state) => state.app.prevVersion,
(state) => state.system.updates, (state) => state.system.updates,
(version, prevVersion, updates) => { (version, updates) => {
const { const {
isPopulated, isPopulated,
error, error,
@@ -19,7 +18,6 @@ function createMapStateToProps() {
return { return {
version, version,
prevVersion,
isPopulated, isPopulated,
error, error,
items items
-49
View File
@@ -1,49 +0,0 @@
import PropTypes from 'prop-types';
import React, { Fragment, useCallback, useEffect } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import themes from 'Styles/Themes';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.ui.item.theme || window.Readarr.theme,
(
theme
) => {
return {
theme
};
}
);
}
function ApplyTheme({ theme, children }) {
// Update the CSS Variables
const updateCSSVariables = useCallback(() => {
const arrayOfVariableKeys = Object.keys(themes[theme]);
const arrayOfVariableValues = Object.values(themes[theme]);
// Loop through each array key and set the CSS Variables
arrayOfVariableKeys.forEach((cssVariableKey, index) => {
// Based on our snippet from MDN
document.documentElement.style.setProperty(
`--${cssVariableKey}`,
arrayOfVariableValues[index]
);
});
}, [theme]);
// On Component Mount and Component Update
useEffect(() => {
updateCSSVariables(theme);
}, [updateCSSVariables, theme]);
return <Fragment>{children}</Fragment>;
}
ApplyTheme.propTypes = {
theme: PropTypes.string.isRequired,
children: PropTypes.object.isRequired
};
export default connect(createMapStateToProps)(ApplyTheme);
-7
View File
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'automatic': string;
}
export const cssExports: CssExports;
export default cssExports;
-5
View File
@@ -1,5 +0,0 @@
interface ModelBase {
id: number;
}
export default ModelBase;
-48
View File
@@ -1,48 +0,0 @@
import SortDirection from 'Helpers/Props/SortDirection';
export interface Error {
responseJSON: {
message: string;
};
}
export interface AppSectionDeleteState {
isDeleting: boolean;
deleteError: Error;
}
export interface AppSectionSaveState {
isSaving: boolean;
saveError: Error;
}
export interface PagedAppSectionState {
pageSize: number;
}
export interface AppSectionSchemaState<T> {
isSchemaFetching: boolean;
isSchemaPopulated: boolean;
schemaError: Error;
schema: {
items: T[];
};
}
export interface AppSectionItemState<T> {
isFetching: boolean;
isPopulated: boolean;
error: Error;
item: T;
}
interface AppSectionState<T> {
isFetching: boolean;
isPopulated: boolean;
error: Error;
items: T[];
sortKey: string;
sortDirection: SortDirection;
}
export default AppSectionState;
-41
View File
@@ -1,41 +0,0 @@
import SettingsAppState from './SettingsAppState';
import TagsAppState from './TagsAppState';
interface FilterBuilderPropOption {
id: string;
name: string;
}
export interface FilterBuilderProp<T> {
name: string;
label: string;
type: string;
valueType?: string;
optionsSelector?: (items: T[]) => FilterBuilderPropOption[];
}
export interface PropertyFilter {
key: string;
value: boolean | string | number | string[] | number[];
type: string;
}
export interface Filter {
key: string;
label: string;
filers: PropertyFilter[];
}
export interface CustomFilter {
id: number;
type: string;
label: string;
filers: PropertyFilter[];
}
interface AppState {
settings: SettingsAppState;
tags: TagsAppState;
}
export default AppState;
@@ -1,40 +0,0 @@
import AppSectionState, {
AppSectionDeleteState,
AppSectionSaveState,
} from 'App/State/AppSectionState';
import DownloadClient from 'typings/DownloadClient';
import ImportList from 'typings/ImportList';
import Indexer from 'typings/Indexer';
import Notification from 'typings/Notification';
import { UiSettings } from 'typings/UiSettings';
export interface DownloadClientAppState
extends AppSectionState<DownloadClient>,
AppSectionDeleteState,
AppSectionSaveState {}
export interface ImportListAppState
extends AppSectionState<ImportList>,
AppSectionDeleteState,
AppSectionSaveState {}
export interface IndexerAppState
extends AppSectionState<Indexer>,
AppSectionDeleteState,
AppSectionSaveState {}
export interface NotificationAppState
extends AppSectionState<Notification>,
AppSectionDeleteState {}
export type UiSettingsAppState = AppSectionState<UiSettings>;
interface SettingsAppState {
downloadClients: DownloadClientAppState;
importLists: ImportListAppState;
indexers: IndexerAppState;
notifications: NotificationAppState;
uiSettings: UiSettingsAppState;
}
export default SettingsAppState;
-12
View File
@@ -1,12 +0,0 @@
import ModelBase from 'App/ModelBase';
import AppSectionState, {
AppSectionDeleteState,
} from 'App/State/AppSectionState';
export interface Tag extends ModelBase {
label: string;
}
interface TagsAppState extends AppSectionState<Tag>, AppSectionDeleteState {}
export default TagsAppState;
+2 -2
View File
@@ -99,7 +99,7 @@ class AuthorImage extends Component {
if (this.props.onError) { if (this.props.onError) {
this.props.onError(); this.props.onError();
} }
}; }
onLoad = () => { onLoad = () => {
this.setState({ this.setState({
@@ -110,7 +110,7 @@ class AuthorImage extends Component {
if (this.props.onLoad) { if (this.props.onLoad) {
this.props.onLoad(); this.props.onLoad();
} }
}; }
// //
// Render // Render
@@ -8,5 +8,5 @@
.deleteFilesMessage { .deleteFilesMessage {
margin-top: 20px; margin-top: 20px;
color: var(--dangerColor); color: $dangerColor;
} }
@@ -1,9 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'deleteFilesMessage': string;
'pathContainer': string;
'pathIcon': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -33,11 +33,11 @@ class DeleteAuthorModalContent extends Component {
onDeleteFilesChange = ({ value }) => { onDeleteFilesChange = ({ value }) => {
this.setState({ deleteFiles: value }); this.setState({ deleteFiles: value });
}; }
onAddImportListExclusionChange = ({ value }) => { onAddImportListExclusionChange = ({ value }) => {
this.setState({ addImportListExclusion: value }); this.setState({ addImportListExclusion: value });
}; }
onDeleteAuthorConfirmed = () => { onDeleteAuthorConfirmed = () => {
const deleteFiles = this.state.deleteFiles; const deleteFiles = this.state.deleteFiles;
@@ -46,7 +46,7 @@ class DeleteAuthorModalContent extends Component {
this.setState({ deleteFiles: false }); this.setState({ deleteFiles: false });
this.setState({ addImportListExclusion: false }); this.setState({ addImportListExclusion: false });
this.props.onDeletePress(deleteFiles, addImportListExclusion); this.props.onDeletePress(deleteFiles, addImportListExclusion);
}; }
// //
// Render // Render
@@ -32,7 +32,7 @@ class DeleteAuthorModalContentConnector extends Component {
}); });
this.props.onModalClose(true); this.props.onModalClose(true);
}; }
// //
// Render // Render
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'alternateTitle': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -9,7 +9,7 @@
} }
.metadataMessage { .metadataMessage {
color: var(--helpTextColor); color: $helpTextColor;
text-align: center; text-align: center;
font-weight: 300; font-weight: 300;
font-size: 20px; font-size: 20px;
@@ -22,7 +22,7 @@
.tabList { .tabList {
margin: 0; margin: 0;
padding: 0; padding: 0;
border-bottom: 1px solid var(--lightGray); border-bottom: 1px solid $lightGray;
} }
.tab { .tab {
@@ -37,7 +37,7 @@
} }
.selectedTab { .selectedTab {
border-bottom: 4px solid var(--linkColor); border-bottom: 4px solid $linkColor;
} }
.tabContent { .tabContent {
@@ -63,7 +63,7 @@
white-space: nowrap; white-space: nowrap;
&:hover { &:hover {
color: var(--iconButtonHoverLightColor); color: $iconButtonHoverLightColor;
} }
} }
-17
View File
@@ -1,17 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'authorNavigationButton': string;
'authorNavigationButtons': string;
'authorUpButton': string;
'contentContainer': string;
'errorMessage': string;
'innerContentBody': string;
'metadataMessage': string;
'selectedTab': string;
'tab': string;
'tabContent': string;
'tabList': string;
}
export const cssExports: CssExports;
export default cssExports;
+26 -23
View File
@@ -99,69 +99,69 @@ class AuthorDetails extends Component {
} }
this.setState({ selectedState: newSelectedState, allSelected: isAllSelected, allUnselected: isAllUnselected }); this.setState({ selectedState: newSelectedState, allSelected: isAllSelected, allUnselected: isAllUnselected });
}; }
getSelectedIds = () => { getSelectedIds = () => {
return getSelectedIds(this.state.selectedState); return getSelectedIds(this.state.selectedState);
}; }
// //
// Listeners // Listeners
onOrganizePress = () => { onOrganizePress = () => {
this.setState({ isOrganizeModalOpen: true }); this.setState({ isOrganizeModalOpen: true });
}; }
onOrganizeModalClose = () => { onOrganizeModalClose = () => {
this.setState({ isOrganizeModalOpen: false }); this.setState({ isOrganizeModalOpen: false });
}; }
onRetagPress = () => { onRetagPress = () => {
this.setState({ isRetagModalOpen: true }); this.setState({ isRetagModalOpen: true });
}; }
onRetagModalClose = () => { onRetagModalClose = () => {
this.setState({ isRetagModalOpen: false }); this.setState({ isRetagModalOpen: false });
}; }
onInteractiveImportPress = () => { onInteractiveImportPress = () => {
this.setState({ isInteractiveImportModalOpen: true }); this.setState({ isInteractiveImportModalOpen: true });
}; }
onInteractiveImportModalClose = () => { onInteractiveImportModalClose = () => {
this.setState({ isInteractiveImportModalOpen: false }); this.setState({ isInteractiveImportModalOpen: false });
}; }
onEditAuthorPress = () => { onEditAuthorPress = () => {
this.setState({ isEditAuthorModalOpen: true }); this.setState({ isEditAuthorModalOpen: true });
}; }
onEditAuthorModalClose = () => { onEditAuthorModalClose = () => {
this.setState({ isEditAuthorModalOpen: false }); this.setState({ isEditAuthorModalOpen: false });
}; }
onDeleteAuthorPress = () => { onDeleteAuthorPress = () => {
this.setState({ this.setState({
isEditAuthorModalOpen: false, isEditAuthorModalOpen: false,
isDeleteAuthorModalOpen: true isDeleteAuthorModalOpen: true
}); });
}; }
onDeleteAuthorModalClose = () => { onDeleteAuthorModalClose = () => {
this.setState({ isDeleteAuthorModalOpen: false }); this.setState({ isDeleteAuthorModalOpen: false });
}; }
onMonitorOptionsPress = () => { onMonitorOptionsPress = () => {
this.setState({ isMonitorOptionsModalOpen: true }); this.setState({ isMonitorOptionsModalOpen: true });
}; }
onMonitorOptionsClose = () => { onMonitorOptionsClose = () => {
this.setState({ isMonitorOptionsModalOpen: false }); this.setState({ isMonitorOptionsModalOpen: false });
}; }
onBookEditorTogglePress = () => { onBookEditorTogglePress = () => {
this.setState({ isEditorActive: !this.state.isEditorActive }); this.setState({ isEditorActive: !this.state.isEditorActive });
}; }
onExpandAllPress = () => { onExpandAllPress = () => {
const { const {
@@ -170,7 +170,7 @@ class AuthorDetails extends Component {
} = this.state; } = this.state;
this.setState(getExpandedState(selectAll(expandedState, !allExpanded))); this.setState(getExpandedState(selectAll(expandedState, !allExpanded)));
}; }
onExpandPress = (bookId, isExpanded) => { onExpandPress = (bookId, isExpanded) => {
this.setState((state) => { this.setState((state) => {
@@ -184,32 +184,32 @@ class AuthorDetails extends Component {
return getExpandedState(newState); return getExpandedState(newState);
}); });
}; }
onSelectAllChange = ({ value }) => { onSelectAllChange = ({ value }) => {
this.setState(selectAll(this.state.selectedState, value)); this.setState(selectAll(this.state.selectedState, value));
}; }
onSelectAllPress = () => { onSelectAllPress = () => {
this.onSelectAllChange({ value: !this.state.allSelected }); this.onSelectAllChange({ value: !this.state.allSelected });
}; }
onSelectedChange = (items, id, value, shiftKey = false) => { onSelectedChange = (items, id, value, shiftKey = false) => {
this.setState((state) => { this.setState((state) => {
return toggleSelected(state, items, id, value, shiftKey); return toggleSelected(state, items, id, value, shiftKey);
}); });
}; }
onSaveSelected = (changes) => { onSaveSelected = (changes) => {
this.props.onSaveSelected({ this.props.onSaveSelected({
bookIds: this.getSelectedIds(), bookIds: this.getSelectedIds(),
...changes ...changes
}); });
}; }
onTabSelect = (index, lastIndex) => { onTabSelect = (index, lastIndex) => {
this.setState({ selectedTabIndex: index }); this.setState({ selectedTabIndex: index });
}; }
// //
// Render // Render
@@ -392,7 +392,10 @@ class AuthorDetails extends Component {
name={icons.ARROW_UP} name={icons.ARROW_UP}
size={30} size={30}
title={translate('GoToAuthorListing')} title={translate('GoToAuthorListing')}
to={'/'} to={{
pathname: '/',
state: { restoreScrollPosition: true }
}}
/> />
<IconButton <IconButton
@@ -270,7 +270,7 @@ class AuthorDetailsConnector extends Component {
this.props.fetchSeries({ authorId }); this.props.fetchSeries({ authorId });
this.props.fetchBookFiles({ authorId }); this.props.fetchBookFiles({ authorId });
this.props.fetchQueueDetails({ authorId }); this.props.fetchQueueDetails({ authorId });
}; }
unpopulate = () => { unpopulate = () => {
this.props.cancelFetchReleases(); this.props.cancelFetchReleases();
@@ -278,7 +278,7 @@ class AuthorDetailsConnector extends Component {
this.props.clearBookFiles(); this.props.clearBookFiles();
this.props.clearQueueDetails(); this.props.clearQueueDetails();
this.props.clearReleases(); this.props.clearReleases();
}; }
// //
// Listeners // Listeners
@@ -288,25 +288,25 @@ class AuthorDetailsConnector extends Component {
authorId: this.props.id, authorId: this.props.id,
monitored monitored
}); });
}; }
onRefreshPress = () => { onRefreshPress = () => {
this.props.executeCommand({ this.props.executeCommand({
name: commandNames.REFRESH_AUTHOR, name: commandNames.REFRESH_AUTHOR,
authorId: this.props.id authorId: this.props.id
}); });
}; }
onSearchPress = () => { onSearchPress = () => {
this.props.executeCommand({ this.props.executeCommand({
name: commandNames.AUTHOR_SEARCH, name: commandNames.AUTHOR_SEARCH,
authorId: this.props.id authorId: this.props.id
}); });
}; }
onSaveSelected = (payload) => { onSaveSelected = (payload) => {
this.props.saveBookEditor(payload); this.props.saveBookEditor(payload);
}; }
// //
// Render // Render
@@ -16,7 +16,7 @@
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: var(--black); background: $black;
opacity: 0.7; opacity: 0.7;
} }
@@ -25,7 +25,7 @@
padding: 30px; padding: 30px;
width: 100%; width: 100%;
height: 100%; height: 100%;
color: var(--white); color: $white;
} }
.poster { .poster {
@@ -69,7 +69,7 @@
width: 40px; width: 40px;
&:hover { &:hover {
color: var(--iconButtonHoverLightColor); color: $iconButtonHoverLightColor;
} }
} }
@@ -97,7 +97,7 @@
white-space: nowrap; white-space: nowrap;
&:hover { &:hover {
color: var(--iconButtonHoverLightColor); color: $iconButtonHoverLightColor;
} }
} }
@@ -1,29 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'alternateTitlesIconContainer': string;
'authorNavigationButton': string;
'authorNavigationButtons': string;
'authorUpButton': string;
'backdrop': string;
'backdropOverlay': string;
'details': string;
'detailsLabel': string;
'header': string;
'headerContent': string;
'info': string;
'links': string;
'monitorToggleButton': string;
'overview': string;
'path': string;
'poster': string;
'qualityProfileName': string;
'sizeOnDisk': string;
'tags': string;
'title': string;
'titleContainer': string;
'titleRow': string;
'toggleMonitoredContainer': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -52,11 +52,11 @@ class AuthorDetailsHeader extends Component {
onOverviewMeasure = ({ height }) => { onOverviewMeasure = ({ height }) => {
this.setState({ overviewHeight: height }); this.setState({ overviewHeight: height });
}; }
onTitleMeasure = ({ width }) => { onTitleMeasure = ({ width }) => {
this.setState({ titleWidth: width }); this.setState({ titleWidth: width });
}; }
// //
// Render // Render
@@ -92,7 +92,6 @@ class AuthorDetailsHeader extends Component {
titleWidth titleWidth
} = this.state; } = this.state;
const fanartUrl = getFanartUrl(images);
const marqueeWidth = titleWidth - (isSmallScreen ? 85 : 160); const marqueeWidth = titleWidth - (isSmallScreen ? 85 : 160);
const continuing = status === 'continuing'; const continuing = status === 'continuing';
@@ -109,11 +108,9 @@ class AuthorDetailsHeader extends Component {
<div className={styles.header} style={{ width }} > <div className={styles.header} style={{ width }} >
<div <div
className={styles.backdrop} className={styles.backdrop}
style={ style={{
fanartUrl ? backgroundImage: `url(${getFanartUrl(images)})`
{ backgroundImage: `url(${fanartUrl})` } : }}
null
}
> >
<div className={styles.backdropOverlay} /> <div className={styles.backdropOverlay} />
</div> </div>
@@ -48,7 +48,7 @@ class AuthorDetailsHeaderConnector extends Component {
authorId: this.props.authorId, authorId: this.props.authorId,
monitored monitored
}); });
}; }
// //
// Render // Render
@@ -1,9 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'link': string;
'linkLabel': string;
'links': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -74,7 +74,7 @@ class AuthorDetailsPageConnector extends Component {
if (isFetching && !isPopulated) { if (isFetching && !isPopulated) {
return ( return (
<PageContent title={translate('Loading')}> <PageContent title='loading'>
<PageContentBody> <PageContentBody>
<LoadingIndicator /> <LoadingIndicator />
</PageContentBody> </PageContentBody>
@@ -1,8 +1,8 @@
.bookType { .bookType {
margin-bottom: 20px; margin-bottom: 20px;
border: 1px solid var(--borderColor); border: 1px solid $borderColor;
border-radius: 4px; border-radius: 4px;
background-color: var(--cardBackgroundColor); background-color: $white;
&:last-of-type { &:last-of-type {
margin-bottom: 0; margin-bottom: 0;
@@ -77,7 +77,7 @@
.books { .books {
padding-top: 15px; padding-top: 15px;
border-top: 1px solid var(--borderColor); border-top: 1px solid $borderColor;
} }
.collapseButtonContainer { .collapseButtonContainer {
@@ -86,10 +86,10 @@
justify-content: center; justify-content: center;
padding: 10px 15px; padding: 10px 15px;
width: 100%; width: 100%;
border-top: 1px solid var(--borderColor); border-top: 1px solid $borderColor;
border-bottom-right-radius: 4px; border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px; border-bottom-left-radius: 4px;
background-color: var(--collapseButtonBackgroundColor); background-color: #fafafa;
} }
.collapseButtonIcon { .collapseButtonIcon {
@@ -1,23 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'actionButton': string;
'actionMenuIcon': string;
'actions': string;
'actionsMenu': string;
'actionsMenuContent': string;
'bookCount': string;
'bookType': string;
'bookTypeLabel': string;
'books': string;
'collapseButtonContainer': string;
'collapseButtonIcon': string;
'episodeCountTooltip': string;
'expandButton': string;
'expandButtonIcon': string;
'header': string;
'left': string;
'noBooks': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -61,7 +61,7 @@ class AuthorDetailsSeason extends Component {
this.setState({ lastToggledBook: bookId }); this.setState({ lastToggledBook: bookId });
this.props.onMonitorBookPress(_.uniq(bookIds), monitored); this.props.onMonitorBookPress(_.uniq(bookIds), monitored);
}; }
onSelectedChange = ({ id, value, shiftKey = false }) => { onSelectedChange = ({ id, value, shiftKey = false }) => {
const { const {
@@ -70,7 +70,7 @@ class AuthorDetailsSeason extends Component {
} = this.props; } = this.props;
return onSelectedChange(items, id, value, shiftKey); return onSelectedChange(items, id, value, shiftKey);
}; }
// //
// Render // Render
@@ -66,18 +66,18 @@ class AuthorDetailsSeasonConnector extends Component {
onTableOptionChange = (payload) => { onTableOptionChange = (payload) => {
this.props.setBooksTableOption(payload); this.props.setBooksTableOption(payload);
}; }
onSortPress = (sortKey) => { onSortPress = (sortKey) => {
this.props.setAuthorDetailsSort({ sortKey }); this.props.setAuthorDetailsSort({ sortKey });
}; }
onMonitorBookPress = (bookIds, monitored) => { onMonitorBookPress = (bookIds, monitored) => {
this.props.toggleBooksMonitored({ this.props.toggleBooksMonitored({
bookIds, bookIds,
monitored monitored
}); });
}; }
// //
// Render // Render
@@ -1,8 +1,8 @@
.bookType { .bookType {
margin-bottom: 20px; margin-bottom: 20px;
border: 1px solid var(--borderColor); border: 1px solid $borderColor;
border-radius: 4px; border-radius: 4px;
background-color: var(--cardBackgroundColor); background-color: $white;
&:last-of-type { &:last-of-type {
margin-bottom: 0; margin-bottom: 0;
@@ -77,7 +77,7 @@
.books { .books {
padding-top: 15px; padding-top: 15px;
border-top: 1px solid var(--borderColor); border-top: 1px solid $borderColor;
} }
.collapseButtonContainer { .collapseButtonContainer {
@@ -86,10 +86,10 @@
justify-content: center; justify-content: center;
padding: 10px 15px; padding: 10px 15px;
width: 100%; width: 100%;
border-top: 1px solid var(--borderColor); border-top: 1px solid $borderColor;
border-bottom-right-radius: 4px; border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px; border-bottom-left-radius: 4px;
background-color: var(--cardBackgroundColor); background-color: #fafafa;
} }
.collapseButtonIcon { .collapseButtonIcon {
@@ -1,24 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'actionButton': string;
'actionMenuIcon': string;
'actions': string;
'actionsMenu': string;
'actionsMenuContent': string;
'bookCount': string;
'bookType': string;
'bookTypeLabel': string;
'books': string;
'collapseButtonContainer': string;
'collapseButtonIcon': string;
'episodeCountTooltip': string;
'expandButton': string;
'expandButtonIcon': string;
'header': string;
'left': string;
'noBooks': string;
'seriesTitle': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -73,7 +73,7 @@ class AuthorDetailsSeries extends Component {
} = this.props; } = this.props;
this.props.onExpandPress(id, !isExpanded); this.props.onExpandPress(id, !isExpanded);
}; }
onMonitorBookPress = (bookId, monitored, { shiftKey }) => { onMonitorBookPress = (bookId, monitored, { shiftKey }) => {
const lastToggled = this.state.lastToggledBook; const lastToggled = this.state.lastToggledBook;
@@ -91,13 +91,13 @@ class AuthorDetailsSeries extends Component {
this.setState({ lastToggledBook: bookId }); this.setState({ lastToggledBook: bookId });
this.props.onMonitorBookPress(_.uniq(bookIds), monitored); this.props.onMonitorBookPress(_.uniq(bookIds), monitored);
}; }
onMonitorSeriesPress = (monitored, { shiftKey }) => { onMonitorSeriesPress = (monitored, { shiftKey }) => {
const bookIds = this.props.items.map((book) => book.id); const bookIds = this.props.items.map((book) => book.id);
this.props.onMonitorBookPress(_.uniq(bookIds), monitored); this.props.onMonitorBookPress(_.uniq(bookIds), monitored);
}; }
// //
// Render // Render
@@ -81,18 +81,18 @@ class AuthorDetailsSeasonConnector extends Component {
onTableOptionChange = (payload) => { onTableOptionChange = (payload) => {
this.props.setSeriesTableOption(payload); this.props.setSeriesTableOption(payload);
}; }
onSortPress = (sortKey) => { onSortPress = (sortKey) => {
this.props.dispatchSetSeriesSort({ sortKey }); this.props.dispatchSetSeriesSort({ sortKey });
}; }
onMonitorBookPress = (bookIds, monitored) => { onMonitorBookPress = (bookIds, monitored) => {
this.props.toggleBooksMonitored({ this.props.toggleBooksMonitored({
bookIds, bookIds,
monitored monitored
}); });
}; }
// //
// Render // Render
@@ -1,3 +1,4 @@
import _ from 'lodash';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createAuthorSelector from 'Store/Selectors/createAuthorSelector'; import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
@@ -9,11 +10,15 @@ function createMapStateToProps() {
createAuthorSelector(), createAuthorSelector(),
createTagsSelector(), createTagsSelector(),
(author, tagList) => { (author, tagList) => {
const tags = author.tags const tags = _.reduce(author.tags, (acc, tag) => {
.map((tagId) => tagList.find((tag) => tag.id === tagId)) const matchingTag = _.find(tagList, { id: tag });
.filter((tag) => !!tag)
.map((tag) => tag.label) if (matchingTag) {
.sort((a, b) => a.localeCompare(b)); acc.push(matchingTag.label);
}
return acc;
}, []);
return { return {
tags tags
-13
View File
@@ -1,13 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'monitored': string;
'pageCount': string;
'position': string;
'rating': string;
'releaseDate': string;
'status': string;
'title': string;
}
export const cssExports: CssExports;
export default cssExports;
+5 -5
View File
@@ -30,23 +30,23 @@ class BookRow extends Component {
onManualSearchPress = () => { onManualSearchPress = () => {
this.setState({ isDetailsModalOpen: true }); this.setState({ isDetailsModalOpen: true });
}; }
onDetailsModalClose = () => { onDetailsModalClose = () => {
this.setState({ isDetailsModalOpen: false }); this.setState({ isDetailsModalOpen: false });
}; }
onEditBookPress = () => { onEditBookPress = () => {
this.setState({ isEditBookModalOpen: true }); this.setState({ isEditBookModalOpen: true });
}; }
onEditBookModalClose = () => { onEditBookModalClose = () => {
this.setState({ isEditBookModalOpen: false }); this.setState({ isEditBookModalOpen: false });
}; }
onMonitorBookPress = (monitored, options) => { onMonitorBookPress = (monitored, options) => {
this.props.onMonitorBookPress(this.props.id, monitored, options); this.props.onMonitorBookPress(this.props.id, monitored, options);
}; }
// //
// Render // Render

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