mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-18 21:55:12 -04:00
Compare commits
5 Commits
v1.25.3.48
...
paging
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ca6f83a4d | ||
|
|
fb5b325271 | ||
|
|
ec8025c3dc | ||
|
|
b42bf2cf20 | ||
|
|
712d95e6ce |
@@ -1,13 +0,0 @@
|
|||||||
// This file is used to open the backend and frontend in the same workspace, which is necessary as
|
|
||||||
// the frontend has vscode settings that are distinct from the backend
|
|
||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"path": ".."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../frontend"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
||||||
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
|
|
||||||
{
|
|
||||||
"name": "Prowlarr",
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/dotnet:1-6.0",
|
|
||||||
"features": {
|
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
|
||||||
"nodeGypDependencies": true,
|
|
||||||
"version": "16",
|
|
||||||
"nvmVersion": "latest"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"forwardPorts": [9696],
|
|
||||||
"customizations": {
|
|
||||||
"vscode": {
|
|
||||||
"extensions": ["esbenp.prettier-vscode"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
@@ -267,7 +258,7 @@ dotnet_diagnostic.CA5401.severity = suggestion
|
|||||||
|
|
||||||
dotnet_diagnostic.SYSLIB0014.severity = none
|
dotnet_diagnostic.SYSLIB0014.severity = none
|
||||||
|
|
||||||
[*.{js,jsx,ts,tsx,html,hbs,less,css}]
|
[*.{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
.esprintrc
Normal file
9
.esprintrc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"frontend/src/**/*.js"
|
||||||
|
],
|
||||||
|
"ignored": [
|
||||||
|
"**/node_modules/**/*"
|
||||||
|
],
|
||||||
|
"port": 5004
|
||||||
|
}
|
||||||
9
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
9
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Discord first'
|
description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Reddit or Discord first'
|
||||||
labels: ['Type: Bug', 'Status: Needs Triage']
|
labels: ['Type: Bug', 'Status: Needs Triage']
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
@@ -71,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
|
|
||||||
|
|||||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -6,3 +6,6 @@ contact_links:
|
|||||||
- name: Support via Discord
|
- name: Support via Discord
|
||||||
url: https://prowlarr.com/discord
|
url: https://prowlarr.com/discord
|
||||||
about: Chat with users and devs on support and setup related topics.
|
about: Chat with users and devs on support and setup related topics.
|
||||||
|
- name: Support via Reddit
|
||||||
|
url: https://reddit.com/r/prowlarr
|
||||||
|
about: Discuss and search thru support topics.
|
||||||
|
|||||||
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@@ -1,12 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for more information:
|
|
||||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
# https://containers.dev/guide/dependabot
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "devcontainers"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
14
.github/label-actions.yml
vendored
14
.github/label-actions.yml
vendored
@@ -4,21 +4,13 @@
|
|||||||
comment: >
|
comment: >
|
||||||
:wave: @{issue-author}, we use the issue tracker exclusively
|
:wave: @{issue-author}, we use the issue tracker exclusively
|
||||||
for bug reports and feature requests. However, this issue appears
|
for bug reports and feature requests. However, this issue appears
|
||||||
to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord).
|
to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord)
|
||||||
|
or [Subreddit](https://reddit.com/r/prowlarr)
|
||||||
close: true
|
close: true
|
||||||
close-reason: 'not planned'
|
|
||||||
|
|
||||||
'Type: Indexer Request':
|
'Type: Indexer Request':
|
||||||
comment: >
|
comment: >
|
||||||
:wave: @{issue-author}, we use the issue tracker exclusively
|
:wave: @{issue-author}, we use the issue tracker exclusively
|
||||||
for bug reports and feature requests. However, this issue appears
|
for bug reports and feature requests. However, this issue appears
|
||||||
to be a indexer request. Please use our Indexer request [site](https://requests.prowlarr.com/)
|
to be a indexer request. Please use our Indexer request [site](https://requests.prowlarr.com/)
|
||||||
close: true
|
close: true
|
||||||
close-reason: 'not planned'
|
|
||||||
|
|
||||||
'Status: Logs Needed':
|
|
||||||
comment: >
|
|
||||||
:wave: @{issue-author}, in order to help you further we'll need to see logs.
|
|
||||||
You'll need to enable trace logging and replicate the problem that you encountered.
|
|
||||||
Guidance on how to enable trace logging can be found in
|
|
||||||
our [troubleshooting guide](https://wiki.servarr.com/prowlarr/troubleshooting#logging-and-log-files).
|
|
||||||
31
.github/labeler.yml
vendored
31
.github/labeler.yml
vendored
@@ -1,31 +0,0 @@
|
|||||||
'Area: API':
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- src/Prowlarr.Api.V1/**/*
|
|
||||||
|
|
||||||
'Area: Db-migration':
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- src/NzbDrone.Core/Datastore/Migration/*
|
|
||||||
|
|
||||||
'Area: Download Clients':
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- src/NzbDrone.Core/Download/Clients/**/*
|
|
||||||
|
|
||||||
'Area: Indexer':
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- src/NzbDrone.Core/Indexers/**/*
|
|
||||||
|
|
||||||
'Area: Notifications':
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- src/NzbDrone.Core/Notifications/**/*
|
|
||||||
|
|
||||||
'Area: UI':
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- frontend/**/*
|
|
||||||
- package.json
|
|
||||||
- yarn.lock
|
|
||||||
4
.github/workflows/label-actions.yml
vendored
4
.github/workflows/label-actions.yml
vendored
@@ -18,6 +18,6 @@ jobs:
|
|||||||
action:
|
action:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/label-actions@v4
|
- uses: dessant/label-actions@v2
|
||||||
with:
|
with:
|
||||||
process-only: 'issues, prs'
|
process-only: 'issues, prs'
|
||||||
12
.github/workflows/labeler.yml
vendored
12
.github/workflows/labeler.yml
vendored
@@ -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@v5
|
|
||||||
12
.github/workflows/lock.yml
vendored
12
.github/workflows/lock.yml
vendored
@@ -9,13 +9,13 @@ jobs:
|
|||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v5
|
- 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: ''
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -127,7 +127,6 @@ coverage*.xml
|
|||||||
coverage*.json
|
coverage*.json
|
||||||
setup/Output/
|
setup/Output/
|
||||||
*.~is
|
*.~is
|
||||||
.mono
|
|
||||||
|
|
||||||
# VS outout folders
|
# VS outout folders
|
||||||
bin
|
bin
|
||||||
|
|||||||
7
.vscode/extensions.json
vendored
7
.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"ms-dotnettools.csdevkit",
|
|
||||||
"ms-vscode-remote.remote-containers"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
26
.vscode/launch.json
vendored
26
.vscode/launch.json
vendored
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
|
||||||
// Use hover for the description of the existing attributes
|
|
||||||
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
|
|
||||||
"name": "Run Prowlarr",
|
|
||||||
"type": "coreclr",
|
|
||||||
"request": "launch",
|
|
||||||
"preLaunchTask": "build dotnet",
|
|
||||||
// If you have changed target frameworks, make sure to update the program path.
|
|
||||||
"program": "${workspaceFolder}/_output/net6.0/Prowlarr",
|
|
||||||
"args": [],
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"stopAtEntry": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": ".NET Core Attach",
|
|
||||||
"type": "coreclr",
|
|
||||||
"request": "attach"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
44
.vscode/tasks.json
vendored
44
.vscode/tasks.json
vendored
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "build dotnet",
|
|
||||||
"command": "dotnet",
|
|
||||||
"type": "process",
|
|
||||||
"args": [
|
|
||||||
"msbuild",
|
|
||||||
"-restore",
|
|
||||||
"${workspaceFolder}/src/Prowlarr.sln",
|
|
||||||
"-p:GenerateFullPaths=true",
|
|
||||||
"-p:Configuration=Debug",
|
|
||||||
"-p:Platform=Posix",
|
|
||||||
"-consoleloggerparameters:NoSummary;ForceNoAlign"
|
|
||||||
],
|
|
||||||
"problemMatcher": "$msCompile"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "publish",
|
|
||||||
"command": "dotnet",
|
|
||||||
"type": "process",
|
|
||||||
"args": [
|
|
||||||
"publish",
|
|
||||||
"${workspaceFolder}/src/Prowlarr.sln",
|
|
||||||
"-property:GenerateFullPaths=true",
|
|
||||||
"-consoleloggerparameters:NoSummary;ForceNoAlign"
|
|
||||||
],
|
|
||||||
"problemMatcher": "$msCompile"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "watch",
|
|
||||||
"command": "dotnet",
|
|
||||||
"type": "process",
|
|
||||||
"args": [
|
|
||||||
"watch",
|
|
||||||
"run",
|
|
||||||
"--project",
|
|
||||||
"${workspaceFolder}/src/Prowlarr.sln"
|
|
||||||
],
|
|
||||||
"problemMatcher": "$msCompile"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
# Prowlarr
|
# Prowlarr
|
||||||
|
|
||||||
[](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop)
|
[](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop)
|
||||||
[](https://translate.servarr.com/engage/servarr/?utm_source=widget)
|
[](https://translate.servarr.com/engage/prowlarr/?utm_source=widget)
|
||||||
[](https://wiki.servarr.com/prowlarr/installation/docker)
|
[](https://wiki.servarr.com/prowlarr/installation#docker)
|
||||||

|

|
||||||
[](#backers)
|
[](#backers)
|
||||||
[](#sponsors)
|
[](#sponsors)
|
||||||
@@ -29,6 +29,7 @@ Prowlarr is an indexer manager/proxy built on the popular \*arr .net/reactjs bas
|
|||||||
|
|
||||||
[](https://wiki.servarr.com/prowlarr)
|
[](https://wiki.servarr.com/prowlarr)
|
||||||
[](https://prowlarr.com/discord)
|
[](https://prowlarr.com/discord)
|
||||||
|
[](https://www.reddit.com/r/Prowlarr)
|
||||||
|
|
||||||
Note: GitHub Issues are for Bugs and Feature Requests Only
|
Note: GitHub Issues are for Bugs and Feature Requests Only
|
||||||
|
|
||||||
|
|||||||
@@ -9,28 +9,24 @@ variables:
|
|||||||
testsFolder: './_tests'
|
testsFolder: './_tests'
|
||||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||||
majorVersion: '1.25.3'
|
majorVersion: '1.3.1'
|
||||||
minorVersion: $[counter('minorVersion', 1)]
|
minorVersion: $[counter('minorVersion', 1)]
|
||||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||||
sentryOrg: 'servarr'
|
sentryOrg: 'servarr'
|
||||||
sentryUrl: 'https://sentry.servarr.com'
|
sentryUrl: 'https://sentry.servarr.com'
|
||||||
dotnetVersion: '6.0.427'
|
dotnetVersion: '6.0.405'
|
||||||
nodeVersion: '20.X'
|
innoVersion: '6.2.0'
|
||||||
innoVersion: '6.2.2'
|
nodeVersion: '16.x'
|
||||||
windowsImage: 'windows-2022'
|
windowsImage: 'windows-2022'
|
||||||
linuxImage: 'ubuntu-20.04'
|
linuxImage: 'ubuntu-20.04'
|
||||||
macImage: 'macOS-13'
|
macImage: 'macOS-11'
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
- develop
|
- develop
|
||||||
- master
|
- master
|
||||||
paths:
|
|
||||||
exclude:
|
|
||||||
- .github
|
|
||||||
- src/Prowlarr.Api.*/openapi.json
|
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
branches:
|
branches:
|
||||||
@@ -38,9 +34,8 @@ pr:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
exclude:
|
exclude:
|
||||||
- .github
|
|
||||||
- src/NzbDrone.Core/Localization/Core
|
- src/NzbDrone.Core/Localization/Core
|
||||||
- src/Prowlarr.Api.*/openapi.json
|
- src/Prowlarr.API.*/openapi.json
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- stage: Setup
|
- stage: Setup
|
||||||
@@ -166,10 +161,10 @@ stages:
|
|||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
steps:
|
steps:
|
||||||
- task: UseNode@1
|
- task: NodeTool@0
|
||||||
displayName: Set Node.js version
|
displayName: Set Node.js version
|
||||||
inputs:
|
inputs:
|
||||||
version: $(nodeVersion)
|
versionSpec: $(nodeVersion)
|
||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
@@ -354,7 +349,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)/Prowlarr.$(buildName).freebsd-core-x64.tar.gz'
|
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).freebsd-core-x64.tar.gz'
|
||||||
archiveType: 'tar'
|
archiveType: 'tar'
|
||||||
@@ -367,7 +362,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="Prowlarr@${PROWLARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
RELEASENAME="${PROWLARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
||||||
sentry-cli releases new --finalize -p prowlarr -p prowlarr-ui -p prowlarr-update "${RELEASENAME}"
|
sentry-cli releases new --finalize -p prowlarr -p prowlarr-ui -p prowlarr-update "${RELEASENAME}"
|
||||||
sentry-cli releases -p prowlarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
sentry-cli releases -p prowlarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
||||||
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
||||||
@@ -533,8 +528,8 @@ stages:
|
|||||||
testRunTitle: '$(testName) Unit Tests'
|
testRunTitle: '$(testName) Unit Tests'
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
|
|
||||||
- job: Unit_LinuxCore_Postgres14
|
- job: Unit_LinuxCore_Postgres
|
||||||
displayName: Unit Native LinuxCore with Postgres14 Database
|
displayName: Unit Native LinuxCore with Postgres Database
|
||||||
dependsOn: Prepare
|
dependsOn: Prepare
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
variables:
|
variables:
|
||||||
@@ -570,7 +565,6 @@ stages:
|
|||||||
-e POSTGRES_PASSWORD=prowlarr \
|
-e POSTGRES_PASSWORD=prowlarr \
|
||||||
-e POSTGRES_USER=prowlarr \
|
-e POSTGRES_USER=prowlarr \
|
||||||
-p 5432:5432/tcp \
|
-p 5432:5432/tcp \
|
||||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
|
||||||
postgres:14
|
postgres:14
|
||||||
displayName: Start postgres
|
displayName: Start postgres
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -583,60 +577,7 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: 'LinuxCore Postgres14 Unit Tests'
|
testRunTitle: 'LinuxCore Postgres Unit Tests'
|
||||||
failTaskOnFailedTests: true
|
|
||||||
|
|
||||||
- job: Unit_LinuxCore_Postgres15
|
|
||||||
displayName: Unit Native LinuxCore with Postgres15 Database
|
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
variables:
|
|
||||||
pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
|
|
||||||
artifactName: linux-x64-tests
|
|
||||||
Prowlarr__Postgres__Host: 'localhost'
|
|
||||||
Prowlarr__Postgres__Port: '5432'
|
|
||||||
Prowlarr__Postgres__User: 'prowlarr'
|
|
||||||
Prowlarr__Postgres__Password: 'prowlarr'
|
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: ${{ variables.linuxImage }}
|
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .net core'
|
|
||||||
inputs:
|
|
||||||
version: $(dotnetVersion)
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download Test Artifact
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: $(artifactName)
|
|
||||||
targetPath: $(testsFolder)
|
|
||||||
- bash: find ${TESTSFOLDER} -name "Prowlarr.Test.Dummy" -exec chmod a+x {} \;
|
|
||||||
displayName: Make Test Dummy Executable
|
|
||||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
|
||||||
- bash: |
|
|
||||||
docker run -d --name=postgres15 \
|
|
||||||
-e POSTGRES_PASSWORD=prowlarr \
|
|
||||||
-e POSTGRES_USER=prowlarr \
|
|
||||||
-p 5432:5432/tcp \
|
|
||||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
|
||||||
postgres:15
|
|
||||||
displayName: Start postgres
|
|
||||||
- bash: |
|
|
||||||
chmod a+x ${TESTSFOLDER}/test.sh
|
|
||||||
ls -lR ${TESTSFOLDER}
|
|
||||||
${TESTSFOLDER}/test.sh Linux Unit Test
|
|
||||||
displayName: Run Tests
|
|
||||||
- task: PublishTestResults@2
|
|
||||||
displayName: Publish Test Results
|
|
||||||
inputs:
|
|
||||||
testResultsFormat: 'NUnit'
|
|
||||||
testResultsFiles: '**/TestResult.xml'
|
|
||||||
testRunTitle: 'LinuxCore Postgres15 Unit Tests'
|
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
|
|
||||||
- stage: Integration
|
- stage: Integration
|
||||||
@@ -722,8 +663,8 @@ stages:
|
|||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
- job: Integration_LinuxCore_Postgres14
|
- job: Integration_LinuxCore_Postgres
|
||||||
displayName: Integration Native LinuxCore with Postgres14 Database
|
displayName: Integration Native LinuxCore with Postgres Database
|
||||||
dependsOn: Prepare
|
dependsOn: Prepare
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
variables:
|
variables:
|
||||||
@@ -769,7 +710,6 @@ stages:
|
|||||||
-e POSTGRES_PASSWORD=prowlarr \
|
-e POSTGRES_PASSWORD=prowlarr \
|
||||||
-e POSTGRES_USER=prowlarr \
|
-e POSTGRES_USER=prowlarr \
|
||||||
-p 5432:5432/tcp \
|
-p 5432:5432/tcp \
|
||||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
|
||||||
postgres:14
|
postgres:14
|
||||||
displayName: Start postgres
|
displayName: Start postgres
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -780,70 +720,7 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
|
testRunTitle: 'Integration LinuxCore Postgres Database Integration Tests'
|
||||||
failTaskOnFailedTests: true
|
|
||||||
displayName: Publish Test Results
|
|
||||||
|
|
||||||
|
|
||||||
- job: Integration_LinuxCore_Postgres15
|
|
||||||
displayName: Integration Native LinuxCore with Postgres Database
|
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
variables:
|
|
||||||
pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
|
|
||||||
Prowlarr__Postgres__Host: 'localhost'
|
|
||||||
Prowlarr__Postgres__Port: '5432'
|
|
||||||
Prowlarr__Postgres__User: 'prowlarr'
|
|
||||||
Prowlarr__Postgres__Password: 'prowlarr'
|
|
||||||
|
|
||||||
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/Prowlarr/. ./bin/
|
|
||||||
displayName: Move Package Contents
|
|
||||||
- bash: |
|
|
||||||
docker run -d --name=postgres15 \
|
|
||||||
-e POSTGRES_PASSWORD=prowlarr \
|
|
||||||
-e POSTGRES_USER=prowlarr \
|
|
||||||
-p 5432:5432/tcp \
|
|
||||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
|
||||||
postgres:15
|
|
||||||
displayName: Start postgres
|
|
||||||
- bash: |
|
|
||||||
chmod a+x ${TESTSFOLDER}/test.sh
|
|
||||||
${TESTSFOLDER}/test.sh Linux Integration Test
|
|
||||||
displayName: Run Integration Tests
|
|
||||||
- task: PublishTestResults@2
|
|
||||||
inputs:
|
|
||||||
testResultsFormat: 'NUnit'
|
|
||||||
testResultsFiles: '**/TestResult.xml'
|
|
||||||
testRunTitle: 'Integration LinuxCore Postgres15 Database Integration Tests'
|
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
@@ -1075,10 +952,10 @@ stages:
|
|||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
steps:
|
steps:
|
||||||
- task: UseNode@1
|
- task: NodeTool@0
|
||||||
displayName: Set Node.js version
|
displayName: Set Node.js version
|
||||||
inputs:
|
inputs:
|
||||||
version: $(nodeVersion)
|
versionSpec: $(nodeVersion)
|
||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
@@ -1169,7 +1046,7 @@ stages:
|
|||||||
submodules: true
|
submodules: true
|
||||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||||
displayName: Enable Windows Test Service
|
displayName: Enable Windows Test Service
|
||||||
- task: SonarCloudPrepare@2
|
- task: SonarCloudPrepare@1
|
||||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||||
inputs:
|
inputs:
|
||||||
SonarCloud: 'SonarCloud'
|
SonarCloud: 'SonarCloud'
|
||||||
@@ -1187,21 +1064,25 @@ stages:
|
|||||||
./build.sh --backend -f net6.0 -r win-x64
|
./build.sh --backend -f net6.0 -r win-x64
|
||||||
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||||
displayName: Coverage Unit Tests
|
displayName: Coverage Unit Tests
|
||||||
- task: SonarCloudAnalyze@2
|
- task: SonarCloudAnalyze@1
|
||||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||||
displayName: Publish SonarCloud Results
|
displayName: Publish SonarCloud Results
|
||||||
- task: reportgenerator@5
|
- task: reportgenerator@4
|
||||||
displayName: Generate Coverage Report
|
displayName: Generate Coverage Report
|
||||||
inputs:
|
inputs:
|
||||||
reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml'
|
reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml'
|
||||||
targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined'
|
targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined'
|
||||||
reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
|
reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
|
||||||
publishCodeCoverageResults: true
|
- task: PublishCodeCoverageResults@1
|
||||||
|
displayName: Publish Coverage Report
|
||||||
|
inputs:
|
||||||
|
codeCoverageTool: 'cobertura'
|
||||||
|
summaryFileLocation: './CoverageResults/combined/Cobertura.xml'
|
||||||
|
reportDirectory: './CoverageResults/combined/'
|
||||||
|
|
||||||
- stage: Report_Out
|
- stage: Report_Out
|
||||||
dependsOn:
|
dependsOn:
|
||||||
- Analyze
|
- Analyze
|
||||||
- Installer
|
|
||||||
- Unit_Test
|
- Unit_Test
|
||||||
- Integration
|
- Integration
|
||||||
- Automation
|
- Automation
|
||||||
|
|||||||
15
build.sh
15
build.sh
@@ -254,7 +254,7 @@ InstallInno()
|
|||||||
ProgressStart "Installing portable Inno Setup"
|
ProgressStart "Installing portable Inno Setup"
|
||||||
|
|
||||||
rm -rf _inno
|
rm -rf _inno
|
||||||
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.2}.exe"
|
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.0}.exe"
|
||||||
mkdir _inno
|
mkdir _inno
|
||||||
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
|
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
|
||||||
rm innosetup.exe
|
rm innosetup.exe
|
||||||
@@ -392,21 +392,22 @@ then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$LINT" = "YES" || "$FRONTEND" = "YES" ]];
|
if [ "$FRONTEND" = "YES" ];
|
||||||
then
|
then
|
||||||
YarnInstall
|
YarnInstall
|
||||||
|
RunWebpack
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$LINT" = "YES" ];
|
if [ "$LINT" = "YES" ];
|
||||||
then
|
then
|
||||||
|
if [ -z "$FRONTEND" ];
|
||||||
|
then
|
||||||
|
YarnInstall
|
||||||
|
fi
|
||||||
|
|
||||||
LintUI
|
LintUI
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$FRONTEND" = "YES" ];
|
|
||||||
then
|
|
||||||
RunWebpack
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$PACKAGES" = "YES" ];
|
if [ "$PACKAGES" = "YES" ];
|
||||||
then
|
then
|
||||||
UpdateVersionNumber
|
UpdateVersionNumber
|
||||||
|
|||||||
22
docs.sh
Executable file → Normal file
22
docs.sh
Executable file → Normal file
@@ -1,15 +1,11 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
FRAMEWORK="net6.0"
|
|
||||||
PLATFORM=$1
|
PLATFORM=$1
|
||||||
|
|
||||||
if [ "$PLATFORM" = "Windows" ]; then
|
if [ "$PLATFORM" = "Windows" ]; then
|
||||||
RUNTIME="win-x64"
|
RUNTIME="win-x64"
|
||||||
elif [ "$PLATFORM" = "Linux" ]; then
|
elif [ "$PLATFORM" = "Linux" ]; then
|
||||||
RUNTIME="linux-x64"
|
WHERE="linux-x64"
|
||||||
elif [ "$PLATFORM" = "Mac" ]; then
|
elif [ "$PLATFORM" = "Mac" ]; then
|
||||||
RUNTIME="osx-x64"
|
WHERE="osx-x64"
|
||||||
else
|
else
|
||||||
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -25,24 +21,18 @@ slnFile=src/Prowlarr.sln
|
|||||||
|
|
||||||
platform=Posix
|
platform=Posix
|
||||||
|
|
||||||
if [ "$PLATFORM" = "Windows" ]; then
|
|
||||||
application=Prowlarr.Console.dll
|
|
||||||
else
|
|
||||||
application=Prowlarr.dll
|
|
||||||
fi
|
|
||||||
|
|
||||||
dotnet clean $slnFile -c Debug
|
dotnet clean $slnFile -c Debug
|
||||||
dotnet clean $slnFile -c Release
|
dotnet clean $slnFile -c Release
|
||||||
|
|
||||||
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
||||||
|
|
||||||
dotnet new tool-manifest
|
dotnet new tool-manifest
|
||||||
dotnet tool install --version 6.6.2 Swashbuckle.AspNetCore.Cli
|
dotnet tool install --version 6.3.0 Swashbuckle.AspNetCore.Cli
|
||||||
|
|
||||||
dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v1 &
|
dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/net6.0/$RUNTIME/prowlarr.console.dll" v1 &
|
||||||
|
|
||||||
sleep 45
|
sleep 30
|
||||||
|
|
||||||
kill %1
|
kill %1
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
25
frontend/.csscomb.json
Normal file
25
frontend/.csscomb.json
Normal 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
frontend/.esformatter
Normal file
335
frontend/.esformatter
Normal 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,2 +1 @@
|
|||||||
**/JsLibraries/**
|
**/JsLibraries/**
|
||||||
**/*.css.d.ts
|
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
// eslint-disable @typescript-eslint/no-var-requires
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
|
||||||
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: {
|
||||||
@@ -26,8 +21,7 @@ module.exports = {
|
|||||||
globals: {
|
globals: {
|
||||||
expect: false,
|
expect: false,
|
||||||
chai: false,
|
chai: false,
|
||||||
sinon: false,
|
sinon: false
|
||||||
JSX: true
|
|
||||||
},
|
},
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
@@ -47,9 +41,7 @@ module.exports = {
|
|||||||
'react',
|
'react',
|
||||||
'react-hooks',
|
'react-hooks',
|
||||||
'simple-import-sort',
|
'simple-import-sort',
|
||||||
'import',
|
'import'
|
||||||
'@typescript-eslint',
|
|
||||||
'prettier'
|
|
||||||
],
|
],
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
@@ -232,7 +224,7 @@ module.exports = {
|
|||||||
'consistent-this': ['error', 'self'],
|
'consistent-this': ['error', 'self'],
|
||||||
'eol-last': 'error',
|
'eol-last': 'error',
|
||||||
'func-names': 'off',
|
'func-names': 'off',
|
||||||
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
|
'func-style': ['error', 'declaration'],
|
||||||
indent: ['error', 2, { SwitchCase: 1 }],
|
indent: ['error', 2, { SwitchCase: 1 }],
|
||||||
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
||||||
'keyword-spacing': ['error', { before: true, after: true }],
|
'keyword-spacing': ['error', { before: true, after: true }],
|
||||||
@@ -323,9 +315,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: [
|
files: ['*.js'],
|
||||||
'*.js'
|
|
||||||
],
|
|
||||||
rules: {
|
rules: {
|
||||||
'simple-import-sort/imports': [
|
'simple-import-sort/imports': [
|
||||||
'error',
|
'error',
|
||||||
@@ -340,91 +330,6 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'*.ts',
|
|
||||||
'*.tsx'
|
|
||||||
],
|
|
||||||
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
project: './tsconfig.json'
|
|
||||||
},
|
|
||||||
|
|
||||||
extends: [
|
|
||||||
'prettier'
|
|
||||||
],
|
|
||||||
|
|
||||||
rules: Object.assign(typescriptEslintRecommended.rules, {
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
args: 'after-used',
|
|
||||||
argsIgnorePattern: '^_',
|
|
||||||
ignoreRestSiblings: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'no-shadow': 'off',
|
|
||||||
'prettier/prettier': 'error',
|
|
||||||
'simple-import-sort/imports': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
groups: [
|
|
||||||
// Packages
|
|
||||||
// Absolute Paths
|
|
||||||
// Relative Paths
|
|
||||||
// Css
|
|
||||||
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// React Hooks
|
|
||||||
'react-hooks/rules-of-hooks': 'error',
|
|
||||||
'react-hooks/exhaustive-deps': 'error',
|
|
||||||
|
|
||||||
// React
|
|
||||||
'react/function-component-definition': 'error',
|
|
||||||
'react/hook-use-state': 'error',
|
|
||||||
'react/jsx-boolean-value': ['error', 'always'],
|
|
||||||
'react/jsx-curly-brace-presence': [
|
|
||||||
'error',
|
|
||||||
{ props: 'never', children: 'never' }
|
|
||||||
],
|
|
||||||
'react/jsx-fragments': 'error',
|
|
||||||
'react/jsx-handler-names': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
eventHandlerPrefix: 'on',
|
|
||||||
eventHandlerPropPrefix: 'on'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'react/jsx-no-bind': ['error', { ignoreRefs: true }],
|
|
||||||
'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }],
|
|
||||||
'react/jsx-pascal-case': ['error', { allowAllCaps: true }],
|
|
||||||
'react/jsx-sort-props': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
callbacksLast: true,
|
|
||||||
noSortAlphabetically: true,
|
|
||||||
reservedFirst: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'react/prop-types': 'off',
|
|
||||||
'react/self-closing-comp': 'error'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'*.css.d.ts'
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'filenames/match-exported': 'off',
|
|
||||||
'init-declarations': 'off',
|
|
||||||
'prettier/prettier': 'off'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
12
frontend/.jsbeautifyrc
Normal file
12
frontend/.jsbeautifyrc
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Ignore everything recursively
|
|
||||||
*
|
|
||||||
|
|
||||||
# But not the .ts files
|
|
||||||
!*.ts*
|
|
||||||
|
|
||||||
*css.d.ts
|
|
||||||
|
|
||||||
# Check subdirectories too
|
|
||||||
!*/
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"arrowParens": "always",
|
|
||||||
"endOfLine": "auto",
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "es5"
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"stylelint-order"
|
"stylelint-order"
|
||||||
],
|
],
|
||||||
"ignoreFiles": [
|
"ignoreFiles": [
|
||||||
"frontend/src/Styles/scaffolding.css",
|
"frontend/src/Styles/scaffolding.css",
|
||||||
"**/*.js"
|
"**/*.js"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"at-rule-empty-line-before": [
|
"at-rule-empty-line-before": [
|
||||||
"always",
|
"always",
|
||||||
{
|
{
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"at-rule-name-case": "lower",
|
||||||
|
"at-rule-name-newline-after": "always-multi-line",
|
||||||
|
"at-rule-name-space-after": "always",
|
||||||
"at-rule-no-unknown": [
|
"at-rule-no-unknown": [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
@@ -25,36 +28,83 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"at-rule-no-vendor-prefix": true,
|
"at-rule-no-vendor-prefix": true,
|
||||||
|
"at-rule-semicolon-newline-after": "always",
|
||||||
|
"at-rule-semicolon-space-before": "never",
|
||||||
|
"block-closing-brace-empty-line-before": "never",
|
||||||
|
"block-closing-brace-newline-after": "always",
|
||||||
|
"block-closing-brace-newline-before": "always",
|
||||||
|
"block-closing-brace-space-after": "always-single-line",
|
||||||
|
"block-closing-brace-space-before": "always-single-line",
|
||||||
"block-no-empty": true,
|
"block-no-empty": true,
|
||||||
|
"block-opening-brace-newline-after": "always",
|
||||||
|
"block-opening-brace-newline-before": "never-single-line",
|
||||||
|
"block-opening-brace-space-after": "always-single-line",
|
||||||
|
"block-opening-brace-space-before": "always",
|
||||||
|
"color-hex-case": "lower",
|
||||||
"color-hex-length": "short",
|
"color-hex-length": "short",
|
||||||
"color-named": "never",
|
"color-named": "never",
|
||||||
"color-no-invalid-hex": true,
|
"color-no-invalid-hex": true,
|
||||||
"comment-whitespace-inside": "always",
|
"comment-whitespace-inside": "always",
|
||||||
|
"declaration-bang-space-after": "never",
|
||||||
|
"declaration-bang-space-before": "always",
|
||||||
"declaration-block-no-duplicate-properties": [
|
"declaration-block-no-duplicate-properties": [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
"ignoreProperties": [
|
"ignoreProperties": [
|
||||||
"composes"
|
"composes"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"declaration-block-no-redundant-longhand-properties": true,
|
"declaration-block-no-redundant-longhand-properties": true,
|
||||||
"declaration-block-no-shorthand-property-overrides": true,
|
"declaration-block-no-shorthand-property-overrides": true,
|
||||||
|
"declaration-block-semicolon-newline-after": "always",
|
||||||
|
"declaration-block-semicolon-newline-before": "never-multi-line",
|
||||||
|
"declaration-block-semicolon-space-before": "never",
|
||||||
"declaration-block-single-line-max-declarations": 1,
|
"declaration-block-single-line-max-declarations": 1,
|
||||||
|
"declaration-block-trailing-semicolon": "always",
|
||||||
|
"declaration-colon-space-after": "always",
|
||||||
|
"declaration-colon-space-before": "never",
|
||||||
"font-family-name-quotes": "always-unless-keyword",
|
"font-family-name-quotes": "always-unless-keyword",
|
||||||
"function-calc-no-unspaced-operator": true,
|
"function-calc-no-unspaced-operator": true,
|
||||||
|
"function-comma-newline-after": "never-multi-line",
|
||||||
|
"function-comma-newline-before": "never-multi-line",
|
||||||
|
"function-comma-space-after": "always",
|
||||||
|
"function-comma-space-before": "never",
|
||||||
"function-linear-gradient-no-nonstandard-direction": true,
|
"function-linear-gradient-no-nonstandard-direction": true,
|
||||||
"function-name-case": "lower",
|
"function-name-case": "lower",
|
||||||
|
"function-parentheses-newline-inside": "never-multi-line",
|
||||||
|
"function-parentheses-space-inside": "never",
|
||||||
"function-url-quotes": "always",
|
"function-url-quotes": "always",
|
||||||
"function-url-scheme-disallowed-list": [
|
"function-url-scheme-disallowed-list": [
|
||||||
"data"
|
"data"
|
||||||
],
|
],
|
||||||
|
"function-whitespace-after": "always",
|
||||||
|
"indentation": 2,
|
||||||
"keyframe-declaration-no-important": true,
|
"keyframe-declaration-no-important": true,
|
||||||
"length-zero-no-unit": true,
|
"length-zero-no-unit": true,
|
||||||
|
"max-empty-lines": 1,
|
||||||
|
"max-line-length": [
|
||||||
|
100,
|
||||||
|
{
|
||||||
|
"ignore": [
|
||||||
|
"non-comments"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"max-nesting-depth": 2,
|
"max-nesting-depth": 2,
|
||||||
|
"media-feature-colon-space-after": "always",
|
||||||
|
"media-feature-colon-space-before": "never",
|
||||||
|
"media-feature-name-case": "lower",
|
||||||
"media-feature-name-no-vendor-prefix": true,
|
"media-feature-name-no-vendor-prefix": true,
|
||||||
|
"media-feature-range-operator-space-after": "always",
|
||||||
|
"media-feature-range-operator-space-before": "always",
|
||||||
"no-empty-source": true,
|
"no-empty-source": true,
|
||||||
|
"no-eol-whitespace": true,
|
||||||
|
"no-extra-semicolons": true,
|
||||||
"no-invalid-double-slash-comments": true,
|
"no-invalid-double-slash-comments": true,
|
||||||
|
"no-missing-end-of-source-newline": true,
|
||||||
|
"number-leading-zero": "always",
|
||||||
|
"number-no-trailing-zeros": true,
|
||||||
"order/order": [
|
"order/order": [
|
||||||
"custom-properties",
|
"custom-properties",
|
||||||
"dollar-variables",
|
"dollar-variables",
|
||||||
@@ -82,7 +132,6 @@
|
|||||||
"right",
|
"right",
|
||||||
"bottom",
|
"bottom",
|
||||||
"left",
|
"left",
|
||||||
"inset",
|
|
||||||
"z-index",
|
"z-index",
|
||||||
"display",
|
"display",
|
||||||
"visibility",
|
"visibility",
|
||||||
@@ -294,33 +343,54 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"property-case": "lower",
|
||||||
"property-no-vendor-prefix": true,
|
"property-no-vendor-prefix": true,
|
||||||
"rule-empty-line-before": [
|
"rule-empty-line-before": [
|
||||||
"always",
|
"always",
|
||||||
{
|
{
|
||||||
"except": [
|
"except": [
|
||||||
"first-nested"
|
"first-nested"
|
||||||
],
|
],
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"after-comment"
|
"after-comment"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selector-attribute-brackets-space-inside": "never",
|
||||||
|
"selector-attribute-operator-space-after": "never",
|
||||||
|
"selector-attribute-operator-space-before": "never",
|
||||||
"selector-attribute-quotes": "never",
|
"selector-attribute-quotes": "never",
|
||||||
"selector-class-pattern": "^[A-Za-z0-9]+$",
|
"selector-class-pattern": "^[A-Za-z0-9]+$",
|
||||||
|
"selector-combinator-space-after": "always",
|
||||||
|
"selector-combinator-space-before": "always",
|
||||||
|
"selector-descendant-combinator-no-non-space": true,
|
||||||
|
"selector-list-comma-newline-after": "always",
|
||||||
|
"selector-list-comma-newline-before": "never-multi-line",
|
||||||
|
"selector-list-comma-space-before": "never",
|
||||||
"selector-max-attribute": 0,
|
"selector-max-attribute": 0,
|
||||||
"selector-max-class": 3,
|
"selector-max-class": 3,
|
||||||
"selector-max-compound-selectors": 3,
|
"selector-max-compound-selectors": 3,
|
||||||
|
"selector-max-empty-lines": 0,
|
||||||
"selector-max-id": 0,
|
"selector-max-id": 0,
|
||||||
"selector-max-universal": 0,
|
"selector-max-universal": 0,
|
||||||
|
"selector-pseudo-class-case": "lower",
|
||||||
|
"selector-pseudo-class-parentheses-space-inside": "never",
|
||||||
|
"selector-pseudo-element-case": "lower",
|
||||||
"selector-pseudo-element-colon-notation": "double",
|
"selector-pseudo-element-colon-notation": "double",
|
||||||
"selector-pseudo-element-no-unknown": true,
|
"selector-pseudo-element-no-unknown": true,
|
||||||
"selector-type-case": "lower",
|
"selector-type-case": "lower",
|
||||||
"selector-type-no-unknown": true,
|
"selector-type-no-unknown": true,
|
||||||
"shorthand-property-no-redundant-values": true,
|
"shorthand-property-no-redundant-values": true,
|
||||||
"string-no-newline": true,
|
"string-no-newline": true,
|
||||||
|
"string-quotes": "single",
|
||||||
"time-min-milliseconds": 100,
|
"time-min-milliseconds": 100,
|
||||||
|
"unit-case": "lower",
|
||||||
"unit-no-unknown": true,
|
"unit-no-unknown": true,
|
||||||
|
"value-list-comma-newline-after": "never-multi-line",
|
||||||
|
"value-list-comma-newline-before": "never-multi-line",
|
||||||
|
"value-list-comma-space-after": "always",
|
||||||
|
"value-list-comma-space-before": "never",
|
||||||
|
"value-list-max-empty-lines": 0,
|
||||||
"value-no-vendor-prefix": true
|
"value-no-vendor-prefix": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
frontend/.vscode/extensions.json
vendored
7
frontend/.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"stylelint.vscode-stylelint",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"esbenp.prettier-vscode"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
23
frontend/.vscode/settings.json
vendored
23
frontend/.vscode/settings.json
vendored
@@ -1,23 +0,0 @@
|
|||||||
// Place your settings in this file to overwrite default and user settings.
|
|
||||||
{
|
|
||||||
"files.insertFinalNewline": true,
|
|
||||||
|
|
||||||
"files.exclude": {
|
|
||||||
"**/node_modules": true,
|
|
||||||
"**/*.d.css": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"editor.formatOnSave": false,
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"typescript.preferences.quoteStyle": "single",
|
|
||||||
|
|
||||||
"eslint.validate": [
|
|
||||||
"javascript",
|
|
||||||
"javascriptreact",
|
|
||||||
"typescript",
|
|
||||||
"typescriptreact"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@@ -2,25 +2,22 @@ const loose = true;
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
'@babel/plugin-transform-logical-assignment-operators',
|
|
||||||
|
|
||||||
// Stage 1
|
// Stage 1
|
||||||
'@babel/plugin-proposal-export-default-from',
|
'@babel/plugin-proposal-export-default-from',
|
||||||
['@babel/plugin-transform-optional-chaining', { loose }],
|
['@babel/plugin-proposal-optional-chaining', { loose }],
|
||||||
['@babel/plugin-transform-nullish-coalescing-operator', { loose }],
|
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
|
||||||
|
|
||||||
// Stage 2
|
// Stage 2
|
||||||
'@babel/plugin-transform-export-namespace-from',
|
'@babel/plugin-proposal-export-namespace-from',
|
||||||
|
|
||||||
// Stage 3
|
// Stage 3
|
||||||
['@babel/plugin-transform-class-properties', { loose }],
|
['@babel/plugin-proposal-class-properties', { loose }],
|
||||||
'@babel/plugin-syntax-dynamic-import'
|
'@babel/plugin-syntax-dynamic-import'
|
||||||
],
|
],
|
||||||
env: {
|
env: {
|
||||||
development: {
|
development: {
|
||||||
presets: [
|
presets: [
|
||||||
['@babel/preset-react', { development: true }],
|
['@babel/preset-react', { development: true }]
|
||||||
'@babel/preset-typescript'
|
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
'babel-plugin-inline-classnames'
|
'babel-plugin-inline-classnames'
|
||||||
@@ -28,8 +25,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
production: {
|
production: {
|
||||||
presets: [
|
presets: [
|
||||||
'@babel/preset-react',
|
'@babel/preset-react'
|
||||||
'@babel/preset-typescript'
|
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
'babel-plugin-transform-react-remove-prop-types'
|
'babel-plugin-transform-react-remove-prop-types'
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const FileManagerPlugin = require('filemanager-webpack-plugin');
|
const FileManagerPlugin = require('filemanager-webpack-plugin');
|
||||||
@@ -6,7 +5,6 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|||||||
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = (env) => {
|
module.exports = (env) => {
|
||||||
const uiFolder = 'UI';
|
const uiFolder = 'UI';
|
||||||
@@ -35,22 +33,17 @@ module.exports = (env) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
entry: {
|
entry: {
|
||||||
index: 'index.ts'
|
index: 'index.js'
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [
|
|
||||||
'.ts',
|
|
||||||
'.tsx',
|
|
||||||
'.js'
|
|
||||||
],
|
|
||||||
modules: [
|
modules: [
|
||||||
srcFolder,
|
srcFolder,
|
||||||
path.join(srcFolder, 'Shims'),
|
path.join(srcFolder, 'Shims'),
|
||||||
'node_modules'
|
'node_modules'
|
||||||
],
|
],
|
||||||
alias: {
|
alias: {
|
||||||
jquery: 'jquery/dist/jquery.min'
|
jquery: 'jquery/src/jquery'
|
||||||
},
|
},
|
||||||
fallback: {
|
fallback: {
|
||||||
buffer: false,
|
buffer: false,
|
||||||
@@ -65,23 +58,23 @@ module.exports = (env) => {
|
|||||||
output: {
|
output: {
|
||||||
path: distFolder,
|
path: distFolder,
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
filename: isProduction ? '[name]-[contenthash].js' : '[name].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,
|
||||||
@@ -89,15 +82,13 @@ module.exports = (env) => {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: 'Content/styles.css',
|
filename: 'Content/styles.css'
|
||||||
chunkFilename: isProduction ? 'Content/[id]-[chunkhash].css' : 'Content/[id].css'
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: 'frontend/src/index.ejs',
|
template: 'frontend/src/index.ejs',
|
||||||
filename: 'index.html',
|
filename: 'index.html',
|
||||||
publicPath: '/',
|
publicPath: '/'
|
||||||
inject: false
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new FileManagerPlugin({
|
new FileManagerPlugin({
|
||||||
@@ -138,8 +129,6 @@ module.exports = (env) => {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new ForkTsCheckerWebpackPlugin(),
|
|
||||||
|
|
||||||
new LiveReloadPlugin()
|
new LiveReloadPlugin()
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -153,8 +142,8 @@ module.exports = (env) => {
|
|||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: [/\.jsx?$/, /\.tsx?$/],
|
test: /\.jsx?$/,
|
||||||
exclude: /[\\/]node_modules[\\/](?!(@sentry|chart\.js|filesize)[\\/])/,
|
exclude: /[\\/]node_modules[\\/](?!(@sentry\/browser|@sentry\/integrations|chart.js|filesize|normalize.css)[\\/])/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
@@ -184,13 +173,12 @@ 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: {
|
||||||
importLoaders: 1,
|
importLoaders: 1,
|
||||||
modules: {
|
modules: {
|
||||||
localIdentName: isProduction ? '[name]/[local]/[hash:base64:5]' : '[name]/[local]'
|
localIdentName: '[name]/[local]/[hash:base64:5]'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -253,19 +241,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) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const mixinsFiles = [
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
'autoprefixer',
|
|
||||||
['postcss-mixins', {
|
['postcss-mixins', {
|
||||||
mixinsFiles
|
mixinsFiles
|
||||||
}],
|
}],
|
||||||
|
|||||||
4
frontend/src/.vscode/settings.json
vendored
Normal file
4
frontend/src/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
|
{
|
||||||
|
"files.insertFinalNewline": true
|
||||||
|
}
|
||||||
@@ -1,30 +1,31 @@
|
|||||||
import { ConnectedRouter, ConnectedRouterProps } from 'connected-react-router';
|
import { ConnectedRouter } from 'connected-react-router';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
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 { Store } from 'redux';
|
|
||||||
import PageConnector from 'Components/Page/PageConnector';
|
import PageConnector from 'Components/Page/PageConnector';
|
||||||
import ApplyTheme from './ApplyTheme';
|
import ApplyTheme from './ApplyTheme';
|
||||||
import AppRoutes from './AppRoutes';
|
import AppRoutes from './AppRoutes';
|
||||||
|
|
||||||
interface AppProps {
|
function App({ store, history }) {
|
||||||
store: Store;
|
|
||||||
history: ConnectedRouterProps['history'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function App({ store, history }: AppProps) {
|
|
||||||
return (
|
return (
|
||||||
<DocumentTitle title={window.Prowlarr.instanceName}>
|
<DocumentTitle title={window.Prowlarr.instanceName}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ConnectedRouter history={history}>
|
<ConnectedRouter history={history}>
|
||||||
<ApplyTheme />
|
<ApplyTheme>
|
||||||
<PageConnector>
|
<PageConnector>
|
||||||
<AppRoutes />
|
<AppRoutes app={App} />
|
||||||
</PageConnector>
|
</PageConnector>
|
||||||
|
</ApplyTheme>
|
||||||
</ConnectedRouter>
|
</ConnectedRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
</DocumentTitle>
|
</DocumentTitle>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
App.propTypes = {
|
||||||
|
store: PropTypes.object.isRequired,
|
||||||
|
history: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
184
frontend/src/App/AppRoutes.js
Normal file
184
frontend/src/App/AppRoutes.js
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import { Redirect, Route } from 'react-router-dom';
|
||||||
|
import NotFound from 'Components/NotFound';
|
||||||
|
import Switch from 'Components/Router/Switch';
|
||||||
|
import HistoryConnector from 'History/HistoryConnector';
|
||||||
|
import IndexerIndexConnector from 'Indexer/Index/IndexerIndexConnector';
|
||||||
|
import StatsConnector from 'Indexer/Stats/StatsConnector';
|
||||||
|
import SearchIndexConnector from 'Search/SearchIndexConnector';
|
||||||
|
import ApplicationSettingsConnector from 'Settings/Applications/ApplicationSettingsConnector';
|
||||||
|
import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector';
|
||||||
|
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
|
||||||
|
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
|
||||||
|
import IndexerSettings from 'Settings/Indexers/IndexerSettings';
|
||||||
|
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
|
||||||
|
import Settings from 'Settings/Settings';
|
||||||
|
import TagSettings from 'Settings/Tags/TagSettings';
|
||||||
|
import UISettingsConnector from 'Settings/UI/UISettingsConnector';
|
||||||
|
import BackupsConnector from 'System/Backup/BackupsConnector';
|
||||||
|
import LogsTableConnector from 'System/Events/LogsTableConnector';
|
||||||
|
import Logs from 'System/Logs/Logs';
|
||||||
|
import Status from 'System/Status/Status';
|
||||||
|
import Tasks from 'System/Tasks/Tasks';
|
||||||
|
import UpdatesConnector from 'System/Updates/UpdatesConnector';
|
||||||
|
import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
|
||||||
|
|
||||||
|
function AppRoutes(props) {
|
||||||
|
const {
|
||||||
|
app
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Switch>
|
||||||
|
{/*
|
||||||
|
Indexers
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<Route
|
||||||
|
exact={true}
|
||||||
|
path="/"
|
||||||
|
component={IndexerIndexConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
window.Prowlarr.urlBase &&
|
||||||
|
<Route
|
||||||
|
exact={true}
|
||||||
|
path="/"
|
||||||
|
addUrlBase={false}
|
||||||
|
render={() => {
|
||||||
|
return (
|
||||||
|
<Redirect
|
||||||
|
to={getPathWithUrlBase('/')}
|
||||||
|
component={app}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/indexers/stats"
|
||||||
|
component={StatsConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
Search
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/search"
|
||||||
|
component={SearchIndexConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
Activity
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/history"
|
||||||
|
component={HistoryConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
Settings
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<Route
|
||||||
|
exact={true}
|
||||||
|
path="/settings"
|
||||||
|
component={Settings}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/settings/indexers"
|
||||||
|
component={IndexerSettings}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/settings/applications"
|
||||||
|
component={ApplicationSettingsConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/settings/downloadclients"
|
||||||
|
component={DownloadClientSettingsConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/settings/connect"
|
||||||
|
component={NotificationSettings}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/settings/tags"
|
||||||
|
component={TagSettings}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/settings/general"
|
||||||
|
component={GeneralSettingsConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/settings/ui"
|
||||||
|
component={UISettingsConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/settings/development"
|
||||||
|
component={DevelopmentSettingsConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
System
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/system/status"
|
||||||
|
component={Status}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/system/tasks"
|
||||||
|
component={Tasks}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/system/backup"
|
||||||
|
component={BackupsConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/system/updates"
|
||||||
|
component={UpdatesConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/system/events"
|
||||||
|
component={LogsTableConnector}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/system/logs/files"
|
||||||
|
component={Logs}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
Not Found
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="*"
|
||||||
|
component={NotFound}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppRoutes.propTypes = {
|
||||||
|
app: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppRoutes;
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Redirect, Route } from 'react-router-dom';
|
|
||||||
import NotFound from 'Components/NotFound';
|
|
||||||
import Switch from 'Components/Router/Switch';
|
|
||||||
import HistoryConnector from 'History/HistoryConnector';
|
|
||||||
import IndexerIndex from 'Indexer/Index/IndexerIndex';
|
|
||||||
import IndexerStats from 'Indexer/Stats/IndexerStats';
|
|
||||||
import SearchIndexConnector from 'Search/SearchIndexConnector';
|
|
||||||
import ApplicationSettings from 'Settings/Applications/ApplicationSettings';
|
|
||||||
import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector';
|
|
||||||
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
|
|
||||||
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
|
|
||||||
import IndexerSettings from 'Settings/Indexers/IndexerSettings';
|
|
||||||
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
|
|
||||||
import Settings from 'Settings/Settings';
|
|
||||||
import TagSettings from 'Settings/Tags/TagSettings';
|
|
||||||
import UISettingsConnector from 'Settings/UI/UISettingsConnector';
|
|
||||||
import BackupsConnector from 'System/Backup/BackupsConnector';
|
|
||||||
import LogsTableConnector from 'System/Events/LogsTableConnector';
|
|
||||||
import Logs from 'System/Logs/Logs';
|
|
||||||
import Status from 'System/Status/Status';
|
|
||||||
import Tasks from 'System/Tasks/Tasks';
|
|
||||||
import Updates from 'System/Updates/Updates';
|
|
||||||
import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
|
|
||||||
|
|
||||||
function RedirectWithUrlBase() {
|
|
||||||
return <Redirect to={getPathWithUrlBase('/')} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function AppRoutes() {
|
|
||||||
return (
|
|
||||||
<Switch>
|
|
||||||
{/*
|
|
||||||
Indexers
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<Route exact={true} path="/" component={IndexerIndex} />
|
|
||||||
|
|
||||||
{window.Prowlarr.urlBase && (
|
|
||||||
<Route
|
|
||||||
exact={true}
|
|
||||||
path="/"
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
addUrlBase={false}
|
|
||||||
render={RedirectWithUrlBase}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Route path="/indexers/stats" component={IndexerStats} />
|
|
||||||
|
|
||||||
{/*
|
|
||||||
Search
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<Route path="/search" component={SearchIndexConnector} />
|
|
||||||
|
|
||||||
{/*
|
|
||||||
Activity
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<Route path="/history" component={HistoryConnector} />
|
|
||||||
|
|
||||||
{/*
|
|
||||||
Settings
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<Route exact={true} path="/settings" component={Settings} />
|
|
||||||
|
|
||||||
<Route path="/settings/indexers" component={IndexerSettings} />
|
|
||||||
|
|
||||||
<Route path="/settings/applications" component={ApplicationSettings} />
|
|
||||||
|
|
||||||
<Route
|
|
||||||
path="/settings/downloadclients"
|
|
||||||
component={DownloadClientSettingsConnector}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route path="/settings/connect" component={NotificationSettings} />
|
|
||||||
|
|
||||||
<Route path="/settings/tags" component={TagSettings} />
|
|
||||||
|
|
||||||
<Route path="/settings/general" component={GeneralSettingsConnector} />
|
|
||||||
|
|
||||||
<Route path="/settings/ui" component={UISettingsConnector} />
|
|
||||||
|
|
||||||
<Route
|
|
||||||
path="/settings/development"
|
|
||||||
component={DevelopmentSettingsConnector}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/*
|
|
||||||
System
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<Route path="/system/status" component={Status} />
|
|
||||||
|
|
||||||
<Route path="/system/tasks" component={Tasks} />
|
|
||||||
|
|
||||||
<Route path="/system/backup" component={BackupsConnector} />
|
|
||||||
|
|
||||||
<Route path="/system/updates" component={Updates} />
|
|
||||||
|
|
||||||
<Route path="/system/events" component={LogsTableConnector} />
|
|
||||||
|
|
||||||
<Route path="/system/logs/files" component={Logs} />
|
|
||||||
|
|
||||||
{/*
|
|
||||||
Not Found
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<Route path="*" component={NotFound} />
|
|
||||||
</Switch>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AppRoutes;
|
|
||||||
30
frontend/src/App/AppUpdatedModal.js
Normal file
30
frontend/src/App/AppUpdatedModal.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import AppUpdatedModalContentConnector from './AppUpdatedModalContentConnector';
|
||||||
|
|
||||||
|
function AppUpdatedModal(props) {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
onModalClose
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
closeOnBackgroundClick={false}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
>
|
||||||
|
<AppUpdatedModalContentConnector
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppUpdatedModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppUpdatedModal;
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import React, { useCallback } from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import AppUpdatedModalContent from './AppUpdatedModalContent';
|
|
||||||
|
|
||||||
interface AppUpdatedModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
onModalClose: (...args: unknown[]) => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
function AppUpdatedModal(props: AppUpdatedModalProps) {
|
|
||||||
const { isOpen, onModalClose } = props;
|
|
||||||
|
|
||||||
const handleModalClose = useCallback(() => {
|
|
||||||
location.reload();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
closeOnBackgroundClick={false}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<AppUpdatedModalContent onModalClose={handleModalClose} />
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AppUpdatedModal;
|
|
||||||
12
frontend/src/App/AppUpdatedModalConnector.js
Normal file
12
frontend/src/App/AppUpdatedModalConnector.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import AppUpdatedModal from './AppUpdatedModal';
|
||||||
|
|
||||||
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
|
return {
|
||||||
|
onModalClose() {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(null, createMapDispatchToProps)(AppUpdatedModal);
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
.version {
|
.version {
|
||||||
margin: 0 3px;
|
margin: 0 3px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-family: var(--defaultFontFamily);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.maintenance {
|
.maintenance {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
140
frontend/src/App/AppUpdatedModalContent.js
Normal file
140
frontend/src/App/AppUpdatedModalContent.js
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import { kinds } from 'Helpers/Props';
|
||||||
|
import UpdateChanges from 'System/Updates/UpdateChanges';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
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) {
|
||||||
|
const {
|
||||||
|
version,
|
||||||
|
prevVersion,
|
||||||
|
isPopulated,
|
||||||
|
error,
|
||||||
|
items,
|
||||||
|
onSeeChangesPress,
|
||||||
|
onModalClose
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const update = mergeUpdates(items, version, prevVersion);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
Prowlarr Updated
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<div>
|
||||||
|
Version <span className={styles.version}>{version}</span> of Prowlarr has been installed, in order to get the latest changes you'll need to reload Prowlarr.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
isPopulated && !error && !!update &&
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
!update.changes &&
|
||||||
|
<div className={styles.maintenance}>
|
||||||
|
{translate('MaintenanceRelease')}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
!!update.changes &&
|
||||||
|
<div>
|
||||||
|
<div className={styles.changes}>
|
||||||
|
What's new?
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UpdateChanges
|
||||||
|
title={translate('New')}
|
||||||
|
changes={update.changes.new}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UpdateChanges
|
||||||
|
title={translate('Fixed')}
|
||||||
|
changes={update.changes.fixed}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
!isPopulated && !error &&
|
||||||
|
<LoadingIndicator />
|
||||||
|
}
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
onPress={onSeeChangesPress}
|
||||||
|
>
|
||||||
|
Recent Changes
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
kind={kinds.PRIMARY}
|
||||||
|
onPress={onModalClose}
|
||||||
|
>
|
||||||
|
Reload
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppUpdatedModalContent.propTypes = {
|
||||||
|
version: PropTypes.string.isRequired,
|
||||||
|
prevVersion: PropTypes.string,
|
||||||
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
|
error: PropTypes.object,
|
||||||
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
onSeeChangesPress: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppUpdatedModalContent;
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
|
||||||
import usePrevious from 'Helpers/Hooks/usePrevious';
|
|
||||||
import { kinds } from 'Helpers/Props';
|
|
||||||
import { fetchUpdates } from 'Store/Actions/systemActions';
|
|
||||||
import UpdateChanges from 'System/Updates/UpdateChanges';
|
|
||||||
import Update from 'typings/Update';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import AppState from './State/AppState';
|
|
||||||
import styles from './AppUpdatedModalContent.css';
|
|
||||||
|
|
||||||
function mergeUpdates(items: Update[], version: string, prevVersion?: string) {
|
|
||||||
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: Update['changes'] = { new: [], fixed: [] };
|
|
||||||
|
|
||||||
appliedUpdates.forEach((u: Update) => {
|
|
||||||
if (u.changes) {
|
|
||||||
appliedChanges.new.push(...u.changes.new);
|
|
||||||
appliedChanges.fixed.push(...u.changes.fixed);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const mergedUpdate: Update = Object.assign({}, appliedUpdates[0], {
|
|
||||||
changes: appliedChanges,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!appliedChanges.new.length && !appliedChanges.fixed.length) {
|
|
||||||
mergedUpdate.changes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mergedUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AppUpdatedModalContentProps {
|
|
||||||
onModalClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function AppUpdatedModalContent(props: AppUpdatedModalContentProps) {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const { version, prevVersion } = useSelector((state: AppState) => state.app);
|
|
||||||
const { isPopulated, error, items } = useSelector(
|
|
||||||
(state: AppState) => state.system.updates
|
|
||||||
);
|
|
||||||
const previousVersion = usePrevious(version);
|
|
||||||
|
|
||||||
const { onModalClose } = props;
|
|
||||||
|
|
||||||
const update = mergeUpdates(items, version, prevVersion);
|
|
||||||
|
|
||||||
const handleSeeChangesPress = useCallback(() => {
|
|
||||||
window.location.href = `${window.Prowlarr.urlBase}/system/updates`;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(fetchUpdates());
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (version !== previousVersion) {
|
|
||||||
dispatch(fetchUpdates());
|
|
||||||
}
|
|
||||||
}, [version, previousVersion, dispatch]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>{translate('AppUpdated')}</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<div>
|
|
||||||
<InlineMarkdown
|
|
||||||
data={translate('AppUpdatedVersion', { version })}
|
|
||||||
blockClassName={styles.version}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isPopulated && !error && !!update ? (
|
|
||||||
<div>
|
|
||||||
{update.changes ? (
|
|
||||||
<div className={styles.maintenance}>
|
|
||||||
{translate('MaintenanceRelease')}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{update.changes ? (
|
|
||||||
<div>
|
|
||||||
<div className={styles.changes}>{translate('WhatsNew')}</div>
|
|
||||||
|
|
||||||
<UpdateChanges
|
|
||||||
title={translate('New')}
|
|
||||||
changes={update.changes.new}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<UpdateChanges
|
|
||||||
title={translate('Fixed')}
|
|
||||||
changes={update.changes.fixed}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{!isPopulated && !error ? <LoadingIndicator /> : null}
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={handleSeeChangesPress}>
|
|
||||||
{translate('RecentChanges')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button kind={kinds.PRIMARY} onPress={onModalClose}>
|
|
||||||
{translate('Reload')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AppUpdatedModalContent;
|
|
||||||
78
frontend/src/App/AppUpdatedModalContentConnector.js
Normal file
78
frontend/src/App/AppUpdatedModalContentConnector.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { fetchUpdates } from 'Store/Actions/systemActions';
|
||||||
|
import AppUpdatedModalContent from './AppUpdatedModalContent';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.app.version,
|
||||||
|
(state) => state.app.prevVersion,
|
||||||
|
(state) => state.system.updates,
|
||||||
|
(version, prevVersion, updates) => {
|
||||||
|
const {
|
||||||
|
isPopulated,
|
||||||
|
error,
|
||||||
|
items
|
||||||
|
} = updates;
|
||||||
|
|
||||||
|
return {
|
||||||
|
version,
|
||||||
|
prevVersion,
|
||||||
|
isPopulated,
|
||||||
|
error,
|
||||||
|
items
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
|
return {
|
||||||
|
dispatchFetchUpdates() {
|
||||||
|
dispatch(fetchUpdates());
|
||||||
|
},
|
||||||
|
|
||||||
|
onSeeChangesPress() {
|
||||||
|
window.location = `${window.Prowlarr.urlBase}/system/updates`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppUpdatedModalContentConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.dispatchFetchUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (prevProps.version !== this.props.version) {
|
||||||
|
this.props.dispatchFetchUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
dispatchFetchUpdates,
|
||||||
|
...otherProps
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppUpdatedModalContent {...otherProps} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppUpdatedModalContentConnector.propTypes = {
|
||||||
|
version: PropTypes.string.isRequired,
|
||||||
|
dispatchFetchUpdates: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, createMapDispatchToProps)(AppUpdatedModalContentConnector);
|
||||||
50
frontend/src/App/ApplyTheme.js
Normal file
50
frontend/src/App/ApplyTheme.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
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.Prowlarr.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);
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { useCallback, useEffect } from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import themes from 'Styles/Themes';
|
|
||||||
import AppState from './State/AppState';
|
|
||||||
|
|
||||||
function createThemeSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.settings.ui.item.theme || window.Prowlarr.theme,
|
|
||||||
(theme) => {
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ApplyTheme() {
|
|
||||||
const theme = useSelector(createThemeSelector());
|
|
||||||
|
|
||||||
const updateCSSVariables = useCallback(() => {
|
|
||||||
Object.entries(themes[theme]).forEach(([key, value]) => {
|
|
||||||
document.documentElement.style.setProperty(`--${key}`, value);
|
|
||||||
});
|
|
||||||
}, [theme]);
|
|
||||||
|
|
||||||
// On Component Mount and Component Update
|
|
||||||
useEffect(() => {
|
|
||||||
updateCSSVariables();
|
|
||||||
}, [updateCSSVariables, theme]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ApplyTheme;
|
|
||||||
@@ -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;
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useCallback } from 'react';
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
import Button from 'Components/Link/Button';
|
import Button from 'Components/Link/Button';
|
||||||
import Modal from 'Components/Modal/Modal';
|
import Modal from 'Components/Modal/Modal';
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
@@ -9,31 +10,36 @@ import { kinds } from 'Helpers/Props';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './ConnectionLostModal.css';
|
import styles from './ConnectionLostModal.css';
|
||||||
|
|
||||||
interface ConnectionLostModalProps {
|
function ConnectionLostModal(props) {
|
||||||
isOpen: boolean;
|
const {
|
||||||
}
|
isOpen,
|
||||||
|
onModalClose
|
||||||
function ConnectionLostModal(props: ConnectionLostModalProps) {
|
} = props;
|
||||||
const { isOpen } = props;
|
|
||||||
|
|
||||||
const handleModalClose = useCallback(() => {
|
|
||||||
location.reload();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onModalClose={handleModalClose}>
|
<Modal
|
||||||
<ModalContent onModalClose={handleModalClose}>
|
isOpen={isOpen}
|
||||||
<ModalHeader>{translate('ConnectionLost')}</ModalHeader>
|
onModalClose={onModalClose}
|
||||||
|
>
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
{translate('ConnectionLost')}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>{translate('ConnectionLostToBackend')}</div>
|
<div>
|
||||||
|
{translate('ConnectionLostMessage')}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={styles.automatic}>
|
<div className={styles.automatic}>
|
||||||
{translate('ConnectionLostReconnect')}
|
{translate('ConnectionLostAutomaticMessage')}
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button kind={kinds.PRIMARY} onPress={handleModalClose}>
|
<Button
|
||||||
|
kind={kinds.PRIMARY}
|
||||||
|
onPress={onModalClose}
|
||||||
|
>
|
||||||
{translate('Reload')}
|
{translate('Reload')}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
@@ -42,4 +48,9 @@ function ConnectionLostModal(props: ConnectionLostModalProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConnectionLostModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
export default ConnectionLostModal;
|
export default ConnectionLostModal;
|
||||||
12
frontend/src/App/ConnectionLostModalConnector.js
Normal file
12
frontend/src/App/ConnectionLostModalConnector.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ConnectionLostModal from './ConnectionLostModal';
|
||||||
|
|
||||||
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
|
return {
|
||||||
|
onModalClose() {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(undefined, createMapDispatchToProps)(ConnectionLostModal);
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
interface ModelBase {
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModelBase;
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import { cloneDeep } from 'lodash';
|
|
||||||
import React, { useCallback, useEffect } from 'react';
|
|
||||||
import useSelectState, { SelectState } from 'Helpers/Hooks/useSelectState';
|
|
||||||
import ModelBase from './ModelBase';
|
|
||||||
|
|
||||||
export type SelectContextAction =
|
|
||||||
| { type: 'reset' }
|
|
||||||
| { type: 'selectAll' }
|
|
||||||
| { type: 'unselectAll' }
|
|
||||||
| {
|
|
||||||
type: 'toggleSelected';
|
|
||||||
id: number;
|
|
||||||
isSelected: boolean;
|
|
||||||
shiftKey: boolean;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'removeItem';
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'updateItems';
|
|
||||||
items: ModelBase[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SelectDispatch = (action: SelectContextAction) => void;
|
|
||||||
|
|
||||||
interface SelectProviderOptions<T extends ModelBase> {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
children: any;
|
|
||||||
items: Array<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SelectContext = React.createContext<
|
|
||||||
[SelectState, SelectDispatch] | undefined
|
|
||||||
>(cloneDeep(undefined));
|
|
||||||
|
|
||||||
export function SelectProvider<T extends ModelBase>(
|
|
||||||
props: SelectProviderOptions<T>
|
|
||||||
) {
|
|
||||||
const { items } = props;
|
|
||||||
const [state, dispatch] = useSelectState();
|
|
||||||
|
|
||||||
const dispatchWrapper = useCallback(
|
|
||||||
(action: SelectContextAction) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'reset':
|
|
||||||
case 'removeItem':
|
|
||||||
dispatch(action);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
dispatch({
|
|
||||||
...action,
|
|
||||||
items,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[items, dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const value: [SelectState, SelectDispatch] = [state, dispatchWrapper];
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({ type: 'updateItems', items });
|
|
||||||
}, [items, dispatch]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SelectContext.Provider value={value}>
|
|
||||||
{props.children}
|
|
||||||
</SelectContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useSelect() {
|
|
||||||
const context = React.useContext(SelectContext);
|
|
||||||
|
|
||||||
if (context === undefined) {
|
|
||||||
throw new Error('useSelect must be used within a SelectProvider');
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import Column from 'Components/Table/Column';
|
|
||||||
import SortDirection from 'Helpers/Props/SortDirection';
|
|
||||||
import { FilterBuilderProp, PropertyFilter } from './AppState';
|
|
||||||
|
|
||||||
export interface Error {
|
|
||||||
responseJSON: {
|
|
||||||
message: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppSectionDeleteState {
|
|
||||||
isDeleting: boolean;
|
|
||||||
deleteError: Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppSectionSaveState {
|
|
||||||
isSaving: boolean;
|
|
||||||
saveError: Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PagedAppSectionState {
|
|
||||||
page: number;
|
|
||||||
pageSize: number;
|
|
||||||
totalPages: number;
|
|
||||||
totalRecords?: number;
|
|
||||||
}
|
|
||||||
export interface TableAppSectionState {
|
|
||||||
columns: Column[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppSectionFilterState<T> {
|
|
||||||
selectedFilterKey: string;
|
|
||||||
filters: PropertyFilter[];
|
|
||||||
filterBuilderProps: FilterBuilderProp<T>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppSectionSchemaState<T> {
|
|
||||||
isSchemaFetching: boolean;
|
|
||||||
isSchemaPopulated: boolean;
|
|
||||||
schemaError: Error;
|
|
||||||
schema: {
|
|
||||||
items: T[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppSectionItemState<T> {
|
|
||||||
isFetching: boolean;
|
|
||||||
isPopulated: boolean;
|
|
||||||
error: Error;
|
|
||||||
pendingChanges: Partial<T>;
|
|
||||||
item: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AppSectionState<T> {
|
|
||||||
isFetching: boolean;
|
|
||||||
isPopulated: boolean;
|
|
||||||
error: Error;
|
|
||||||
items: T[];
|
|
||||||
sortKey: string;
|
|
||||||
sortDirection: SortDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AppSectionState;
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import CommandAppState from './CommandAppState';
|
|
||||||
import HistoryAppState from './HistoryAppState';
|
|
||||||
import IndexerAppState, {
|
|
||||||
IndexerHistoryAppState,
|
|
||||||
IndexerIndexAppState,
|
|
||||||
IndexerStatusAppState,
|
|
||||||
} from './IndexerAppState';
|
|
||||||
import IndexerStatsAppState from './IndexerStatsAppState';
|
|
||||||
import SettingsAppState from './SettingsAppState';
|
|
||||||
import SystemAppState from './SystemAppState';
|
|
||||||
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[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppSectionState {
|
|
||||||
isConnected: boolean;
|
|
||||||
isReconnecting: boolean;
|
|
||||||
version: string;
|
|
||||||
prevVersion?: string;
|
|
||||||
dimensions: {
|
|
||||||
isSmallScreen: boolean;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AppState {
|
|
||||||
app: AppSectionState;
|
|
||||||
commands: CommandAppState;
|
|
||||||
history: HistoryAppState;
|
|
||||||
indexerHistory: IndexerHistoryAppState;
|
|
||||||
indexerIndex: IndexerIndexAppState;
|
|
||||||
indexerStats: IndexerStatsAppState;
|
|
||||||
indexerStatus: IndexerStatusAppState;
|
|
||||||
indexers: IndexerAppState;
|
|
||||||
settings: SettingsAppState;
|
|
||||||
system: SystemAppState;
|
|
||||||
tags: TagsAppState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AppState;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { CustomFilter } from './AppState';
|
|
||||||
|
|
||||||
interface ClientSideCollectionAppState {
|
|
||||||
totalItems: number;
|
|
||||||
customFilters: CustomFilter[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ClientSideCollectionAppState;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import AppSectionState from 'App/State/AppSectionState';
|
|
||||||
import Command from 'Commands/Command';
|
|
||||||
|
|
||||||
export type CommandAppState = AppSectionState<Command>;
|
|
||||||
|
|
||||||
export default CommandAppState;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import AppSectionState, {
|
|
||||||
AppSectionFilterState,
|
|
||||||
} from 'App/State/AppSectionState';
|
|
||||||
import Column from 'Components/Table/Column';
|
|
||||||
import History from 'typings/History';
|
|
||||||
|
|
||||||
interface HistoryAppState
|
|
||||||
extends AppSectionState<History>,
|
|
||||||
AppSectionFilterState<History> {
|
|
||||||
pageSize: number;
|
|
||||||
columns: Column[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default HistoryAppState;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import Column from 'Components/Table/Column';
|
|
||||||
import SortDirection from 'Helpers/Props/SortDirection';
|
|
||||||
import Indexer, { IndexerStatus } from 'Indexer/Indexer';
|
|
||||||
import History from 'typings/History';
|
|
||||||
import AppSectionState, {
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionSaveState,
|
|
||||||
} from './AppSectionState';
|
|
||||||
import { Filter, FilterBuilderProp } from './AppState';
|
|
||||||
|
|
||||||
export interface IndexerIndexAppState {
|
|
||||||
isTestingAll: boolean;
|
|
||||||
sortKey: string;
|
|
||||||
sortDirection: SortDirection;
|
|
||||||
secondarySortKey: string;
|
|
||||||
secondarySortDirection: SortDirection;
|
|
||||||
view: string;
|
|
||||||
|
|
||||||
tableOptions: {
|
|
||||||
showSearchAction: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
selectedFilterKey: string;
|
|
||||||
filterBuilderProps: FilterBuilderProp<Indexer>[];
|
|
||||||
filters: Filter[];
|
|
||||||
columns: Column[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IndexerAppState
|
|
||||||
extends AppSectionState<Indexer>,
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionSaveState {
|
|
||||||
itemMap: Record<number, number>;
|
|
||||||
|
|
||||||
isTestingAll: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IndexerStatusAppState = AppSectionState<IndexerStatus>;
|
|
||||||
|
|
||||||
export type IndexerHistoryAppState = AppSectionState<History>;
|
|
||||||
|
|
||||||
export default IndexerAppState;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { AppSectionItemState } from 'App/State/AppSectionState';
|
|
||||||
import { Filter, FilterBuilderProp } from 'App/State/AppState';
|
|
||||||
import Indexer from 'Indexer/Indexer';
|
|
||||||
import { IndexerStats } from 'typings/IndexerStats';
|
|
||||||
|
|
||||||
export interface IndexerStatsAppState
|
|
||||||
extends AppSectionItemState<IndexerStats> {
|
|
||||||
filterBuilderProps: FilterBuilderProp<Indexer>[];
|
|
||||||
selectedFilterKey: string;
|
|
||||||
filters: Filter[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IndexerStatsAppState;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import AppSectionState, {
|
|
||||||
AppSectionDeleteState,
|
|
||||||
} from 'App/State/AppSectionState';
|
|
||||||
import Release from 'typings/Release';
|
|
||||||
|
|
||||||
interface ReleaseAppState
|
|
||||||
extends AppSectionState<Release>,
|
|
||||||
AppSectionDeleteState {}
|
|
||||||
|
|
||||||
export default ReleaseAppState;
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import AppSectionState, {
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionItemState,
|
|
||||||
AppSectionSaveState,
|
|
||||||
} from 'App/State/AppSectionState';
|
|
||||||
import { IndexerCategory } from 'Indexer/Indexer';
|
|
||||||
import Application from 'typings/Application';
|
|
||||||
import DownloadClient from 'typings/DownloadClient';
|
|
||||||
import Notification from 'typings/Notification';
|
|
||||||
import General from 'typings/Settings/General';
|
|
||||||
import UiSettings from 'typings/Settings/UiSettings';
|
|
||||||
|
|
||||||
export interface AppProfileAppState
|
|
||||||
extends AppSectionState<Application>,
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionSaveState {}
|
|
||||||
|
|
||||||
export interface ApplicationAppState
|
|
||||||
extends AppSectionState<Application>,
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionSaveState {
|
|
||||||
isTestingAll: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DownloadClientAppState
|
|
||||||
extends AppSectionState<DownloadClient>,
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionSaveState {
|
|
||||||
isTestingAll: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GeneralAppState
|
|
||||||
extends AppSectionItemState<General>,
|
|
||||||
AppSectionSaveState {}
|
|
||||||
|
|
||||||
export interface IndexerCategoryAppState
|
|
||||||
extends AppSectionState<IndexerCategory>,
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionSaveState {}
|
|
||||||
|
|
||||||
export interface NotificationAppState
|
|
||||||
extends AppSectionState<Notification>,
|
|
||||||
AppSectionDeleteState {}
|
|
||||||
|
|
||||||
export type UiSettingsAppState = AppSectionItemState<UiSettings>;
|
|
||||||
|
|
||||||
interface SettingsAppState {
|
|
||||||
appProfiles: AppProfileAppState;
|
|
||||||
applications: ApplicationAppState;
|
|
||||||
downloadClients: DownloadClientAppState;
|
|
||||||
general: GeneralAppState;
|
|
||||||
indexerCategories: IndexerCategoryAppState;
|
|
||||||
notifications: NotificationAppState;
|
|
||||||
ui: UiSettingsAppState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SettingsAppState;
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import Health from 'typings/Health';
|
|
||||||
import SystemStatus from 'typings/SystemStatus';
|
|
||||||
import Task from 'typings/Task';
|
|
||||||
import Update from 'typings/Update';
|
|
||||||
import AppSectionState, { AppSectionItemState } from './AppSectionState';
|
|
||||||
|
|
||||||
export type HealthAppState = AppSectionState<Health>;
|
|
||||||
export type SystemStatusAppState = AppSectionItemState<SystemStatus>;
|
|
||||||
export type TaskAppState = AppSectionState<Task>;
|
|
||||||
export type UpdateAppState = AppSectionState<Update>;
|
|
||||||
|
|
||||||
interface SystemAppState {
|
|
||||||
health: HealthAppState;
|
|
||||||
status: SystemStatusAppState;
|
|
||||||
tasks: TaskAppState;
|
|
||||||
updates: UpdateAppState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SystemAppState;
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import ModelBase from 'App/ModelBase';
|
|
||||||
import AppSectionState, {
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionSaveState,
|
|
||||||
} from 'App/State/AppSectionState';
|
|
||||||
|
|
||||||
export interface Tag extends ModelBase {
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TagDetail extends ModelBase {
|
|
||||||
label: string;
|
|
||||||
applicationIds: number[];
|
|
||||||
indexerIds: number[];
|
|
||||||
indexerProxyIds: number[];
|
|
||||||
notificationIds: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TagDetailAppState
|
|
||||||
extends AppSectionState<TagDetail>,
|
|
||||||
AppSectionDeleteState,
|
|
||||||
AppSectionSaveState {}
|
|
||||||
|
|
||||||
interface TagsAppState extends AppSectionState<Tag>, AppSectionDeleteState {
|
|
||||||
details: TagDetailAppState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TagsAppState;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import ModelBase from 'App/ModelBase';
|
|
||||||
|
|
||||||
export interface CommandBody {
|
|
||||||
sendUpdatesToClient: boolean;
|
|
||||||
updateScheduledTask: boolean;
|
|
||||||
completionMessage: string;
|
|
||||||
requiresDiskAccess: boolean;
|
|
||||||
isExclusive: boolean;
|
|
||||||
isLongRunning: boolean;
|
|
||||||
name: string;
|
|
||||||
lastExecutionTime: string;
|
|
||||||
lastStartTime: string;
|
|
||||||
trigger: string;
|
|
||||||
suppressMessages: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Command extends ModelBase {
|
|
||||||
name: string;
|
|
||||||
commandName: string;
|
|
||||||
message: string;
|
|
||||||
body: CommandBody;
|
|
||||||
priority: string;
|
|
||||||
status: string;
|
|
||||||
result: string;
|
|
||||||
queued: string;
|
|
||||||
started: string;
|
|
||||||
ended: string;
|
|
||||||
duration: string;
|
|
||||||
trigger: string;
|
|
||||||
stateChangeTime: string;
|
|
||||||
sendUpdatesToClient: boolean;
|
|
||||||
updateScheduledTask: boolean;
|
|
||||||
lastExecutionTime: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Command;
|
|
||||||
11
frontend/src/Components/Alert.css.d.ts
vendored
11
frontend/src/Components/Alert.css.d.ts
vendored
@@ -1,11 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'alert': string;
|
|
||||||
'danger': string;
|
|
||||||
'info': string;
|
|
||||||
'success': string;
|
|
||||||
'warning': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -4,9 +4,7 @@ import React from 'react';
|
|||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import styles from './Alert.css';
|
import styles from './Alert.css';
|
||||||
|
|
||||||
function Alert(props) {
|
function Alert({ className, kind, children, ...otherProps }) {
|
||||||
const { className, kind, children, ...otherProps } = props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@@ -21,8 +19,8 @@ function Alert(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Alert.propTypes = {
|
Alert.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string.isRequired,
|
||||||
kind: PropTypes.oneOf(kinds.all),
|
kind: PropTypes.oneOf(kinds.all).isRequired,
|
||||||
children: PropTypes.node.isRequired
|
children: PropTypes.node.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
9
frontend/src/Components/Card.css.d.ts
vendored
9
frontend/src/Components/Card.css.d.ts
vendored
@@ -1,9 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'card': string;
|
|
||||||
'overlay': string;
|
|
||||||
'underlay': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -2,7 +2,6 @@ import Chart from 'chart.js/auto';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import { defaultFontFamily } from 'Styles/Variables/fonts';
|
|
||||||
|
|
||||||
function getColors(kind) {
|
function getColors(kind) {
|
||||||
|
|
||||||
@@ -40,15 +39,7 @@ class BarChart extends Component {
|
|||||||
plugins: {
|
plugins: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
align: 'start',
|
text: this.props.title
|
||||||
text: this.props.title,
|
|
||||||
padding: {
|
|
||||||
bottom: 30
|
|
||||||
},
|
|
||||||
font: {
|
|
||||||
size: 14,
|
|
||||||
family: defaultFontFamily
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
display: this.props.legend
|
display: this.props.legend
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import Chart from 'chart.js/auto';
|
import Chart from 'chart.js/auto';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { defaultFontFamily } from 'Styles/Variables/fonts';
|
|
||||||
|
|
||||||
function getColors(kind) {
|
function getColors(kind) {
|
||||||
|
|
||||||
@@ -23,15 +22,7 @@ class DoughnutChart extends Component {
|
|||||||
plugins: {
|
plugins: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
align: 'start',
|
text: this.props.title
|
||||||
text: this.props.title,
|
|
||||||
padding: {
|
|
||||||
bottom: 30
|
|
||||||
},
|
|
||||||
font: {
|
|
||||||
size: 14,
|
|
||||||
family: defaultFontFamily
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
position: 'bottom'
|
position: 'bottom'
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import Chart from 'chart.js/auto';
|
import Chart from 'chart.js/auto';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { defaultFontFamily } from 'Styles/Variables/fonts';
|
|
||||||
|
|
||||||
function getColors(index) {
|
function getColors(index) {
|
||||||
|
|
||||||
@@ -37,19 +36,7 @@ class StackedBarChart extends Component {
|
|||||||
plugins: {
|
plugins: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
align: 'start',
|
text: this.props.title
|
||||||
text: this.props.title,
|
|
||||||
padding: {
|
|
||||||
bottom: 30
|
|
||||||
},
|
|
||||||
font: {
|
|
||||||
size: 14,
|
|
||||||
family: defaultFontFamily
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
mode: 'index',
|
|
||||||
position: 'average'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'descriptionList': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -10,7 +10,6 @@ class DescriptionListItem extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
className,
|
|
||||||
titleClassName,
|
titleClassName,
|
||||||
descriptionClassName,
|
descriptionClassName,
|
||||||
title,
|
title,
|
||||||
@@ -18,7 +17,7 @@ class DescriptionListItem extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<span>
|
||||||
<DescriptionListItemTitle
|
<DescriptionListItemTitle
|
||||||
className={titleClassName}
|
className={titleClassName}
|
||||||
>
|
>
|
||||||
@@ -30,13 +29,12 @@ class DescriptionListItem extends Component {
|
|||||||
>
|
>
|
||||||
{data}
|
{data}
|
||||||
</DescriptionListItemDescription>
|
</DescriptionListItemDescription>
|
||||||
</div>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptionListItem.propTypes = {
|
DescriptionListItem.propTypes = {
|
||||||
className: PropTypes.string,
|
|
||||||
titleClassName: PropTypes.string,
|
titleClassName: PropTypes.string,
|
||||||
descriptionClassName: PropTypes.string,
|
descriptionClassName: PropTypes.string,
|
||||||
title: PropTypes.string,
|
title: 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;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'title': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'dragLayer': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -25,10 +25,6 @@
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointMedium) {
|
@media only screen and (max-width: $breakpointMedium) {
|
||||||
.image {
|
.image {
|
||||||
height: 250px;
|
height: 250px;
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'container': string;
|
|
||||||
'details': string;
|
|
||||||
'image': string;
|
|
||||||
'imageContainer': string;
|
|
||||||
'message': string;
|
|
||||||
'version': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
60
frontend/src/Components/Error/ErrorBoundaryError.js
Normal file
60
frontend/src/Components/Error/ErrorBoundaryError.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import styles from './ErrorBoundaryError.css';
|
||||||
|
|
||||||
|
function ErrorBoundaryError(props) {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
messageClassName,
|
||||||
|
detailsClassName,
|
||||||
|
message,
|
||||||
|
error,
|
||||||
|
info
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<div className={messageClassName}>
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.imageContainer}>
|
||||||
|
<img
|
||||||
|
className={styles.image}
|
||||||
|
src={`${window.Prowlarr.urlBase}/Content/Images/error.png`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<details className={detailsClassName}>
|
||||||
|
{
|
||||||
|
error &&
|
||||||
|
<div>
|
||||||
|
{error.toString()}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className={styles.info}>
|
||||||
|
{info.componentStack}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorBoundaryError.propTypes = {
|
||||||
|
className: PropTypes.string.isRequired,
|
||||||
|
messageClassName: PropTypes.string.isRequired,
|
||||||
|
detailsClassName: PropTypes.string.isRequired,
|
||||||
|
message: PropTypes.string.isRequired,
|
||||||
|
error: PropTypes.object.isRequired,
|
||||||
|
info: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorBoundaryError.defaultProps = {
|
||||||
|
className: styles.container,
|
||||||
|
messageClassName: styles.message,
|
||||||
|
detailsClassName: styles.details,
|
||||||
|
message: 'There was an error loading this content'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ErrorBoundaryError;
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import StackTrace from 'stacktrace-js';
|
|
||||||
import styles from './ErrorBoundaryError.css';
|
|
||||||
|
|
||||||
interface ErrorBoundaryErrorProps {
|
|
||||||
className: string;
|
|
||||||
messageClassName: string;
|
|
||||||
detailsClassName: string;
|
|
||||||
message: string;
|
|
||||||
error: Error;
|
|
||||||
info: {
|
|
||||||
componentStack: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
|
|
||||||
const {
|
|
||||||
className = styles.container,
|
|
||||||
messageClassName = styles.message,
|
|
||||||
detailsClassName = styles.details,
|
|
||||||
message = 'There was an error loading this content',
|
|
||||||
error,
|
|
||||||
info,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const [detailedError, setDetailedError] = useState<
|
|
||||||
StackTrace.StackFrame[] | null
|
|
||||||
>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (error) {
|
|
||||||
StackTrace.fromError(error).then((de) => {
|
|
||||||
setDetailedError(de);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setDetailedError(null);
|
|
||||||
}
|
|
||||||
}, [error, setDetailedError]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<div className={messageClassName}>{message}</div>
|
|
||||||
|
|
||||||
<div className={styles.imageContainer}>
|
|
||||||
<img
|
|
||||||
className={styles.image}
|
|
||||||
src={`${window.Prowlarr.urlBase}/Content/Images/error.png`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<details className={detailsClassName}>
|
|
||||||
{error ? <div>{error.message}</div> : null}
|
|
||||||
|
|
||||||
{detailedError ? (
|
|
||||||
detailedError.map((d, index) => {
|
|
||||||
return (
|
|
||||||
<div key={index}>
|
|
||||||
{` at ${d.functionName} (${d.fileName}:${d.lineNumber}:${d.columnNumber})`}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<div>{info.componentStack}</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={styles.version}>Version: {window.Prowlarr.version}</div>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ErrorBoundaryError;
|
|
||||||
8
frontend/src/Components/FieldSet.css.d.ts
vendored
8
frontend/src/Components/FieldSet.css.d.ts
vendored
@@ -1,8 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'fieldSet': string;
|
|
||||||
'legend': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'modal': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'faqLink': string;
|
|
||||||
'loading': string;
|
|
||||||
'mappedDrivesWarning': string;
|
|
||||||
'modalBody': string;
|
|
||||||
'pathInput': string;
|
|
||||||
'scroller': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
import Alert from 'Components/Alert';
|
import Alert from 'Components/Alert';
|
||||||
import PathInput from 'Components/Form/PathInput';
|
import PathInput from 'Components/Form/PathInput';
|
||||||
import Button from 'Components/Link/Button';
|
import Button from 'Components/Link/Button';
|
||||||
@@ -20,12 +21,12 @@ import styles from './FileBrowserModalContent.css';
|
|||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'type',
|
name: 'type',
|
||||||
label: () => translate('Type'),
|
label: translate('Type'),
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: () => translate('Name'),
|
label: translate('Name'),
|
||||||
isVisible: true
|
isVisible: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -38,7 +39,7 @@ class FileBrowserModalContent extends Component {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this._scrollerRef = React.createRef();
|
this._scrollerNode = null;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isFileBrowserModalOpen: false,
|
isFileBrowserModalOpen: false,
|
||||||
@@ -56,10 +57,21 @@ class FileBrowserModalContent extends Component {
|
|||||||
currentPath !== prevState.currentPath
|
currentPath !== prevState.currentPath
|
||||||
) {
|
) {
|
||||||
this.setState({ currentPath });
|
this.setState({ currentPath });
|
||||||
this._scrollerRef.current.scrollTop = 0;
|
this._scrollerNode.scrollTop = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
setScrollerRef = (ref) => {
|
||||||
|
if (ref) {
|
||||||
|
this._scrollerNode = ReactDOM.findDOMNode(ref);
|
||||||
|
} else {
|
||||||
|
this._scrollerNode = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
@@ -133,7 +145,7 @@ class FileBrowserModalContent extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Scroller
|
<Scroller
|
||||||
ref={this._scrollerRef}
|
ref={this.setScrollerRef}
|
||||||
className={styles.scroller}
|
className={styles.scroller}
|
||||||
scrollDirection={scrollDirections.BOTH}
|
scrollDirection={scrollDirections.BOTH}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'type': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import AppState from 'App/State/AppState';
|
|
||||||
import { IndexerCategory } from 'Indexer/Indexer';
|
|
||||||
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
|
||||||
import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
|
|
||||||
|
|
||||||
const indexerCategoriesSelector = createSelector(
|
|
||||||
(state: AppState) => state.settings.indexerCategories,
|
|
||||||
(categories) => categories.items
|
|
||||||
);
|
|
||||||
|
|
||||||
function CategoryFilterBuilderRowValue(props: FilterBuilderRowValueProps) {
|
|
||||||
const categories: IndexerCategory[] = useSelector(indexerCategoriesSelector);
|
|
||||||
|
|
||||||
const tagList = categories.reduce(
|
|
||||||
(acc: { id: number; name: string }[], element) => {
|
|
||||||
acc.push({
|
|
||||||
id: element.id,
|
|
||||||
name: `${element.name} (${element.id})`,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (element.subCategories && element.subCategories.length > 0) {
|
|
||||||
element.subCategories.forEach((subCat) => {
|
|
||||||
acc.push({
|
|
||||||
id: subCat.id,
|
|
||||||
name: `${subCat.name} (${subCat.id})`,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return <FilterBuilderRowValue {...props} tagList={tagList} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CategoryFilterBuilderRowValue;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'container': string;
|
|
||||||
'numberInput': string;
|
|
||||||
'selectInput': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'label': string;
|
|
||||||
'labelContainer': string;
|
|
||||||
'labelInputContainer': string;
|
|
||||||
'rows': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { maxBy } from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
@@ -51,7 +50,7 @@ class FilterBuilderModalContent extends Component {
|
|||||||
if (id) {
|
if (id) {
|
||||||
dispatchSetFilter({ selectedFilterKey: id });
|
dispatchSetFilter({ selectedFilterKey: id });
|
||||||
} else {
|
} else {
|
||||||
const last = maxBy(customFilters, 'id');
|
const last = customFilters[customFilters.length -1];
|
||||||
dispatchSetFilter({ selectedFilterKey: last.id });
|
dispatchSetFilter({ selectedFilterKey: last.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +108,7 @@ class FilterBuilderModalContent extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
labelErrors: [
|
labelErrors: [
|
||||||
{
|
{
|
||||||
message: translate('LabelIsRequired')
|
message: 'Label is required'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -147,13 +146,13 @@ class FilterBuilderModalContent extends Component {
|
|||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
{translate('CustomFilter')}
|
Custom Filter
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div className={styles.labelContainer}>
|
<div className={styles.labelContainer}>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
{translate('Label')}
|
Label
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.labelInputContainer}>
|
<div className={styles.labelInputContainer}>
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'actionsContainer': string;
|
|
||||||
'filterRow': string;
|
|
||||||
'inputContainer': string;
|
|
||||||
'valueInputContainer': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -3,13 +3,10 @@ import React, { Component } from 'react';
|
|||||||
import SelectInput from 'Components/Form/SelectInput';
|
import SelectInput from 'Components/Form/SelectInput';
|
||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import { filterBuilderTypes, filterBuilderValueTypes, icons } from 'Helpers/Props';
|
import { filterBuilderTypes, filterBuilderValueTypes, icons } from 'Helpers/Props';
|
||||||
import sortByProp from 'Utilities/Array/sortByProp';
|
|
||||||
import AppProfileFilterBuilderRowValueConnector from './AppProfileFilterBuilderRowValueConnector';
|
import AppProfileFilterBuilderRowValueConnector from './AppProfileFilterBuilderRowValueConnector';
|
||||||
import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
|
import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
|
||||||
import CategoryFilterBuilderRowValue from './CategoryFilterBuilderRowValue';
|
|
||||||
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
|
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
|
||||||
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
|
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
|
||||||
import HistoryEventTypeFilterBuilderRowValue from './HistoryEventTypeFilterBuilderRowValue';
|
|
||||||
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
|
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
|
||||||
import PrivacyFilterBuilderRowValue from './PrivacyFilterBuilderRowValue';
|
import PrivacyFilterBuilderRowValue from './PrivacyFilterBuilderRowValue';
|
||||||
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
|
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
|
||||||
@@ -58,15 +55,9 @@ function getRowValueConnector(selectedFilterBuilderProp) {
|
|||||||
case filterBuilderValueTypes.BOOL:
|
case filterBuilderValueTypes.BOOL:
|
||||||
return BoolFilterBuilderRowValue;
|
return BoolFilterBuilderRowValue;
|
||||||
|
|
||||||
case filterBuilderValueTypes.CATEGORY:
|
|
||||||
return CategoryFilterBuilderRowValue;
|
|
||||||
|
|
||||||
case filterBuilderValueTypes.DATE:
|
case filterBuilderValueTypes.DATE:
|
||||||
return DateFilterBuilderRowValue;
|
return DateFilterBuilderRowValue;
|
||||||
|
|
||||||
case filterBuilderValueTypes.HISTORY_EVENT_TYPE:
|
|
||||||
return HistoryEventTypeFilterBuilderRowValue;
|
|
||||||
|
|
||||||
case filterBuilderValueTypes.INDEXER:
|
case filterBuilderValueTypes.INDEXER:
|
||||||
return IndexerFilterBuilderRowValueConnector;
|
return IndexerFilterBuilderRowValueConnector;
|
||||||
|
|
||||||
@@ -207,13 +198,11 @@ class FilterBuilderRow extends Component {
|
|||||||
const selectedFilterBuilderProp = this.selectedFilterBuilderProp;
|
const selectedFilterBuilderProp = this.selectedFilterBuilderProp;
|
||||||
|
|
||||||
const keyOptions = filterBuilderProps.map((availablePropFilter) => {
|
const keyOptions = filterBuilderProps.map((availablePropFilter) => {
|
||||||
const { name, label } = availablePropFilter;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: name,
|
key: availablePropFilter.name,
|
||||||
value: typeof label === 'function' ? label() : label
|
value: availablePropFilter.label
|
||||||
};
|
};
|
||||||
}).sort(sortByProp('value'));
|
});
|
||||||
|
|
||||||
const ValueComponent = getRowValueConnector(selectedFilterBuilderProp);
|
const ValueComponent = getRowValueConnector(selectedFilterBuilderProp);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { filterBuilderTypes } from 'Helpers/Props';
|
import { filterBuilderTypes } from 'Helpers/Props';
|
||||||
import * as filterTypes from 'Helpers/Props/filterTypes';
|
import * as filterTypes from 'Helpers/Props/filterTypes';
|
||||||
import sortByProp from 'Utilities/Array/sortByProp';
|
import sortByName from 'Utilities/Array/sortByName';
|
||||||
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
||||||
|
|
||||||
function createTagListSelector() {
|
function createTagListSelector() {
|
||||||
@@ -38,7 +38,7 @@ function createTagListSelector() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []).sort(sortByProp('name'));
|
}, []).sort(sortByName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _.uniqBy(items, 'id');
|
return _.uniqBy(items, 'id');
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import { FilterBuilderProp } from 'App/State/AppState';
|
|
||||||
|
|
||||||
interface FilterBuilderRowOnChangeProps {
|
|
||||||
name: string;
|
|
||||||
value: unknown[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FilterBuilderRowValueProps {
|
|
||||||
filterType?: string;
|
|
||||||
filterValue: string | number | object | string[] | number[] | object[];
|
|
||||||
selectedFilterBuilderProp: FilterBuilderProp<unknown>;
|
|
||||||
sectionItem: unknown[];
|
|
||||||
onChange: (payload: FilterBuilderRowOnChangeProps) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FilterBuilderRowValueProps;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'isLastTag': string;
|
|
||||||
'label': string;
|
|
||||||
'or': string;
|
|
||||||
'tag': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
|
||||||
import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
|
|
||||||
|
|
||||||
const EVENT_TYPE_OPTIONS = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
get name() {
|
|
||||||
return translate('Grabbed');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
get name() {
|
|
||||||
return translate('IndexerRss');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
get name() {
|
|
||||||
return translate('IndexerQuery');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
get name() {
|
|
||||||
return translate('IndexerAuth');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function HistoryEventTypeFilterBuilderRowValue(
|
|
||||||
props: FilterBuilderRowValueProps
|
|
||||||
) {
|
|
||||||
return <FilterBuilderRowValue {...props} tagList={EVENT_TYPE_OPTIONS} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default HistoryEventTypeFilterBuilderRowValue;
|
|
||||||
@@ -9,13 +9,13 @@ import FilterBuilderRowValue from './FilterBuilderRowValue';
|
|||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.indexers,
|
(state) => state.indexers,
|
||||||
(indexers) => {
|
(qualityProfiles) => {
|
||||||
const {
|
const {
|
||||||
isFetching,
|
isFetching,
|
||||||
isPopulated,
|
isPopulated,
|
||||||
error,
|
error,
|
||||||
items
|
items
|
||||||
} = indexers;
|
} = qualityProfiles;
|
||||||
|
|
||||||
const tagList = items.map((item) => {
|
const tagList = items.map((item) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -3,24 +3,9 @@ import translate from 'Utilities/String/translate';
|
|||||||
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
||||||
|
|
||||||
const privacyTypes = [
|
const privacyTypes = [
|
||||||
{
|
{ id: 'public', name: translate('Public') },
|
||||||
id: 'public',
|
{ id: 'private', name: translate('Private') },
|
||||||
get name() {
|
{ id: 'semiPrivate', name: translate('SemiPrivate') }
|
||||||
return translate('Public');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'private',
|
|
||||||
get name() {
|
|
||||||
return translate('Private');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'semiPrivate',
|
|
||||||
get name() {
|
|
||||||
return translate('SemiPrivate');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function PrivacyFilterBuilderRowValue(props) {
|
function PrivacyFilterBuilderRowValue(props) {
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'actions': string;
|
|
||||||
'customFilter': string;
|
|
||||||
'label': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -37,8 +37,8 @@ class CustomFilter extends Component {
|
|||||||
dispatchSetFilter
|
dispatchSetFilter
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// Assume that delete and then unmounting means the deletion was successful.
|
// Assume that delete and then unmounting means the delete was successful.
|
||||||
// Moving this check to an ancestor would be more accurate, but would have
|
// Moving this check to a ancestor would be more accurate, but would have
|
||||||
// more boilerplate.
|
// more boilerplate.
|
||||||
if (this.state.isDeleting && id === selectedFilterKey) {
|
if (this.state.isDeleting && id === selectedFilterKey) {
|
||||||
dispatchSetFilter({ selectedFilterKey: 'all' });
|
dispatchSetFilter({ selectedFilterKey: 'all' });
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user