Compare commits
2 Commits
v5.9.0.905
...
update-cha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b4a6b1309 | ||
|
|
fcebfe6759 |
@@ -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": "Radarr",
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/dotnet:1-6.0",
|
|
||||||
"features": {
|
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
|
||||||
"nodeGypDependencies": true,
|
|
||||||
"version": "16",
|
|
||||||
"nvmVersion": "latest"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"forwardPorts": [7878],
|
|
||||||
"customizations": {
|
|
||||||
"vscode": {
|
|
||||||
"extensions": ["esbenp.prettier-vscode"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,10 +19,10 @@ indent_size = 4
|
|||||||
dotnet_sort_system_directives_first = true
|
dotnet_sort_system_directives_first = true
|
||||||
|
|
||||||
# Avoid "this." and "Me." if not necessary
|
# Avoid "this." and "Me." if not necessary
|
||||||
dotnet_style_qualification_for_field = false:refactoring
|
dotnet_style_qualification_for_field = false:warning
|
||||||
dotnet_style_qualification_for_property = false:refactoring
|
dotnet_style_qualification_for_property = false:warning
|
||||||
dotnet_style_qualification_for_method = false:refactoring
|
dotnet_style_qualification_for_method = false:warning
|
||||||
dotnet_style_qualification_for_event = false:refactoring
|
dotnet_style_qualification_for_event = false:warning
|
||||||
|
|
||||||
# Indentation preferences
|
# Indentation preferences
|
||||||
csharp_indent_block_contents = true
|
csharp_indent_block_contents = true
|
||||||
@@ -32,29 +32,17 @@ csharp_indent_case_contents_when_block = true
|
|||||||
csharp_indent_switch_labels = true
|
csharp_indent_switch_labels = true
|
||||||
csharp_indent_labels = flush_left
|
csharp_indent_labels = flush_left
|
||||||
|
|
||||||
dotnet_style_qualification_for_field = false:suggestion
|
|
||||||
dotnet_style_qualification_for_property = false:suggestion
|
|
||||||
dotnet_style_qualification_for_method = false:suggestion
|
|
||||||
dotnet_style_qualification_for_event = false:suggestion
|
|
||||||
dotnet_naming_style.instance_field_style.capitalization = camel_case
|
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
|
||||||
|
dotnet_diagnostic.SA1005.severity = none
|
||||||
dotnet_diagnostic.SA1025.severity = none
|
dotnet_diagnostic.SA1025.severity = none
|
||||||
dotnet_diagnostic.SA1101.severity = none
|
dotnet_diagnostic.SA1101.severity = none
|
||||||
dotnet_diagnostic.SA1116.severity = none
|
dotnet_diagnostic.SA1116.severity = none
|
||||||
@@ -77,7 +65,6 @@ dotnet_diagnostic.SA1406.severity = suggestion
|
|||||||
dotnet_diagnostic.SA1410.severity = suggestion
|
dotnet_diagnostic.SA1410.severity = suggestion
|
||||||
dotnet_diagnostic.SA1411.severity = suggestion
|
dotnet_diagnostic.SA1411.severity = suggestion
|
||||||
dotnet_diagnostic.SA1413.severity = none
|
dotnet_diagnostic.SA1413.severity = none
|
||||||
dotnet_diagnostic.SA1512.severity = none
|
|
||||||
dotnet_diagnostic.SA1516.severity = none
|
dotnet_diagnostic.SA1516.severity = none
|
||||||
dotnet_diagnostic.SA1600.severity = none
|
dotnet_diagnostic.SA1600.severity = none
|
||||||
dotnet_diagnostic.SA1601.severity = none
|
dotnet_diagnostic.SA1601.severity = none
|
||||||
@@ -176,7 +163,6 @@ dotnet_diagnostic.CA1309.severity = suggestion
|
|||||||
dotnet_diagnostic.CA1310.severity = suggestion
|
dotnet_diagnostic.CA1310.severity = suggestion
|
||||||
dotnet_diagnostic.CA1401.severity = suggestion
|
dotnet_diagnostic.CA1401.severity = suggestion
|
||||||
dotnet_diagnostic.CA1416.severity = suggestion
|
dotnet_diagnostic.CA1416.severity = suggestion
|
||||||
dotnet_diagnostic.CA1419.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1507.severity = suggestion
|
dotnet_diagnostic.CA1507.severity = suggestion
|
||||||
dotnet_diagnostic.CA1508.severity = suggestion
|
dotnet_diagnostic.CA1508.severity = suggestion
|
||||||
dotnet_diagnostic.CA1707.severity = suggestion
|
dotnet_diagnostic.CA1707.severity = suggestion
|
||||||
@@ -192,6 +178,9 @@ dotnet_diagnostic.CA1720.severity = suggestion
|
|||||||
dotnet_diagnostic.CA1721.severity = suggestion
|
dotnet_diagnostic.CA1721.severity = suggestion
|
||||||
dotnet_diagnostic.CA1724.severity = suggestion
|
dotnet_diagnostic.CA1724.severity = suggestion
|
||||||
dotnet_diagnostic.CA1725.severity = suggestion
|
dotnet_diagnostic.CA1725.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1801.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1802.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1805.severity = suggestion
|
||||||
dotnet_diagnostic.CA1806.severity = suggestion
|
dotnet_diagnostic.CA1806.severity = suggestion
|
||||||
dotnet_diagnostic.CA1810.severity = suggestion
|
dotnet_diagnostic.CA1810.severity = suggestion
|
||||||
dotnet_diagnostic.CA1812.severity = suggestion
|
dotnet_diagnostic.CA1812.severity = suggestion
|
||||||
@@ -203,11 +192,13 @@ dotnet_diagnostic.CA1819.severity = suggestion
|
|||||||
dotnet_diagnostic.CA1822.severity = suggestion
|
dotnet_diagnostic.CA1822.severity = suggestion
|
||||||
dotnet_diagnostic.CA1823.severity = suggestion
|
dotnet_diagnostic.CA1823.severity = suggestion
|
||||||
dotnet_diagnostic.CA1824.severity = suggestion
|
dotnet_diagnostic.CA1824.severity = suggestion
|
||||||
dotnet_diagnostic.CA1848.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2000.severity = suggestion
|
dotnet_diagnostic.CA2000.severity = suggestion
|
||||||
dotnet_diagnostic.CA2002.severity = suggestion
|
dotnet_diagnostic.CA2002.severity = suggestion
|
||||||
dotnet_diagnostic.CA2007.severity = suggestion
|
dotnet_diagnostic.CA2007.severity = suggestion
|
||||||
dotnet_diagnostic.CA2008.severity = suggestion
|
dotnet_diagnostic.CA2008.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA2009.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA2010.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA2011.severity = suggestion
|
||||||
dotnet_diagnostic.CA2012.severity = suggestion
|
dotnet_diagnostic.CA2012.severity = suggestion
|
||||||
dotnet_diagnostic.CA2013.severity = suggestion
|
dotnet_diagnostic.CA2013.severity = suggestion
|
||||||
dotnet_diagnostic.CA2100.severity = suggestion
|
dotnet_diagnostic.CA2100.severity = suggestion
|
||||||
@@ -238,9 +229,6 @@ dotnet_diagnostic.CA2243.severity = suggestion
|
|||||||
dotnet_diagnostic.CA2244.severity = suggestion
|
dotnet_diagnostic.CA2244.severity = suggestion
|
||||||
dotnet_diagnostic.CA2245.severity = suggestion
|
dotnet_diagnostic.CA2245.severity = suggestion
|
||||||
dotnet_diagnostic.CA2246.severity = suggestion
|
dotnet_diagnostic.CA2246.severity = suggestion
|
||||||
dotnet_diagnostic.CA2249.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2251.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2254.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA3061.severity = suggestion
|
dotnet_diagnostic.CA3061.severity = suggestion
|
||||||
dotnet_diagnostic.CA3075.severity = suggestion
|
dotnet_diagnostic.CA3075.severity = suggestion
|
||||||
dotnet_diagnostic.CA3076.severity = suggestion
|
dotnet_diagnostic.CA3076.severity = suggestion
|
||||||
@@ -268,9 +256,9 @@ dotnet_diagnostic.CA5392.severity = suggestion
|
|||||||
dotnet_diagnostic.CA5394.severity = suggestion
|
dotnet_diagnostic.CA5394.severity = suggestion
|
||||||
dotnet_diagnostic.CA5397.severity = suggestion
|
dotnet_diagnostic.CA5397.severity = suggestion
|
||||||
|
|
||||||
dotnet_diagnostic.SYSLIB0006.severity = none
|
|
||||||
|
|
||||||
[*.{js,html,hbs,less,css,ts,tsx}]
|
|
||||||
|
[*.{js,html,js,hbs,less,css}]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|||||||
10
.esprintrc
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"frontend/src/**/*.js",
|
||||||
|
"src/NzbDrone.Core/Localization/Core/*.json"
|
||||||
|
],
|
||||||
|
"ignored": [
|
||||||
|
"**/node_modules/**/*"
|
||||||
|
],
|
||||||
|
"port": 5004
|
||||||
|
}
|
||||||
2
.gitattributes
vendored
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
# Explicitly set bash scripts to have unix endings
|
# Explicitly set bash scripts to have unix endings
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
distribution/osx/Radarr text eol=lf
|
macOS/Radarr text eol=lf
|
||||||
|
|
||||||
# Custom for Visual Studio
|
# Custom for Visual Studio
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
|
|||||||
2
.github/FUNDING.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: radarr
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
patreon: # Replace with a single Patreon username
|
patreon: # Replace with a single Patreon username
|
||||||
open_collective: radarr
|
open_collective: radarr
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
|||||||
27
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,13 +1,13 @@
|
|||||||
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
|
||||||
attributes:
|
attributes:
|
||||||
label: Is there an existing issue for this?
|
label: Is there an existing issue for this?
|
||||||
description: Please search to see if an open or closed issue already exists for the bug you encountered. If a bug exists and is closed note that it may only be fixed in an unstable branch.
|
description: Please search to see if an issue already exists for the bug you encountered.
|
||||||
options:
|
options:
|
||||||
- label: I have searched the existing open and closed issues
|
- label: I have searched the existing issues
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
@@ -42,14 +42,12 @@ body:
|
|||||||
- **Docker Install**: Yes
|
- **Docker Install**: Yes
|
||||||
- **Using Reverse Proxy**: No
|
- **Using Reverse Proxy**: No
|
||||||
- **Browser**: Firefox 90 (If UI related)
|
- **Browser**: Firefox 90 (If UI related)
|
||||||
- **Database**: Sqlite 3.36.0
|
|
||||||
value: |
|
value: |
|
||||||
- OS:
|
- OS:
|
||||||
- Radarr:
|
- Radarr:
|
||||||
- Docker Install:
|
- Docker Install:
|
||||||
- Using Reverse Proxy:
|
- Using Reverse Proxy:
|
||||||
- Browser:
|
- Browser:
|
||||||
- Database:
|
|
||||||
render: markdown
|
render: markdown
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@@ -65,18 +63,11 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Trace Logs? **Not Optional**
|
label: Trace Logs?
|
||||||
description: |
|
description: |
|
||||||
Trace Logs (https://wiki.servarr.com/radarr/troubleshooting#logging-and-log-files)
|
Trace Logs (https://wiki.servarr.com/radarr/troubleshooting#logging-and-log-files)
|
||||||
***Generally speaking, all bug reports MUST have trace logs provided.***
|
***Generally speaking, all bug reports must have trace logs provided.***
|
||||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||||
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Trace Logs have been provided as applicable. Reports will be closed if the required logs are not provided.
|
|
||||||
description: Trace logs are **generally required** and are not optional for all bug reports and contain `trace`. Info logs are invalid for bug reports and do not contain `debug` nor `trace`
|
|
||||||
options:
|
|
||||||
- label: I have read and followed the steps in the wiki link above and provided the required trace logs - the logs contain `trace` - that are relevant and show this issue.
|
|
||||||
required: true
|
|
||||||
|
|||||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -3,3 +3,6 @@ contact_links:
|
|||||||
- name: Support via Discord
|
- name: Support via Discord
|
||||||
url: https://radarr.video/discord
|
url: https://radarr.video/discord
|
||||||
about: Chat with users and devs on support and setup related topics.
|
about: Chat with users and devs on support and setup related topics.
|
||||||
|
- name: Support via Reddit
|
||||||
|
url: https://reddit.com/r/radarr
|
||||||
|
about: Discuss and search thru support topics.
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -5,9 +5,9 @@ body:
|
|||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Is there an existing issue for this?
|
label: Is there an existing issue for this?
|
||||||
description: Please search to see if an open or closed issue already exists for the feature you are requesting. If a request exists and is closed note that it may only be fixed in an unstable branch.
|
description: Please search to see if an issue already exists for the feature you are requesting.
|
||||||
options:
|
options:
|
||||||
- label: I have searched the existing open and closed issues
|
- label: I have searched the existing issues
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
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
|
|
||||||
16
.github/label-actions.yml
vendored
@@ -1,16 +0,0 @@
|
|||||||
# Configuration for Label Actions - https://github.com/dessant/label-actions
|
|
||||||
|
|
||||||
'Type: Support':
|
|
||||||
comment: >
|
|
||||||
:wave: @{issue-author}, we use the issue tracker exclusively
|
|
||||||
for bug reports and feature requests. However, this issue appears
|
|
||||||
to be a support request. Please hop over onto our [Discord](https://radarr.video/discord).
|
|
||||||
close: true
|
|
||||||
close-reason: 'not planned'
|
|
||||||
|
|
||||||
'Status: Logs Needed':
|
|
||||||
comment: >
|
|
||||||
:wave: @{issue-author}, In order to help you further we'll need to see logs.
|
|
||||||
You'll need to enable trace logging and replicate the problem that you encountered.
|
|
||||||
Guidance on how to enable trace logging can be found in
|
|
||||||
our [troubleshooting guide](https://wiki.servarr.com/radarr/troubleshooting#logging-and-log-files).
|
|
||||||
28
.github/labeler.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
'Area: API':
|
|
||||||
- src/Radarr.Api.V3/**/*
|
|
||||||
|
|
||||||
'Area: Db-migration':
|
|
||||||
- src/NzbDrone.Core/Datastore/Migration/*
|
|
||||||
|
|
||||||
'Area: Download Clients':
|
|
||||||
- src/NzbDrone.Core/Download/Clients/**/*
|
|
||||||
|
|
||||||
'Area: Import Lists':
|
|
||||||
- src/NzbDrone.Core/ImportLists/**/*
|
|
||||||
|
|
||||||
'Area: Indexer':
|
|
||||||
- src/NzbDrone.Core/Indexers/**/*
|
|
||||||
|
|
||||||
'Area: Notifications':
|
|
||||||
- src/NzbDrone.Core/Notifications/**/*
|
|
||||||
|
|
||||||
'Area: Organizer':
|
|
||||||
- src/NzbDrone.Core/Organizer/**/*
|
|
||||||
|
|
||||||
'Area: Parser':
|
|
||||||
- src/NzbDrone.Core/Parser/**/*
|
|
||||||
|
|
||||||
'Area: UI':
|
|
||||||
- frontend/**/*
|
|
||||||
- package.json
|
|
||||||
- yarn.lock
|
|
||||||
41
.github/workflows/azuresync.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: Sync issue to Azure DevOps work item
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types:
|
||||||
|
[opened, edited, deleted, closed, reopened, labeled, unlabeled, assigned]
|
||||||
|
|
||||||
|
concurrency: azuresync-${{ github.event.issue.number }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
alert:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: danhellem/github-actions-issue-to-work-item@master
|
||||||
|
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == true }}"
|
||||||
|
env:
|
||||||
|
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
|
||||||
|
github_token: "${{ github.token }}"
|
||||||
|
ado_organization: "Servarr"
|
||||||
|
ado_project: "Servarr"
|
||||||
|
ado_area_path: "Servarr\\Radarr"
|
||||||
|
ado_wit: "Bug"
|
||||||
|
ado_new_state: "New"
|
||||||
|
ado_active_state: "Active"
|
||||||
|
ado_close_state: "Closed"
|
||||||
|
ado_bypassrules: true
|
||||||
|
log_level: 100
|
||||||
|
- uses: danhellem/github-actions-issue-to-work-item@master
|
||||||
|
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == false }}"
|
||||||
|
env:
|
||||||
|
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
|
||||||
|
github_token: "${{ github.token }}"
|
||||||
|
ado_organization: "Servarr"
|
||||||
|
ado_project: "Servarr"
|
||||||
|
ado_area_path: "Servarr\\Radarr"
|
||||||
|
ado_wit: "User Story"
|
||||||
|
ado_new_state: "New"
|
||||||
|
ado_active_state: "Active"
|
||||||
|
ado_close_state: "Closed"
|
||||||
|
ado_bypassrules: true
|
||||||
|
log_level: 100
|
||||||
17
.github/workflows/label-actions.yml
vendored
@@ -1,17 +0,0 @@
|
|||||||
name: 'Label Actions'
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [labeled, unlabeled]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
action:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: dessant/label-actions@v3
|
|
||||||
with:
|
|
||||||
process-only: 'issues'
|
|
||||||
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@v4
|
|
||||||
17
.github/workflows/lock.yml
vendored
@@ -5,22 +5,17 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
jobs:
|
jobs:
|
||||||
lock:
|
lock:
|
||||||
permissions:
|
|
||||||
issues: write # to lock issues (dessant/lock-threads)
|
|
||||||
pull-requests: write # to lock PRs (dessant/lock-threads)
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v4
|
- uses: dessant/lock-threads@v2
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
issue-inactive-days: '90'
|
issue-lock-inactive-days: '90'
|
||||||
exclude-issue-created-before: ''
|
issue-exclude-created-before: ''
|
||||||
exclude-any-issue-labels: ''
|
issue-exclude-labels: ''
|
||||||
add-issue-labels: ''
|
issue-lock-labels: ''
|
||||||
issue-comment: ''
|
issue-lock-comment: ''
|
||||||
issue-lock-reason: 'resolved'
|
issue-lock-reason: 'resolved'
|
||||||
process-only: ''
|
process-only: ''
|
||||||
|
|||||||
21
.github/workflows/support.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: 'Support requests'
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [labeled, unlabeled, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
support:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/support-requests@v2
|
||||||
|
with:
|
||||||
|
github-token: ${{ github.token }}
|
||||||
|
support-label: 'Type: Support'
|
||||||
|
issue-comment: >
|
||||||
|
:wave: @{issue-author}, we use the issue tracker exclusively
|
||||||
|
for bug reports and feature requests. However, this issue appears
|
||||||
|
to be a support request. Please hop over onto our [Discord](https://radarr.video/discord)
|
||||||
|
or [Subreddit](https://reddit.com/r/radarr)
|
||||||
|
close-issue: true
|
||||||
|
lock-issue: false
|
||||||
24
.gitignore
vendored
@@ -126,7 +126,6 @@ coverage*.xml
|
|||||||
coverage*.json
|
coverage*.json
|
||||||
setup/Output/
|
setup/Output/
|
||||||
*.~is
|
*.~is
|
||||||
.mono
|
|
||||||
|
|
||||||
# VS outout folders
|
# VS outout folders
|
||||||
bin
|
bin
|
||||||
@@ -167,8 +166,27 @@ packages.config.md5sum
|
|||||||
|
|
||||||
# Common IntelliJ Platform excludes
|
# Common IntelliJ Platform excludes
|
||||||
|
|
||||||
# Ignore Rider projects completely for now
|
# User specific
|
||||||
.idea/
|
**/.idea/**/workspace.xml
|
||||||
|
**/.idea/**/tasks.xml
|
||||||
|
**/.idea/shelf/*
|
||||||
|
**/.idea/dictionaries
|
||||||
|
**/.idea/.idea.Radarr.Posix
|
||||||
|
**/.idea/.idea.Radarr.Windows
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
**/.idea/**/dataSources/
|
||||||
|
**/.idea/**/dataSources.ids
|
||||||
|
**/.idea/**/dataSources.xml
|
||||||
|
**/.idea/**/dataSources.local.xml
|
||||||
|
**/.idea/**/sqlDataSources.xml
|
||||||
|
**/.idea/**/dynamic.xml
|
||||||
|
|
||||||
|
# Rider
|
||||||
|
# Rider auto-generates .iml files, and contentModel.xml
|
||||||
|
**/.idea/**/*.iml
|
||||||
|
**/.idea/**/contentModel.xml
|
||||||
|
**/.idea/**/modules.xml
|
||||||
|
|
||||||
# ignore node_modules symlink
|
# ignore node_modules symlink
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
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
@@ -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 Radarr",
|
|
||||||
"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/Radarr",
|
|
||||||
"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
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "build dotnet",
|
|
||||||
"command": "dotnet",
|
|
||||||
"type": "process",
|
|
||||||
"args": [
|
|
||||||
"msbuild",
|
|
||||||
"-restore",
|
|
||||||
"${workspaceFolder}/src/Radarr.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/Radarr.sln",
|
|
||||||
"-property:GenerateFullPaths=true",
|
|
||||||
"-consoleloggerparameters:NoSummary;ForceNoAlign"
|
|
||||||
],
|
|
||||||
"problemMatcher": "$msCompile"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "watch",
|
|
||||||
"command": "dotnet",
|
|
||||||
"type": "process",
|
|
||||||
"args": [
|
|
||||||
"watch",
|
|
||||||
"run",
|
|
||||||
"--project",
|
|
||||||
"${workspaceFolder}/src/Radarr.sln"
|
|
||||||
],
|
|
||||||
"problemMatcher": "$msCompile"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation in our
|
|
||||||
community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
||||||
identity and expression, level of experience, education, socio-economic status,
|
|
||||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
||||||
identity and orientation.
|
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
||||||
diverse, inclusive, and healthy community.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment for our
|
|
||||||
community include:
|
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
|
||||||
* Giving and gracefully accepting constructive feedback
|
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
||||||
and learning from the experience
|
|
||||||
* Focusing on what is best not just for us as individuals, but for the overall
|
|
||||||
community
|
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
||||||
any kind
|
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or email address,
|
|
||||||
without their explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
|
||||||
|
|
||||||
Community leaders are responsible for clarifying and enforcing our standards of
|
|
||||||
acceptable behavior and will take appropriate and fair corrective action in
|
|
||||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
||||||
or harmful.
|
|
||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit, or reject
|
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
||||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
||||||
decisions when appropriate.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when
|
|
||||||
an individual is officially representing the community in public spaces.
|
|
||||||
Examples of representing our community include using an official e-mail address,
|
|
||||||
posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported to the community leaders responsible for enforcement at
|
|
||||||
<development@radarr.video>.
|
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
|
||||||
reporter of any incident.
|
|
||||||
|
|
||||||
## Enforcement Guidelines
|
|
||||||
|
|
||||||
Community leaders will follow these Community Impact Guidelines in determining
|
|
||||||
the consequences for any action they deem in violation of this Code of Conduct:
|
|
||||||
|
|
||||||
### 1. Correction
|
|
||||||
|
|
||||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
||||||
unprofessional or unwelcome in the community.
|
|
||||||
|
|
||||||
**Consequence**: A private, written warning from community leaders, providing
|
|
||||||
clarity around the nature of the violation and an explanation of why the
|
|
||||||
behavior was inappropriate. A public apology may be requested.
|
|
||||||
|
|
||||||
### 2. Warning
|
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series of
|
|
||||||
actions.
|
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued behavior. No
|
|
||||||
interaction with the people involved, including unsolicited interaction with
|
|
||||||
those enforcing the Code of Conduct, for a specified period of time. This
|
|
||||||
includes avoiding interactions in community spaces as well as external channels
|
|
||||||
like social media. Violating these terms may lead to a temporary or permanent
|
|
||||||
ban.
|
|
||||||
|
|
||||||
### 3. Temporary Ban
|
|
||||||
|
|
||||||
**Community Impact**: A serious violation of community standards, including
|
|
||||||
sustained inappropriate behavior.
|
|
||||||
|
|
||||||
**Consequence**: A temporary ban from any sort of interaction or public
|
|
||||||
communication with the community for a specified period of time. No public or
|
|
||||||
private interaction with the people involved, including unsolicited interaction
|
|
||||||
with those enforcing the Code of Conduct, is allowed during this period.
|
|
||||||
Violating these terms may lead to a permanent ban.
|
|
||||||
|
|
||||||
### 4. Permanent Ban
|
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
||||||
community.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
||||||
version 2.1, available at
|
|
||||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by
|
|
||||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at
|
|
||||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
||||||
[https://www.contributor-covenant.org/translations][translations].
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
||||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
||||||
[FAQ]: https://www.contributor-covenant.org/faq
|
|
||||||
[translations]: https://www.contributor-covenant.org/translations
|
|
||||||
BIN
Logo/1024.png
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 49 KiB |
BIN
Logo/128.png
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
Logo/16.png
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 577 B |
BIN
Logo/256.png
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 10 KiB |
BIN
Logo/32.png
|
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 1.1 KiB |
BIN
Logo/400.png
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 17 KiB |
BIN
Logo/48.png
|
Before Width: | Height: | Size: 987 B After Width: | Height: | Size: 1.7 KiB |
BIN
Logo/512.png
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 21 KiB |
BIN
Logo/64.png
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
Logo/800.png
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 49 KiB |
BIN
Logo/text256.png
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 32 KiB |
12
README.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
|
[](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
|
||||||
[](https://translate.servarr.com/engage/radarr/?utm_source=widget)
|
[](https://translate.servarr.com/engage/radarr/?utm_source=widget)
|
||||||
[](https://wiki.servarr.com/radarr/installation/docker)
|
[](https://wiki.servarr.com/radarr/installation#docker)
|
||||||

|

|
||||||
[](#backers)
|
[](#backers)
|
||||||
[](#sponsors)
|
[](#sponsors)
|
||||||
@@ -35,6 +35,7 @@ Note that only one type of a given movie is supported. If you want both an 4k ve
|
|||||||
|
|
||||||
[](https://wiki.servarr.com/radarr)
|
[](https://wiki.servarr.com/radarr)
|
||||||
[](https://radarr.video/discord)
|
[](https://radarr.video/discord)
|
||||||
|
[](https://www.reddit.com/r/Radarr)
|
||||||
|
|
||||||
Note: GitHub Issues are for Bugs and Feature Requests Only
|
Note: GitHub Issues are for Bugs and Feature Requests Only
|
||||||
|
|
||||||
@@ -75,15 +76,6 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrai
|
|||||||
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
|
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
|
||||||
* [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
|
* [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
|
||||||
|
|
||||||
## DigitalOcean
|
|
||||||
|
|
||||||
This project is also supported by DigitalOcean
|
|
||||||
<p>
|
|
||||||
<a href="https://www.digitalocean.com/">
|
|
||||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
Please report (suspected) security vulnerabilities on Discord (preferred) to
|
|
||||||
any of the Servarr Dev role holders (red names) or via email: development@servarr.com. You will receive a response from
|
|
||||||
us within 72 hours. If the issue is confirmed, we will release a patch as soon
|
|
||||||
as possible depending on complexity/severity.
|
|
||||||
@@ -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: '5.9.0'
|
majorVersion: '4.2.0'
|
||||||
minorVersion: $[counter('minorVersion', 2000)]
|
minorVersion: $[counter('minorVersion', 2000)]
|
||||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||||
sentryOrg: 'servarr'
|
sentryOrg: 'servarr'
|
||||||
sentryUrl: 'https://sentry.servarr.com'
|
sentryUrl: 'https://sentry.servarr.com'
|
||||||
dotnetVersion: '6.0.424'
|
dotnetVersion: '6.0.300'
|
||||||
nodeVersion: '20.X'
|
nodeVersion: '16.X'
|
||||||
innoVersion: '6.2.2'
|
innoVersion: '6.2.0'
|
||||||
windowsImage: 'windows-2022'
|
windowsImage: 'windows-2022'
|
||||||
linuxImage: 'ubuntu-20.04'
|
linuxImage: 'ubuntu-20.04'
|
||||||
macImage: 'macOS-12'
|
macImage: 'macOS-11'
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
- develop
|
- develop
|
||||||
- master
|
- master
|
||||||
paths:
|
|
||||||
exclude:
|
|
||||||
- .github
|
|
||||||
- src/Radarr.Api.*/openapi.json
|
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
branches:
|
branches:
|
||||||
@@ -38,7 +34,6 @@ pr:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
exclude:
|
exclude:
|
||||||
- .github
|
|
||||||
- src/NzbDrone.Core/Localization/Core
|
- src/NzbDrone.Core/Localization/Core
|
||||||
- src/Radarr.Api.*/openapi.json
|
- src/Radarr.Api.*/openapi.json
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -178,6 +173,7 @@ stages:
|
|||||||
key: 'yarn | "$(osName)" | yarn.lock'
|
key: 'yarn | "$(osName)" | yarn.lock'
|
||||||
restoreKeys: |
|
restoreKeys: |
|
||||||
yarn | "$(osName)"
|
yarn | "$(osName)"
|
||||||
|
yarn
|
||||||
path: $(yarnCacheFolder)
|
path: $(yarnCacheFolder)
|
||||||
displayName: Cache Yarn packages
|
displayName: Cache Yarn packages
|
||||||
- bash: ./build.sh --frontend
|
- bash: ./build.sh --frontend
|
||||||
@@ -216,8 +212,8 @@ stages:
|
|||||||
displayName: Fetch Frontend
|
displayName: Fetch Frontend
|
||||||
- bash: |
|
- bash: |
|
||||||
./build.sh --packages --installer
|
./build.sh --packages --installer
|
||||||
cp distribution/windows/setup/output/Radarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
|
cp setup/output/Radarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||||
cp distribution/windows/setup/output/Radarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x86-installer.exe
|
cp setup/output/Radarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x86-installer.exe
|
||||||
displayName: Create Installers
|
displayName: Create Installers
|
||||||
- publish: $(Build.ArtifactStagingDirectory)
|
- publish: $(Build.ArtifactStagingDirectory)
|
||||||
artifact: 'WindowsInstaller'
|
artifact: 'WindowsInstaller'
|
||||||
@@ -368,7 +364,7 @@ stages:
|
|||||||
- bash: |
|
- bash: |
|
||||||
echo "Uploading source maps to sentry"
|
echo "Uploading source maps to sentry"
|
||||||
curl -sL https://sentry.io/get-cli/ | bash
|
curl -sL https://sentry.io/get-cli/ | bash
|
||||||
RELEASENAME="Radarr@${RADARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
RELEASENAME="${RADARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
||||||
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
|
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
|
||||||
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
||||||
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
||||||
@@ -541,8 +537,8 @@ stages:
|
|||||||
testRunTitle: '$(testName) Unit Tests'
|
testRunTitle: '$(testName) Unit Tests'
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
|
|
||||||
- job: Unit_LinuxCore_Postgres14
|
- job: Unit_LinuxCore_Postgres
|
||||||
displayName: Unit Native LinuxCore with Postgres14 Database
|
displayName: Unit Native LinuxCore with Postgres Database
|
||||||
dependsOn: Prepare
|
dependsOn: Prepare
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
variables:
|
variables:
|
||||||
@@ -554,7 +550,7 @@ stages:
|
|||||||
Radarr__Postgres__Password: 'radarr'
|
Radarr__Postgres__Password: 'radarr'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.linuxImage }}
|
vmImage: 'ubuntu-18.04'
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
timeoutInMinutes: 10
|
||||||
|
|
||||||
@@ -581,7 +577,6 @@ stages:
|
|||||||
-e POSTGRES_PASSWORD=radarr \
|
-e POSTGRES_PASSWORD=radarr \
|
||||||
-e POSTGRES_USER=radarr \
|
-e POSTGRES_USER=radarr \
|
||||||
-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: |
|
||||||
@@ -594,63 +589,7 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: 'LinuxCore Postgres14 Unit Tests'
|
testRunTitle: 'LinuxCore Postgres Unit Tests'
|
||||||
failTaskOnFailedTests: true
|
|
||||||
|
|
||||||
- job: Unit_LinuxCore_Postgres15
|
|
||||||
displayName: Unit Native LinuxCore with Postgres15 Database
|
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
variables:
|
|
||||||
pattern: 'Radarr.*.linux-core-x64.tar.gz'
|
|
||||||
artifactName: linux-x64-tests
|
|
||||||
Radarr__Postgres__Host: 'localhost'
|
|
||||||
Radarr__Postgres__Port: '5432'
|
|
||||||
Radarr__Postgres__User: 'radarr'
|
|
||||||
Radarr__Postgres__Password: 'radarr'
|
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: ${{ variables.linuxImage }}
|
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .net core'
|
|
||||||
inputs:
|
|
||||||
version: $(dotnetVersion)
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download Test Artifact
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: $(artifactName)
|
|
||||||
targetPath: $(testsFolder)
|
|
||||||
- bash: |
|
|
||||||
chmod a+x _tests/ffprobe
|
|
||||||
displayName: Make ffprobe Executable
|
|
||||||
- bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
|
|
||||||
displayName: Make Test Dummy Executable
|
|
||||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
|
||||||
- bash: |
|
|
||||||
docker run -d --name=postgres15 \
|
|
||||||
-e POSTGRES_PASSWORD=radarr \
|
|
||||||
-e POSTGRES_USER=radarr \
|
|
||||||
-p 5432:5432/tcp \
|
|
||||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
|
||||||
postgres:15
|
|
||||||
displayName: Start postgres
|
|
||||||
- bash: |
|
|
||||||
chmod a+x ${TESTSFOLDER}/test.sh
|
|
||||||
ls -lR ${TESTSFOLDER}
|
|
||||||
${TESTSFOLDER}/test.sh Linux Unit Test
|
|
||||||
displayName: Run Tests
|
|
||||||
- task: PublishTestResults@2
|
|
||||||
displayName: Publish Test Results
|
|
||||||
inputs:
|
|
||||||
testResultsFormat: 'NUnit'
|
|
||||||
testResultsFiles: '**/TestResult.xml'
|
|
||||||
testRunTitle: 'LinuxCore Postgres15 Unit Tests'
|
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
|
|
||||||
- stage: Integration
|
- stage: Integration
|
||||||
@@ -736,8 +675,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:
|
||||||
@@ -748,7 +687,7 @@ stages:
|
|||||||
Radarr__Postgres__Password: 'radarr'
|
Radarr__Postgres__Password: 'radarr'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.linuxImage }}
|
vmImage: 'ubuntu-18.04'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
@@ -783,7 +722,6 @@ stages:
|
|||||||
-e POSTGRES_PASSWORD=radarr \
|
-e POSTGRES_PASSWORD=radarr \
|
||||||
-e POSTGRES_USER=radarr \
|
-e POSTGRES_USER=radarr \
|
||||||
-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: |
|
||||||
@@ -794,70 +732,7 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
|
testRunTitle: 'Integration LinuxCore Postgres Database Integration Tests'
|
||||||
failTaskOnFailedTests: true
|
|
||||||
displayName: Publish Test Results
|
|
||||||
|
|
||||||
|
|
||||||
- job: Integration_LinuxCore_Postgres15
|
|
||||||
displayName: Integration Native LinuxCore with Postgres Database
|
|
||||||
dependsOn: Prepare
|
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
|
||||||
variables:
|
|
||||||
pattern: 'Radarr.*.linux-core-x64.tar.gz'
|
|
||||||
Radarr__Postgres__Host: 'localhost'
|
|
||||||
Radarr__Postgres__Port: '5432'
|
|
||||||
Radarr__Postgres__User: 'radarr'
|
|
||||||
Radarr__Postgres__Password: 'radarr'
|
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: ${{ variables.linuxImage }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .net core'
|
|
||||||
inputs:
|
|
||||||
version: $(dotnetVersion)
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download Test Artifact
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: 'linux-x64-tests'
|
|
||||||
targetPath: $(testsFolder)
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download Build Artifact
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: Packages
|
|
||||||
itemPattern: '**/$(pattern)'
|
|
||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
|
||||||
- task: ExtractFiles@1
|
|
||||||
inputs:
|
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
|
||||||
displayName: Extract Package
|
|
||||||
- bash: |
|
|
||||||
mkdir -p ./bin/
|
|
||||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
|
||||||
displayName: Move Package Contents
|
|
||||||
- bash: |
|
|
||||||
docker run -d --name=postgres15 \
|
|
||||||
-e POSTGRES_PASSWORD=radarr \
|
|
||||||
-e POSTGRES_USER=radarr \
|
|
||||||
-p 5432:5432/tcp \
|
|
||||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
|
||||||
postgres:15
|
|
||||||
displayName: Start postgres
|
|
||||||
- bash: |
|
|
||||||
chmod a+x ${TESTSFOLDER}/test.sh
|
|
||||||
${TESTSFOLDER}/test.sh Linux Integration Test
|
|
||||||
displayName: Run Integration Tests
|
|
||||||
- task: PublishTestResults@2
|
|
||||||
inputs:
|
|
||||||
testResultsFormat: 'NUnit'
|
|
||||||
testResultsFiles: '**/TestResult.xml'
|
|
||||||
testRunTitle: 'Integration LinuxCore Postgres15 Database Integration Tests'
|
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
@@ -885,7 +760,7 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
buildType: 'current'
|
buildType: 'current'
|
||||||
artifactName: Packages
|
artifactName: Packages
|
||||||
itemPattern: '**/$(pattern)'
|
itemPattern: '/$(pattern)'
|
||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- bash: |
|
- bash: |
|
||||||
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
|
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
|
||||||
@@ -1089,10 +964,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
|
||||||
@@ -1101,6 +976,7 @@ stages:
|
|||||||
key: 'yarn | "$(osName)" | yarn.lock'
|
key: 'yarn | "$(osName)" | yarn.lock'
|
||||||
restoreKeys: |
|
restoreKeys: |
|
||||||
yarn | "$(osName)"
|
yarn | "$(osName)"
|
||||||
|
yarn
|
||||||
path: $(yarnCacheFolder)
|
path: $(yarnCacheFolder)
|
||||||
displayName: Cache Yarn packages
|
displayName: Cache Yarn packages
|
||||||
- bash: ./build.sh --lint
|
- bash: ./build.sh --lint
|
||||||
@@ -1116,7 +992,7 @@ stages:
|
|||||||
vmImage: ${{ variables.windowsImage }}
|
vmImage: ${{ variables.windowsImage }}
|
||||||
steps:
|
steps:
|
||||||
- checkout: self # Need history for Sonar analysis
|
- checkout: self # Need history for Sonar analysis
|
||||||
- task: SonarCloudPrepare@2
|
- task: SonarCloudPrepare@1
|
||||||
env:
|
env:
|
||||||
SONAR_SCANNER_OPTS: ''
|
SONAR_SCANNER_OPTS: ''
|
||||||
inputs:
|
inputs:
|
||||||
@@ -1128,7 +1004,7 @@ stages:
|
|||||||
cliProjectName: 'RadarrUI'
|
cliProjectName: 'RadarrUI'
|
||||||
cliProjectVersion: '$(radarrVersion)'
|
cliProjectVersion: '$(radarrVersion)'
|
||||||
cliSources: './frontend'
|
cliSources: './frontend'
|
||||||
- task: SonarCloudAnalyze@2
|
- task: SonarCloudAnalyze@1
|
||||||
|
|
||||||
- job: Api_Docs
|
- job: Api_Docs
|
||||||
displayName: API Docs
|
displayName: API Docs
|
||||||
@@ -1205,7 +1081,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'
|
||||||
@@ -1223,21 +1099,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
|
||||||
@@ -1263,5 +1143,4 @@ stages:
|
|||||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||||
DISCORDCHANNELID: $(discordChannelId)
|
DISCORDCHANNELID: $(discordChannelId)
|
||||||
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
||||||
DISCORDTHREADID: $(discordThreadId)
|
|
||||||
|
|
||||||
|
|||||||
21
build.sh
@@ -21,7 +21,7 @@ UpdateVersionNumber()
|
|||||||
echo "Updating Version Info"
|
echo "Updating Version Info"
|
||||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$RADARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$RADARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
||||||
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
||||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$RADARRVERSION<\/string>/g" distribution/osx/Radarr.app/Contents/Info.plist
|
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$RADARRVERSION<\/string>/g" macOS/Radarr.app/Contents/Info.plist
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ PackageMacOSApp()
|
|||||||
|
|
||||||
rm -rf $folder
|
rm -rf $folder
|
||||||
mkdir -p $folder
|
mkdir -p $folder
|
||||||
cp -r distribution/osx/Radarr.app $folder
|
cp -r macOS/Radarr.app $folder
|
||||||
mkdir -p $folder/Radarr.app/Contents/MacOS
|
mkdir -p $folder/Radarr.app/Contents/MacOS
|
||||||
|
|
||||||
echo "Copying Binaries"
|
echo "Copying Binaries"
|
||||||
@@ -246,7 +246,7 @@ BuildInstaller()
|
|||||||
local framework="$1"
|
local framework="$1"
|
||||||
local runtime="$2"
|
local runtime="$2"
|
||||||
|
|
||||||
./_inno/ISCC.exe distribution/windows/setup/radarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
./_inno/ISCC.exe setup/radarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallInno()
|
InstallInno()
|
||||||
@@ -254,7 +254,7 @@ InstallInno()
|
|||||||
ProgressStart "Installing portable Inno Setup"
|
ProgressStart "Installing portable Inno Setup"
|
||||||
|
|
||||||
rm -rf _inno
|
rm -rf _inno
|
||||||
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.2}.exe"
|
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.0}.exe"
|
||||||
mkdir _inno
|
mkdir _inno
|
||||||
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
|
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
|
||||||
rm innosetup.exe
|
rm innosetup.exe
|
||||||
@@ -392,21 +392,22 @@ then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$LINT" = "YES" || "$FRONTEND" = "YES" ]];
|
if [ "$FRONTEND" = "YES" ];
|
||||||
then
|
then
|
||||||
YarnInstall
|
YarnInstall
|
||||||
|
RunWebpack
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$LINT" = "YES" ];
|
if [ "$LINT" = "YES" ];
|
||||||
then
|
then
|
||||||
|
if [ -z "$FRONTEND" ];
|
||||||
|
then
|
||||||
|
YarnInstall
|
||||||
|
fi
|
||||||
|
|
||||||
LintUI
|
LintUI
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$FRONTEND" = "YES" ];
|
|
||||||
then
|
|
||||||
RunWebpack
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$PACKAGES" = "YES" ];
|
if [ "$PACKAGES" = "YES" ];
|
||||||
then
|
then
|
||||||
UpdateVersionNumber
|
UpdateVersionNumber
|
||||||
|
|||||||
5
debian/changelog
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
nzbdrone {version} {branch}; urgency=low
|
||||||
|
|
||||||
|
* Automatic Release.
|
||||||
|
|
||||||
|
-- NzbDrone <contact@nzbdrone.com> Mon, 26 Aug 2013 00:00:00 -0700
|
||||||
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
8
|
||||||
12
debian/control
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Section: web
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Sonarr <contact@nzbdrone.com>
|
||||||
|
Source: nzbdrone
|
||||||
|
Homepage: https://sonarr.tv
|
||||||
|
Vcs-Git: git@github.com:Sonarr/Sonarr.git
|
||||||
|
Vcs-Browser: https://github.com/Sonarr/Sonarr
|
||||||
|
|
||||||
|
Package: nzbdrone
|
||||||
|
Architecture: all
|
||||||
|
Depends: libmono-cil-dev (>= 3.2), sqlite3 (>= 3.7), mediainfo (>= 0.7.52)
|
||||||
|
Description: Sonarr is an internet PVR
|
||||||
24
debian/copyright
vendored
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: nzbdrone
|
||||||
|
Source: https://github.com/Sonarr/Sonarr
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2010-2016 Sonarr <hello@sonarr.tv>
|
||||||
|
|
||||||
|
License: GPL-3.0+
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
.
|
||||||
|
This package is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the GNU General
|
||||||
|
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||||
1
debian/install
vendored
Executable file
@@ -0,0 +1 @@
|
|||||||
|
nzbdrone_bin/* opt/NzbDrone
|
||||||
13
debian/rules
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
# -*- makefile -*-
|
||||||
|
# Sample debian/rules that uses debhelper.
|
||||||
|
# This file was originally written by Joey Hess and Craig Small.
|
||||||
|
# As a special exception, when this file is copied by dh-make into a
|
||||||
|
# dh-make output file, you may use that output file without restriction.
|
||||||
|
# This special exception was added by Craig Small in version 0.37 of dh-make.
|
||||||
|
|
||||||
|
# Uncomment this to turn on verbose mode.
|
||||||
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
16
docs.sh
@@ -3,9 +3,9 @@ PLATFORM=$1
|
|||||||
if [ "$PLATFORM" = "Windows" ]; then
|
if [ "$PLATFORM" = "Windows" ]; then
|
||||||
RUNTIME="win-x64"
|
RUNTIME="win-x64"
|
||||||
elif [ "$PLATFORM" = "Linux" ]; then
|
elif [ "$PLATFORM" = "Linux" ]; then
|
||||||
RUNTIME="linux-x64"
|
WHERE="linux-x64"
|
||||||
elif [ "$PLATFORM" = "Mac" ]; then
|
elif [ "$PLATFORM" = "Mac" ]; then
|
||||||
RUNTIME="osx-x64"
|
WHERE="osx-x64"
|
||||||
else
|
else
|
||||||
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -21,24 +21,18 @@ slnFile=src/Radarr.sln
|
|||||||
|
|
||||||
platform=Posix
|
platform=Posix
|
||||||
|
|
||||||
if [ "$PLATFORM" = "Windows" ]; then
|
|
||||||
application=Radarr.Console.dll
|
|
||||||
else
|
|
||||||
application=Radarr.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/Radarr.Api.V3/openapi.json "$outputFolder/net6.0/$RUNTIME/$application" v3 &
|
dotnet tool run swagger tofile --output ./src/Radarr.Api.V3/openapi.json "$outputFolder/net6.0/$RUNTIME/radarr.console.dll" v3 &
|
||||||
|
|
||||||
sleep 45
|
sleep 45
|
||||||
|
|
||||||
kill %1
|
kill %1
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
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
@@ -0,0 +1,335 @@
|
|||||||
|
{
|
||||||
|
"indent": {
|
||||||
|
"value": " ",
|
||||||
|
"FunctionExpression": 1,
|
||||||
|
"ArrayExpression": 1,
|
||||||
|
"ObjectExpression": 1
|
||||||
|
},
|
||||||
|
"lineBreak": {
|
||||||
|
"value": "\n",
|
||||||
|
|
||||||
|
"before": {
|
||||||
|
"ArrayPatternClosing": 0,
|
||||||
|
"ArrayPatternComma": 0,
|
||||||
|
"ArrayPatternOpening": 0,
|
||||||
|
"ArrowFunctionExpressionArrow": 0,
|
||||||
|
"ArrowFunctionExpressionClosingBrace": ">=1",
|
||||||
|
"ArrowFunctionExpressionOpeningBrace": 0,
|
||||||
|
"AssignmentExpression": ">=1",
|
||||||
|
"AssignmentOperator": 0,
|
||||||
|
"BlockStatement": 0,
|
||||||
|
"BreakKeyword": ">=1",
|
||||||
|
"CallExpression": -1,
|
||||||
|
"CallExpressionClosingParentheses": -1,
|
||||||
|
"CallExpressionOpeningParentheses": 0,
|
||||||
|
"CatchClosingBrace": ">=1",
|
||||||
|
"CatchKeyword": 0,
|
||||||
|
"CatchOpeningBrace": 0,
|
||||||
|
"ClassDeclaration": ">=1",
|
||||||
|
"ClassDeclarationClosingBrace": ">=1",
|
||||||
|
"ClassDeclarationOpeningBrace": 0,
|
||||||
|
"ConditionalExpression": ">=1",
|
||||||
|
"DeleteOperator": ">=1",
|
||||||
|
"DoWhileStatement": ">=1",
|
||||||
|
"DoWhileStatementClosingBrace": ">=1",
|
||||||
|
"DoWhileStatementOpeningBrace": 0,
|
||||||
|
"ElseIfStatement": 0,
|
||||||
|
"ElseIfStatementClosingBrace": ">=1",
|
||||||
|
"ElseIfStatementOpeningBrace": 0,
|
||||||
|
"ElseStatement": 0,
|
||||||
|
"ElseStatementClosingBrace": ">=1",
|
||||||
|
"ElseStatementOpeningBrace": 0,
|
||||||
|
"EmptyStatement": -1,
|
||||||
|
"EndOfFile": -1,
|
||||||
|
"FinallyClosingBrace": ">=1",
|
||||||
|
"FinallyKeyword": -1,
|
||||||
|
"FinallyOpeningBrace": 0,
|
||||||
|
"ForInStatement": ">=1",
|
||||||
|
"ForInStatementClosingBrace": ">=1",
|
||||||
|
"ForInStatementExpressionClosing": 0,
|
||||||
|
"ForInStatementExpressionOpening": 0,
|
||||||
|
"ForInStatementOpeningBrace": 0,
|
||||||
|
"ForStatement": ">=1",
|
||||||
|
"ForStatementClosingBrace": ">=1",
|
||||||
|
"ForStatementExpressionClosing": "<2",
|
||||||
|
"ForStatementExpressionOpening": 0,
|
||||||
|
"ForStatementOpeningBrace": 0,
|
||||||
|
"FunctionDeclaration": ">=1",
|
||||||
|
"FunctionDeclarationClosingBrace": ">=1",
|
||||||
|
"FunctionDeclarationOpeningBrace": 0,
|
||||||
|
"FunctionExpression": 0,
|
||||||
|
"FunctionExpressionClosingBrace": 1,
|
||||||
|
"FunctionExpressionOpeningBrace":0,
|
||||||
|
"IIFEClosingParentheses": 0,
|
||||||
|
"IfStatement": ">=1",
|
||||||
|
"IfStatementClosingBrace": ">=1",
|
||||||
|
"IfStatementOpeningBrace": 0,
|
||||||
|
"LogicalExpression": -1,
|
||||||
|
"MemberExpressionClosing": 0,
|
||||||
|
"MemberExpressionOpening": 0,
|
||||||
|
"MemberExpressionPeriod": -1,
|
||||||
|
"MethodDefinition": ">=1",
|
||||||
|
"ObjectExpressionClosingBrace": "<=1",
|
||||||
|
"ObjectPatternClosingBrace": 0,
|
||||||
|
"ObjectPatternComma": 0,
|
||||||
|
"ObjectPatternOpeningBrace": 0,
|
||||||
|
"ParameterDefault": 0,
|
||||||
|
"Property": "<=2",
|
||||||
|
"PropertyValue": 0,
|
||||||
|
"ReturnStatement": -1,
|
||||||
|
"SwitchClosingBrace": ">=1",
|
||||||
|
"SwitchOpeningBrace": 0,
|
||||||
|
"ThisExpression": -1,
|
||||||
|
"ThrowStatement": ">=1",
|
||||||
|
"TryClosingBrace": ">=1",
|
||||||
|
"TryKeyword": -1,
|
||||||
|
"TryOpeningBrace": 0,
|
||||||
|
"VariableDeclaration": ">=1",
|
||||||
|
"VariableDeclarationSemiColon": 0,
|
||||||
|
"VariableDeclarationWithoutInit": ">=1",
|
||||||
|
"VariableName": ">=1",
|
||||||
|
"VariableValue": 0,
|
||||||
|
"WhileStatement": ">=1",
|
||||||
|
"WhileStatementClosingBrace": ">=1",
|
||||||
|
"WhileStatementOpeningBrace": 0
|
||||||
|
},
|
||||||
|
|
||||||
|
"after": {
|
||||||
|
"ArrayPatternClosing": 0,
|
||||||
|
"ArrayPatternComma": 0,
|
||||||
|
"ArrayPatternOpening": 0,
|
||||||
|
"ArrowFunctionExpressionArrow": 0,
|
||||||
|
"ArrowFunctionExpressionClosingBrace": -1,
|
||||||
|
"ArrowFunctionExpressionOpeningBrace": ">=1",
|
||||||
|
"AssignmentExpression": ">=1",
|
||||||
|
"AssignmentOperator": 0,
|
||||||
|
"BlockStatement": 0,
|
||||||
|
"BreakKeyword": -1,
|
||||||
|
"CallExpression": -1,
|
||||||
|
"CallExpressionClosingParentheses": -1,
|
||||||
|
"CallExpressionOpeningParentheses": -1,
|
||||||
|
"CatchClosingBrace": ">=0",
|
||||||
|
"CatchKeyword": 0,
|
||||||
|
"CatchOpeningBrace": ">=1",
|
||||||
|
"ClassDeclaration": ">=1",
|
||||||
|
"ClassDeclarationClosingBrace": ">=1",
|
||||||
|
"ClassDeclarationOpeningBrace": ">=1",
|
||||||
|
"ConditionalExpression": ">=1",
|
||||||
|
"DeleteOperator": ">=1",
|
||||||
|
"DoWhileStatement": ">=1",
|
||||||
|
"DoWhileStatementClosingBrace": 0,
|
||||||
|
"DoWhileStatementOpeningBrace": ">=1",
|
||||||
|
"ElseIfStatement": ">=1",
|
||||||
|
"ElseIfStatementClosingBrace": ">=1",
|
||||||
|
"ElseIfStatementOpeningBrace": ">=1",
|
||||||
|
"ElseStatement": ">=1",
|
||||||
|
"ElseStatementClosingBrace": ">=1",
|
||||||
|
"ElseStatementOpeningBrace": ">=1",
|
||||||
|
"EmptyStatement": -1,
|
||||||
|
"FinallyClosingBrace": ">=1",
|
||||||
|
"FinallyKeyword": -1,
|
||||||
|
"FinallyOpeningBrace": ">=1",
|
||||||
|
"ForInStatement": ">=1",
|
||||||
|
"ForInStatementClosingBrace": ">=1",
|
||||||
|
"ForInStatementExpressionClosing": -1,
|
||||||
|
"ForInStatementExpressionOpening": "<2",
|
||||||
|
"ForInStatementOpeningBrace": ">=1",
|
||||||
|
"ForStatement": ">=1",
|
||||||
|
"ForStatementClosingBrace": ">=1",
|
||||||
|
"ForStatementExpressionClosing": -1,
|
||||||
|
"ForStatementExpressionOpening": "<2",
|
||||||
|
"ForStatementOpeningBrace": ">=1",
|
||||||
|
"FunctionDeclaration": ">=1",
|
||||||
|
"FunctionDeclarationClosingBrace": ">=1",
|
||||||
|
"FunctionDeclarationOpeningBrace": ">=1",
|
||||||
|
"FunctionExpression": 0,
|
||||||
|
"FunctionExpressionClosingBrace": -1,
|
||||||
|
"FunctionExpressionOpeningBrace": 1,
|
||||||
|
"IIFEOpeningParentheses": 0,
|
||||||
|
"IfStatement": ">=1",
|
||||||
|
"IfStatementClosingBrace": ">=1",
|
||||||
|
"IfStatementOpeningBrace": ">=1",
|
||||||
|
"LogicalExpression": -1,
|
||||||
|
"MemberExpressionClosing": 0,
|
||||||
|
"MemberExpressionOpening": 0,
|
||||||
|
"MemberExpressionPeriod": 0,
|
||||||
|
"MethodDefinition": ">=1",
|
||||||
|
"ObjectExpressionOpeningBrace": "<=1",
|
||||||
|
"ObjectPatternClosingBrace": 0,
|
||||||
|
"ObjectPatternComma": 0,
|
||||||
|
"ObjectPatternOpeningBrace": 0,
|
||||||
|
"ParameterDefault": 0,
|
||||||
|
"Property": -1,
|
||||||
|
"PropertyName": 0,
|
||||||
|
"ReturnStatement": -1,
|
||||||
|
"SwitchCaseColon": ">=1",
|
||||||
|
"SwitchClosingBrace": ">=1",
|
||||||
|
"SwitchOpeningBrace": ">=1",
|
||||||
|
"ThisExpression": 0,
|
||||||
|
"ThrowStatement": ">=1",
|
||||||
|
"TryClosingBrace": 0,
|
||||||
|
"TryKeyword": -1,
|
||||||
|
"TryOpeningBrace": ">=1",
|
||||||
|
"VariableDeclaration": ">=1",
|
||||||
|
"VariableDeclarationSemiColon": ">=1",
|
||||||
|
"VariableValue": -1,
|
||||||
|
"WhileStatement": ">=1",
|
||||||
|
"WhileStatementClosingBrace": ">=1",
|
||||||
|
"WhileStatementOpeningBrace": ">=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"whiteSpace": {
|
||||||
|
"value": " ",
|
||||||
|
"removeTrailing": 1,
|
||||||
|
"before": {
|
||||||
|
"ArgumentComma": 0,
|
||||||
|
"ArgumentList": 0,
|
||||||
|
"ArgumentListArrayExpression": 0,
|
||||||
|
"ArgumentListFunctionExpression": 1,
|
||||||
|
"ArgumentListObjectExpression": 0,
|
||||||
|
"ArrayExpressionClosing": 0,
|
||||||
|
"ArrayExpressionComma": 0,
|
||||||
|
"ArrayExpressionOpening": 1,
|
||||||
|
"AssignmentOperator": 1,
|
||||||
|
"BinaryExpression": 0,
|
||||||
|
"BinaryExpressionOperator": 1,
|
||||||
|
"BlockComment": 1,
|
||||||
|
"CallExpression": 1,
|
||||||
|
"CatchClosingBrace": 1,
|
||||||
|
"CatchKeyword": 1,
|
||||||
|
"CatchOpeningBrace": 1,
|
||||||
|
"CatchParameterList": 0,
|
||||||
|
"CommaOperator": 0,
|
||||||
|
"ConditionalExpressionAlternate": 1,
|
||||||
|
"ConditionalExpressionConsequent": 1,
|
||||||
|
"DoWhileStatementClosingBrace": 1,
|
||||||
|
"DoWhileStatementConditional": 1,
|
||||||
|
"DoWhileStatementOpeningBrace": 1,
|
||||||
|
"ElseIfStatementClosingBrace": 1,
|
||||||
|
"ElseIfStatementOpeningBrace": 1,
|
||||||
|
"ElseStatementClosingBrace": 1,
|
||||||
|
"ElseStatementOpeningBrace": 1,
|
||||||
|
"EmptyStatement": 0,
|
||||||
|
"ExpressionClosingParentheses": 0,
|
||||||
|
"FinallyClosingBrace": 1,
|
||||||
|
"FinallyKeyword": -1,
|
||||||
|
"FinallyOpeningBrace": 1,
|
||||||
|
"ForInStatement": 1,
|
||||||
|
"ForInStatementClosingBrace": 1,
|
||||||
|
"ForInStatementExpressionClosing": 0,
|
||||||
|
"ForInStatementExpressionOpening": 1,
|
||||||
|
"ForInStatementOpeningBrace": 1,
|
||||||
|
"ForStatement": 1,
|
||||||
|
"ForStatementClosingBrace": 1,
|
||||||
|
"ForStatementExpressionClosing": 0,
|
||||||
|
"ForStatementExpressionOpening": 1,
|
||||||
|
"ForStatementOpeningBrace": 1,
|
||||||
|
"ForStatementSemicolon": 0,
|
||||||
|
"FunctionDeclarationClosingBrace": 1,
|
||||||
|
"FunctionDeclarationOpeningBrace": 1,
|
||||||
|
"FunctionExpressionClosingBrace": 1,
|
||||||
|
"FunctionExpressionOpeningBrace": 1,
|
||||||
|
"IfStatementClosingBrace": 1,
|
||||||
|
"IfStatementConditionalClosing": 0,
|
||||||
|
"IfStatementConditionalOpening": 1,
|
||||||
|
"IfStatementOpeningBrace": 1,
|
||||||
|
"LineComment": 1,
|
||||||
|
"LogicalExpressionOperator": 1,
|
||||||
|
"MemberExpressionClosing": 0,
|
||||||
|
"ObjectExpressionClosingBrace": 1,
|
||||||
|
"ParameterComma": 0,
|
||||||
|
"ParameterList": 0,
|
||||||
|
"Property": 1,
|
||||||
|
"PropertyName": 1,
|
||||||
|
"PropertyValue": 1,
|
||||||
|
"SwitchDiscriminantClosing": 0,
|
||||||
|
"SwitchDiscriminantOpening": 1,
|
||||||
|
"ThrowKeyword": 1,
|
||||||
|
"TryClosingBrace": 1,
|
||||||
|
"TryKeyword": -1,
|
||||||
|
"TryOpeningBrace": 1,
|
||||||
|
"UnaryExpressionOperator": 0,
|
||||||
|
"VariableName": 1,
|
||||||
|
"VariableValue": 1,
|
||||||
|
"WhileStatementClosingBrace": 1,
|
||||||
|
"WhileStatementConditionalClosing": 0,
|
||||||
|
"WhileStatementConditionalOpening": 1,
|
||||||
|
"WhileStatementOpeningBrace": 1
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"ArgumentComma": 1,
|
||||||
|
"ArgumentList": 0,
|
||||||
|
"ArgumentListArrayExpression": 1,
|
||||||
|
"ArgumentListFunctionExpression": 1,
|
||||||
|
"ArgumentListObjectExpression": 0,
|
||||||
|
"ArrayExpressionClosing": 0,
|
||||||
|
"ArrayExpressionComma": 1,
|
||||||
|
"ArrayExpressionOpening": 0,
|
||||||
|
"AssignmentOperator": 1,
|
||||||
|
"BinaryExpression": 0,
|
||||||
|
"BinaryExpressionOperator": 1,
|
||||||
|
"BlockComment": 1,
|
||||||
|
"CallExpression": 0,
|
||||||
|
"CatchClosingBrace": 1,
|
||||||
|
"CatchKeyword": 1,
|
||||||
|
"CatchOpeningBrace": 1,
|
||||||
|
"CatchParameterList": 0,
|
||||||
|
"CommaOperator": 1,
|
||||||
|
"ConditionalExpressionConsequent": 1,
|
||||||
|
"ConditionalExpressionTest": 1,
|
||||||
|
"DoWhileStatementBody": 1,
|
||||||
|
"DoWhileStatementClosingBrace": 1,
|
||||||
|
"DoWhileStatementOpeningBrace": 1,
|
||||||
|
"ElseIfStatementClosingBrace": 1,
|
||||||
|
"ElseIfStatementOpeningBrace": 1,
|
||||||
|
"ElseStatementClosingBrace": 1,
|
||||||
|
"ElseStatementOpeningBrace": 1,
|
||||||
|
"EmptyStatement": 0,
|
||||||
|
"ExpressionOpeningParentheses": 0,
|
||||||
|
"FinallyClosingBrace": 1,
|
||||||
|
"FinallyKeyword": -1,
|
||||||
|
"FinallyOpeningBrace": 1,
|
||||||
|
"ForInStatement": 1,
|
||||||
|
"ForInStatementClosingBrace": 1,
|
||||||
|
"ForInStatementExpressionClosing": 1,
|
||||||
|
"ForInStatementExpressionOpening": 0,
|
||||||
|
"ForInStatementOpeningBrace": 1,
|
||||||
|
"ForStatement": 1,
|
||||||
|
"ForStatementClosingBrace": 1,
|
||||||
|
"ForStatementExpressionClosing": 1,
|
||||||
|
"ForStatementExpressionOpening": 0,
|
||||||
|
"ForStatementOpeningBrace": 1,
|
||||||
|
"ForStatementSemicolon": 1,
|
||||||
|
"FunctionDeclarationClosingBrace": 0,
|
||||||
|
"FunctionDeclarationOpeningBrace": 0,
|
||||||
|
"FunctionExpressionClosingBrace": 0,
|
||||||
|
"FunctionExpressionOpeningBrace": 0,
|
||||||
|
"FunctionName": 0,
|
||||||
|
"FunctionReservedWord": 0,
|
||||||
|
"IfStatementClosingBrace": 1,
|
||||||
|
"IfStatementConditionalClosing": 0,
|
||||||
|
"IfStatementConditionalOpening": 0,
|
||||||
|
"IfStatementOpeningBrace": 1,
|
||||||
|
"LogicalExpressionOperator": 1,
|
||||||
|
"MemberExpressionOpening": 0,
|
||||||
|
"ObjectExpressionClosingBrace": 0,
|
||||||
|
"ObjectExpressionOpeningBrace": 1,
|
||||||
|
"ParameterComma": 1,
|
||||||
|
"ParameterList": 0,
|
||||||
|
"PropertyName": 0,
|
||||||
|
"PropertyValue": 0,
|
||||||
|
"SwitchDiscriminantClosing": 1,
|
||||||
|
"SwitchDiscriminantOpening": 0,
|
||||||
|
"ThrowKeyword": 1,
|
||||||
|
"TryClosingBrace": 1,
|
||||||
|
"TryKeyword": -1,
|
||||||
|
"TryOpeningBrace": 1,
|
||||||
|
"UnaryExpressionOperator": 0,
|
||||||
|
"VariableName": 1,
|
||||||
|
"WhileStatementClosingBrace": 1,
|
||||||
|
"WhileStatementConditionalClosing": 1,
|
||||||
|
"WhileStatementConditionalOpening": 0,
|
||||||
|
"WhileStatementOpeningBrace": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1 @@
|
|||||||
**/JsLibraries/**
|
**/JsLibraries/**
|
||||||
**/*.css.d.ts
|
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
const path = require('path');
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin').configs.recommended;
|
|
||||||
|
|
||||||
const frontendFolder = __dirname;
|
|
||||||
|
|
||||||
const dirs = fs
|
const dirs = fs
|
||||||
.readdirSync(path.join(frontendFolder, 'src'), { withFileTypes: true })
|
.readdirSync('frontend/src', { withFileTypes: true })
|
||||||
.filter((dirent) => dirent.isDirectory())
|
.filter((dirent) => dirent.isDirectory())
|
||||||
.map((dirent) => dirent.name)
|
.map((dirent) => dirent.name)
|
||||||
.join('|');
|
.join('|');
|
||||||
|
|
||||||
module.exports = {
|
const frontendFolder = __dirname;
|
||||||
root: true,
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
parser: '@babel/eslint-parser',
|
parser: '@babel/eslint-parser',
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
@@ -28,8 +21,7 @@ module.exports = {
|
|||||||
globals: {
|
globals: {
|
||||||
expect: false,
|
expect: false,
|
||||||
chai: false,
|
chai: false,
|
||||||
sinon: false,
|
sinon: false
|
||||||
JSX: true
|
|
||||||
},
|
},
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
@@ -47,11 +39,8 @@ module.exports = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
'filenames',
|
'filenames',
|
||||||
'react',
|
'react',
|
||||||
'react-hooks',
|
|
||||||
'simple-import-sort',
|
'simple-import-sort',
|
||||||
'import',
|
'import'
|
||||||
'@typescript-eslint',
|
|
||||||
'prettier'
|
|
||||||
],
|
],
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
@@ -234,7 +223,7 @@ module.exports = {
|
|||||||
'consistent-this': ['error', 'self'],
|
'consistent-this': ['error', 'self'],
|
||||||
'eol-last': 'error',
|
'eol-last': 'error',
|
||||||
'func-names': 'off',
|
'func-names': 'off',
|
||||||
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
|
'func-style': ['error', 'declaration'],
|
||||||
indent: ['error', 2, { SwitchCase: 1 }],
|
indent: ['error', 2, { SwitchCase: 1 }],
|
||||||
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
||||||
'keyword-spacing': ['error', { before: true, after: true }],
|
'keyword-spacing': ['error', { before: true, after: true }],
|
||||||
@@ -319,15 +308,11 @@ module.exports = {
|
|||||||
'react/react-in-jsx-scope': 2,
|
'react/react-in-jsx-scope': 2,
|
||||||
'react/self-closing-comp': 2,
|
'react/self-closing-comp': 2,
|
||||||
'react/sort-comp': 2,
|
'react/sort-comp': 2,
|
||||||
'react/jsx-wrap-multilines': 2,
|
'react/jsx-wrap-multilines': 2
|
||||||
'react-hooks/rules-of-hooks': 'error',
|
|
||||||
'react-hooks/exhaustive-deps': 'error'
|
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: [
|
files: ['*.js'],
|
||||||
'*.js'
|
|
||||||
],
|
|
||||||
rules: {
|
rules: {
|
||||||
'simple-import-sort/imports': [
|
'simple-import-sort/imports': [
|
||||||
'error',
|
'error',
|
||||||
@@ -342,91 +327,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
@@ -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
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"stylelint.vscode-stylelint",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"esbenp.prettier-vscode"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
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';
|
||||||
@@ -36,22 +34,17 @@ module.exports = (env) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
entry: {
|
entry: {
|
||||||
index: 'index.ts'
|
index: 'index.js'
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [
|
|
||||||
'.ts',
|
|
||||||
'.tsx',
|
|
||||||
'.js'
|
|
||||||
],
|
|
||||||
modules: [
|
modules: [
|
||||||
srcFolder,
|
srcFolder,
|
||||||
path.join(srcFolder, 'Shims'),
|
path.join(srcFolder, 'Shims'),
|
||||||
'node_modules'
|
'node_modules'
|
||||||
],
|
],
|
||||||
alias: {
|
alias: {
|
||||||
jquery: 'jquery/dist/jquery.min'
|
jquery: 'jquery/src/jquery'
|
||||||
},
|
},
|
||||||
fallback: {
|
fallback: {
|
||||||
buffer: false,
|
buffer: false,
|
||||||
@@ -66,23 +59,23 @@ module.exports = (env) => {
|
|||||||
output: {
|
output: {
|
||||||
path: distFolder,
|
path: distFolder,
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
filename: 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,
|
||||||
@@ -90,15 +83,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({
|
||||||
@@ -139,8 +130,6 @@ module.exports = (env) => {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new ForkTsCheckerWebpackPlugin(),
|
|
||||||
|
|
||||||
new LiveReloadPlugin()
|
new LiveReloadPlugin()
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -164,7 +153,7 @@ module.exports = (env) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: [/\.jsx?$/, /\.tsx?$/],
|
test: /\.js?$/,
|
||||||
exclude: /(node_modules|JsLibraries)/,
|
exclude: /(node_modules|JsLibraries)/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
@@ -195,13 +184,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]'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -235,7 +223,7 @@ module.exports = (env) => {
|
|||||||
{
|
{
|
||||||
loader: 'url-loader',
|
loader: 'url-loader',
|
||||||
options: {
|
options: {
|
||||||
limit: 10240,
|
limit: 24096,
|
||||||
mimetype: 'application/font-woff',
|
mimetype: 'application/font-woff',
|
||||||
emitFile: false,
|
emitFile: false,
|
||||||
name: 'Content/Fonts/[name].[ext]'
|
name: 'Content/Fonts/[name].[ext]'
|
||||||
@@ -245,11 +233,12 @@ module.exports = (env) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.(ttf|eot|eot?#iefix|gif|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
|
limit: 24096,
|
||||||
emitFile: false,
|
emitFile: false,
|
||||||
name: 'Content/Fonts/[name].[ext]'
|
name: 'Content/Fonts/[name].[ext]'
|
||||||
}
|
}
|
||||||
@@ -264,19 +253,18 @@ module.exports = (env) => {
|
|||||||
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
||||||
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
||||||
|
|
||||||
config.optimization = {
|
config.optimization.minimizer = [
|
||||||
minimize: true,
|
new TerserPlugin({
|
||||||
minimizer: [
|
cache: true,
|
||||||
new TerserPlugin({
|
parallel: true,
|
||||||
terserOptions: {
|
sourceMap: true, // Must be set to true if using source-maps in production
|
||||||
sourceMap: true, // Must be set to true if using source-maps in production
|
terserOptions: {
|
||||||
mangle: false,
|
mangle: false,
|
||||||
keep_classnames: true,
|
keep_classnames: true,
|
||||||
keep_fnames: true
|
keep_fnames: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
]
|
];
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// eslint-disable-next-line filenames/match-exported
|
|
||||||
const loaderUtils = require('loader-utils');
|
const loaderUtils = require('loader-utils');
|
||||||
|
|
||||||
module.exports = function cssVariablesLoader(source) {
|
module.exports = function cssVariablesLoader(source) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const reload = require('require-nocache')(module);
|
const reload = require('require-nocache')(module);
|
||||||
|
|
||||||
const cssVarsFiles = [
|
const cssVarsFiles = [
|
||||||
|
'./src/Styles/Variables/colors',
|
||||||
'./src/Styles/Variables/dimensions',
|
'./src/Styles/Variables/dimensions',
|
||||||
'./src/Styles/Variables/fonts',
|
'./src/Styles/Variables/fonts',
|
||||||
'./src/Styles/Variables/animations',
|
'./src/Styles/Variables/animations',
|
||||||
@@ -28,4 +29,4 @@ module.exports = {
|
|||||||
'postcss-color-function',
|
'postcss-color-function',
|
||||||
'postcss-nested'
|
'postcss-nested'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
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,8 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
@@ -21,7 +19,6 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
|||||||
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
||||||
import selectAll from 'Utilities/Table/selectAll';
|
import selectAll from 'Utilities/Table/selectAll';
|
||||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||||
import BlocklistFilterModal from './BlocklistFilterModal';
|
|
||||||
import BlocklistRowConnector from './BlocklistRowConnector';
|
import BlocklistRowConnector from './BlocklistRowConnector';
|
||||||
|
|
||||||
class Blocklist extends Component {
|
class Blocklist extends Component {
|
||||||
@@ -38,7 +35,6 @@ class Blocklist extends Component {
|
|||||||
lastToggled: null,
|
lastToggled: null,
|
||||||
selectedState: {},
|
selectedState: {},
|
||||||
isConfirmRemoveModalOpen: false,
|
isConfirmRemoveModalOpen: false,
|
||||||
isConfirmClearModalOpen: false,
|
|
||||||
items: props.items
|
items: props.items
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -93,19 +89,6 @@ class Blocklist extends Component {
|
|||||||
this.setState({ isConfirmRemoveModalOpen: false });
|
this.setState({ isConfirmRemoveModalOpen: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
onClearBlocklistPress = () => {
|
|
||||||
this.setState({ isConfirmClearModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onClearBlocklistConfirmed = () => {
|
|
||||||
this.props.onClearBlocklistPress();
|
|
||||||
this.setState({ isConfirmClearModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onConfirmClearModalClose = () => {
|
|
||||||
this.setState({ isConfirmClearModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
|
||||||
@@ -116,13 +99,10 @@ class Blocklist extends Component {
|
|||||||
error,
|
error,
|
||||||
items,
|
items,
|
||||||
columns,
|
columns,
|
||||||
selectedFilterKey,
|
|
||||||
filters,
|
|
||||||
customFilters,
|
|
||||||
totalRecords,
|
totalRecords,
|
||||||
isRemoving,
|
isRemoving,
|
||||||
isClearingBlocklistExecuting,
|
isClearingBlocklistExecuting,
|
||||||
onFilterSelect,
|
onClearBlocklistPress,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -130,8 +110,7 @@ class Blocklist extends Component {
|
|||||||
allSelected,
|
allSelected,
|
||||||
allUnselected,
|
allUnselected,
|
||||||
selectedState,
|
selectedState,
|
||||||
isConfirmRemoveModalOpen,
|
isConfirmRemoveModalOpen
|
||||||
isConfirmClearModalOpen
|
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const selectedIds = this.getSelectedIds();
|
const selectedIds = this.getSelectedIds();
|
||||||
@@ -151,9 +130,8 @@ class Blocklist extends Component {
|
|||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
label={translate('Clear')}
|
label={translate('Clear')}
|
||||||
iconName={icons.CLEAR}
|
iconName={icons.CLEAR}
|
||||||
isDisabled={!items.length}
|
|
||||||
isSpinning={isClearingBlocklistExecuting}
|
isSpinning={isClearingBlocklistExecuting}
|
||||||
onPress={this.onClearBlocklistPress}
|
onPress={onClearBlocklistPress}
|
||||||
/>
|
/>
|
||||||
</PageToolbarSection>
|
</PageToolbarSection>
|
||||||
|
|
||||||
@@ -167,15 +145,6 @@ class Blocklist extends Component {
|
|||||||
iconName={icons.TABLE}
|
iconName={icons.TABLE}
|
||||||
/>
|
/>
|
||||||
</TableOptionsModalWrapper>
|
</TableOptionsModalWrapper>
|
||||||
|
|
||||||
<FilterMenu
|
|
||||||
alignMenu={align.RIGHT}
|
|
||||||
selectedFilterKey={selectedFilterKey}
|
|
||||||
filters={filters}
|
|
||||||
customFilters={customFilters}
|
|
||||||
filterModalConnectorComponent={BlocklistFilterModal}
|
|
||||||
onFilterSelect={onFilterSelect}
|
|
||||||
/>
|
|
||||||
</PageToolbarSection>
|
</PageToolbarSection>
|
||||||
</PageToolbar>
|
</PageToolbar>
|
||||||
|
|
||||||
@@ -187,20 +156,16 @@ class Blocklist extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && !!error &&
|
!isFetching && !!error &&
|
||||||
<Alert kind={kinds.DANGER}>
|
<div>
|
||||||
{translate('BlocklistLoadError')}
|
{translate('UnableToLoadBlocklist')}
|
||||||
</Alert>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !error && !items.length &&
|
isPopulated && !error && !items.length &&
|
||||||
<Alert kind={kinds.INFO}>
|
<div>
|
||||||
{
|
{translate('NoHistory')}
|
||||||
selectedFilterKey === 'all' ?
|
</div>
|
||||||
translate('NoHistoryBlocklist') :
|
|
||||||
translate('BlocklistFilterHasNoItems')
|
|
||||||
}
|
|
||||||
</Alert>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -244,21 +209,11 @@ class Blocklist extends Component {
|
|||||||
isOpen={isConfirmRemoveModalOpen}
|
isOpen={isConfirmRemoveModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('RemoveSelected')}
|
title={translate('RemoveSelected')}
|
||||||
message={translate('RemoveSelectedBlocklistMessageText')}
|
message={translate('AreYouSureYouWantToRemoveTheSelectedItemsFromBlocklist')}
|
||||||
confirmLabel={translate('RemoveSelected')}
|
confirmLabel={translate('RemoveSelected')}
|
||||||
onConfirm={this.onRemoveSelectedConfirmed}
|
onConfirm={this.onRemoveSelectedConfirmed}
|
||||||
onCancel={this.onConfirmRemoveModalClose}
|
onCancel={this.onConfirmRemoveModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={isConfirmClearModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title={translate('ClearBlocklist')}
|
|
||||||
message={translate('ClearBlocklistMessageText')}
|
|
||||||
confirmLabel={translate('Clear')}
|
|
||||||
onConfirm={this.onClearBlocklistConfirmed}
|
|
||||||
onCancel={this.onConfirmClearModalClose}
|
|
||||||
/>
|
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -270,15 +225,11 @@ Blocklist.propTypes = {
|
|||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
|
||||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
totalRecords: PropTypes.number,
|
totalRecords: PropTypes.number,
|
||||||
isRemoving: PropTypes.bool.isRequired,
|
isRemoving: PropTypes.bool.isRequired,
|
||||||
isClearingBlocklistExecuting: PropTypes.bool.isRequired,
|
isClearingBlocklistExecuting: PropTypes.bool.isRequired,
|
||||||
onRemoveSelected: PropTypes.func.isRequired,
|
onRemoveSelected: PropTypes.func.isRequired,
|
||||||
onClearBlocklistPress: PropTypes.func.isRequired,
|
onClearBlocklistPress: PropTypes.func.isRequired
|
||||||
onFilterSelect: PropTypes.func.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Blocklist;
|
export default Blocklist;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import * as commandNames from 'Commands/commandNames';
|
|||||||
import withCurrentPage from 'Components/withCurrentPage';
|
import withCurrentPage from 'Components/withCurrentPage';
|
||||||
import * as blocklistActions from 'Store/Actions/blocklistActions';
|
import * as blocklistActions from 'Store/Actions/blocklistActions';
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
|
|
||||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||||
import Blocklist from './Blocklist';
|
import Blocklist from './Blocklist';
|
||||||
@@ -14,12 +13,10 @@ import Blocklist from './Blocklist';
|
|||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.blocklist,
|
(state) => state.blocklist,
|
||||||
createCustomFiltersSelector('blocklist'),
|
|
||||||
createCommandExecutingSelector(commandNames.CLEAR_BLOCKLIST),
|
createCommandExecutingSelector(commandNames.CLEAR_BLOCKLIST),
|
||||||
(blocklist, customFilters, isClearingBlocklistExecuting) => {
|
(blocklist, isClearingBlocklistExecuting) => {
|
||||||
return {
|
return {
|
||||||
isClearingBlocklistExecuting,
|
isClearingBlocklistExecuting,
|
||||||
customFilters,
|
|
||||||
...blocklist
|
...blocklist
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -100,14 +97,6 @@ class BlocklistConnector extends Component {
|
|||||||
this.props.setBlocklistSort({ sortKey });
|
this.props.setBlocklistSort({ sortKey });
|
||||||
};
|
};
|
||||||
|
|
||||||
onFilterSelect = (selectedFilterKey) => {
|
|
||||||
this.props.setBlocklistFilter({ selectedFilterKey });
|
|
||||||
};
|
|
||||||
|
|
||||||
onClearBlocklistPress = () => {
|
|
||||||
this.props.executeCommand({ name: commandNames.CLEAR_BLOCKLIST });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTableOptionChange = (payload) => {
|
onTableOptionChange = (payload) => {
|
||||||
this.props.setBlocklistTableOption(payload);
|
this.props.setBlocklistTableOption(payload);
|
||||||
|
|
||||||
@@ -116,6 +105,10 @@ class BlocklistConnector extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onClearBlocklistPress = () => {
|
||||||
|
this.props.executeCommand({ name: commandNames.CLEAR_BLOCKLIST });
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
|
||||||
@@ -129,7 +122,6 @@ class BlocklistConnector extends Component {
|
|||||||
onPageSelect={this.onPageSelect}
|
onPageSelect={this.onPageSelect}
|
||||||
onRemoveSelected={this.onRemoveSelected}
|
onRemoveSelected={this.onRemoveSelected}
|
||||||
onSortPress={this.onSortPress}
|
onSortPress={this.onSortPress}
|
||||||
onFilterSelect={this.onFilterSelect}
|
|
||||||
onTableOptionChange={this.onTableOptionChange}
|
onTableOptionChange={this.onTableOptionChange}
|
||||||
onClearBlocklistPress={this.onClearBlocklistPress}
|
onClearBlocklistPress={this.onClearBlocklistPress}
|
||||||
{...this.props}
|
{...this.props}
|
||||||
@@ -150,7 +142,6 @@ BlocklistConnector.propTypes = {
|
|||||||
gotoBlocklistPage: PropTypes.func.isRequired,
|
gotoBlocklistPage: PropTypes.func.isRequired,
|
||||||
removeBlocklistItems: PropTypes.func.isRequired,
|
removeBlocklistItems: PropTypes.func.isRequired,
|
||||||
setBlocklistSort: PropTypes.func.isRequired,
|
setBlocklistSort: PropTypes.func.isRequired,
|
||||||
setBlocklistFilter: PropTypes.func.isRequired,
|
|
||||||
setBlocklistTableOption: PropTypes.func.isRequired,
|
setBlocklistTableOption: PropTypes.func.isRequired,
|
||||||
clearBlocklist: PropTypes.func.isRequired,
|
clearBlocklist: PropTypes.func.isRequired,
|
||||||
executeCommand: PropTypes.func.isRequired
|
executeCommand: PropTypes.func.isRequired
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import React, { useCallback } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import AppState from 'App/State/AppState';
|
|
||||||
import FilterModal from 'Components/Filter/FilterModal';
|
|
||||||
import { setBlocklistFilter } from 'Store/Actions/blocklistActions';
|
|
||||||
|
|
||||||
function createBlocklistSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.blocklist.items,
|
|
||||||
(blocklistItems) => {
|
|
||||||
return blocklistItems;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilterBuilderPropsSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.blocklist.filterBuilderProps,
|
|
||||||
(filterBuilderProps) => {
|
|
||||||
return filterBuilderProps;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BlocklistFilterModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function BlocklistFilterModal(props: BlocklistFilterModalProps) {
|
|
||||||
const sectionItems = useSelector(createBlocklistSelector());
|
|
||||||
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
|
|
||||||
const customFilterType = 'blocklist';
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const dispatchSetFilter = useCallback(
|
|
||||||
(payload: unknown) => {
|
|
||||||
dispatch(setBlocklistFilter(payload));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FilterModal
|
|
||||||
// TODO: Don't spread all the props
|
|
||||||
{...props}
|
|
||||||
sectionItems={sectionItems}
|
|
||||||
filterBuilderProps={filterBuilderProps}
|
|
||||||
customFilterType={customFilterType}
|
|
||||||
dispatchSetFilter={dispatchSetFilter}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'actions': string;
|
|
||||||
'indexer': string;
|
|
||||||
'language': string;
|
|
||||||
'quality': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -7,7 +7,7 @@ import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
|||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import MovieFormats from 'Movie/MovieFormats';
|
import MovieFormats from 'Movie/MovieFormats';
|
||||||
import MovieLanguages from 'Movie/MovieLanguages';
|
import MovieLanguage from 'Movie/MovieLanguage';
|
||||||
import MovieQuality from 'Movie/MovieQuality';
|
import MovieQuality from 'Movie/MovieQuality';
|
||||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
@@ -82,7 +82,7 @@ class BlocklistRow extends Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'movieMetadata.sortTitle') {
|
if (name === 'movies.sortTitle') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<MovieTitleLink
|
<MovieTitleLink
|
||||||
@@ -104,7 +104,7 @@ class BlocklistRow extends Component {
|
|||||||
if (name === 'languages') {
|
if (name === 'languages') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<MovieLanguages
|
<MovieLanguage
|
||||||
languages={languages}
|
languages={languages}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'description': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -7,7 +7,6 @@ import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionList
|
|||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||||
import formatAge from 'Utilities/Number/formatAge';
|
import formatAge from 'Utilities/Number/formatAge';
|
||||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './HistoryDetails.css';
|
import styles from './HistoryDetails.css';
|
||||||
|
|
||||||
@@ -16,7 +15,6 @@ function HistoryDetails(props) {
|
|||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
data,
|
data,
|
||||||
downloadId,
|
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
timeFormat
|
timeFormat
|
||||||
} = props;
|
} = props;
|
||||||
@@ -25,11 +23,10 @@ function HistoryDetails(props) {
|
|||||||
const {
|
const {
|
||||||
indexer,
|
indexer,
|
||||||
releaseGroup,
|
releaseGroup,
|
||||||
movieMatchType,
|
|
||||||
customFormatScore,
|
|
||||||
nzbInfoUrl,
|
nzbInfoUrl,
|
||||||
downloadClient,
|
downloadClient,
|
||||||
downloadClientName,
|
downloadClientName,
|
||||||
|
downloadId,
|
||||||
age,
|
age,
|
||||||
ageHours,
|
ageHours,
|
||||||
ageMinutes,
|
ageMinutes,
|
||||||
@@ -47,55 +44,33 @@ function HistoryDetails(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
indexer ?
|
!!indexer &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Indexer')}
|
title={translate('Indexer')}
|
||||||
data={indexer}
|
data={indexer}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
releaseGroup ?
|
!!releaseGroup &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('ReleaseGroup')}
|
title={translate('ReleaseGroup')}
|
||||||
data={releaseGroup}
|
data={releaseGroup}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
customFormatScore && customFormatScore !== '0' ?
|
!!nzbInfoUrl &&
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('CustomFormatScore')}
|
|
||||||
data={formatCustomFormatScore(customFormatScore)}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
movieMatchType ?
|
|
||||||
<DescriptionListItem
|
|
||||||
descriptionClassName={styles.description}
|
|
||||||
title={translate('MovieMatchType')}
|
|
||||||
data={movieMatchType}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
nzbInfoUrl ?
|
|
||||||
<span>
|
<span>
|
||||||
<DescriptionListItemTitle>
|
<DescriptionListItemTitle>
|
||||||
{translate('InfoUrl')}
|
Info URL
|
||||||
</DescriptionListItemTitle>
|
</DescriptionListItemTitle>
|
||||||
|
|
||||||
<DescriptionListItemDescription>
|
<DescriptionListItemDescription>
|
||||||
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
|
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
|
||||||
</DescriptionListItemDescription>
|
</DescriptionListItemDescription>
|
||||||
</span> :
|
</span>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -108,30 +83,27 @@ function HistoryDetails(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
downloadId ?
|
!!downloadId &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('GrabId')}
|
title={translate('GrabID')}
|
||||||
data={downloadId}
|
data={downloadId}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
indexer ?
|
!!indexer &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('AgeWhenGrabbed')}
|
title={translate('AgeWhenGrabbed')}
|
||||||
data={formatAge(age, ageHours, ageMinutes)}
|
data={formatAge(age, ageHours, ageMinutes)}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
publishedDate ?
|
!!publishedDate &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('PublishedDate')}
|
title={translate('PublishedDate')}
|
||||||
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
@@ -151,21 +123,11 @@ function HistoryDetails(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
downloadId ?
|
!!message &&
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('GrabId')}
|
|
||||||
data={downloadId}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
message ?
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Message')}
|
title={translate('Message')}
|
||||||
data={message}
|
data={message}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
@@ -173,7 +135,6 @@ function HistoryDetails(props) {
|
|||||||
|
|
||||||
if (eventType === 'downloadFolderImported') {
|
if (eventType === 'downloadFolderImported') {
|
||||||
const {
|
const {
|
||||||
customFormatScore,
|
|
||||||
droppedPath,
|
droppedPath,
|
||||||
importedPath
|
importedPath
|
||||||
} = data;
|
} = data;
|
||||||
@@ -187,32 +148,21 @@ function HistoryDetails(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
droppedPath ?
|
!!droppedPath &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Source')}
|
title={translate('Source')}
|
||||||
data={droppedPath}
|
data={droppedPath}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
importedPath ?
|
!!importedPath &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('ImportedTo')}
|
title={translate('ImportedTo')}
|
||||||
data={importedPath}
|
data={importedPath}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
customFormatScore && customFormatScore !== '0' ?
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('CustomFormatScore')}
|
|
||||||
data={formatCustomFormatScore(customFormatScore)}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
@@ -220,21 +170,20 @@ function HistoryDetails(props) {
|
|||||||
|
|
||||||
if (eventType === 'movieFileDeleted') {
|
if (eventType === 'movieFileDeleted') {
|
||||||
const {
|
const {
|
||||||
reason,
|
reason
|
||||||
customFormatScore
|
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let reasonMessage = '';
|
let reasonMessage = '';
|
||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case 'Manual':
|
case 'Manual':
|
||||||
reasonMessage = translate('DeletedReasonManual');
|
reasonMessage = translate('FileWasDeletedByViaUI');
|
||||||
break;
|
break;
|
||||||
case 'MissingFromDisk':
|
case 'MissingFromDisk':
|
||||||
reasonMessage = translate('DeletedReasonMissingFromDisk');
|
reasonMessage = translate('MissingFromDisk');
|
||||||
break;
|
break;
|
||||||
case 'Upgrade':
|
case 'Upgrade':
|
||||||
reasonMessage = translate('DeletedReasonUpgrade');
|
reasonMessage = translate('FileWasDeletedByUpgrade');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
reasonMessage = '';
|
reasonMessage = '';
|
||||||
@@ -251,15 +200,6 @@ function HistoryDetails(props) {
|
|||||||
title={translate('Reason')}
|
title={translate('Reason')}
|
||||||
data={reasonMessage}
|
data={reasonMessage}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
|
||||||
customFormatScore && customFormatScore !== '0' ?
|
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('CustomFormatScore')}
|
|
||||||
data={formatCustomFormatScore(customFormatScore)}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -311,21 +251,11 @@ function HistoryDetails(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
downloadId ?
|
!!message &&
|
||||||
<DescriptionListItem
|
|
||||||
title={translate('GrabId')}
|
|
||||||
data={downloadId}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
message ?
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Message')}
|
title={translate('Message')}
|
||||||
data={message}
|
data={message}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
);
|
);
|
||||||
@@ -346,7 +276,6 @@ HistoryDetails.propTypes = {
|
|||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
downloadId: PropTypes.string,
|
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired
|
timeFormat: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'markAsFailedButton': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -15,19 +15,19 @@ import styles from './HistoryDetailsModal.css';
|
|||||||
function getHeaderTitle(eventType) {
|
function getHeaderTitle(eventType) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'grabbed':
|
||||||
return translate('Grabbed');
|
return 'Grabbed';
|
||||||
case 'downloadFailed':
|
case 'downloadFailed':
|
||||||
return translate('DownloadFailed');
|
return 'Download Failed';
|
||||||
case 'downloadFolderImported':
|
case 'downloadFolderImported':
|
||||||
return translate('MovieImported');
|
return 'Movie Imported';
|
||||||
case 'movieFileDeleted':
|
case 'movieFileDeleted':
|
||||||
return translate('MovieFileDeleted');
|
return 'Movie File Deleted';
|
||||||
case 'movieFileRenamed':
|
case 'movieFileRenamed':
|
||||||
return translate('MovieFileRenamed');
|
return 'Movie File Renamed';
|
||||||
case 'downloadIgnored':
|
case 'downloadIgnored':
|
||||||
return translate('DownloadIgnored');
|
return 'Download Ignored';
|
||||||
default:
|
default:
|
||||||
return translate('Unknown');
|
return 'Unknown';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@ function HistoryDetailsModal(props) {
|
|||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
data,
|
data,
|
||||||
downloadId,
|
|
||||||
isMarkingAsFailed,
|
isMarkingAsFailed,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
@@ -60,7 +59,6 @@ function HistoryDetailsModal(props) {
|
|||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
sourceTitle={sourceTitle}
|
sourceTitle={sourceTitle}
|
||||||
data={data}
|
data={data}
|
||||||
downloadId={downloadId}
|
|
||||||
shortDateFormat={shortDateFormat}
|
shortDateFormat={shortDateFormat}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
/>
|
/>
|
||||||
@@ -75,7 +73,7 @@ function HistoryDetailsModal(props) {
|
|||||||
isSpinning={isMarkingAsFailed}
|
isSpinning={isMarkingAsFailed}
|
||||||
onPress={onMarkAsFailedPress}
|
onPress={onMarkAsFailedPress}
|
||||||
>
|
>
|
||||||
{translate('MarkAsFailed')}
|
Mark as Failed
|
||||||
</SpinnerButton>
|
</SpinnerButton>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +93,6 @@ HistoryDetailsModal.propTypes = {
|
|||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
downloadId: PropTypes.string,
|
|
||||||
isMarkingAsFailed: PropTypes.bool.isRequired,
|
isMarkingAsFailed: PropTypes.bool.isRequired,
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired,
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
@@ -12,9 +11,8 @@ import Table from 'Components/Table/Table';
|
|||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import TablePager from 'Components/Table/TablePager';
|
import TablePager from 'Components/Table/TablePager';
|
||||||
import { align, icons, kinds } from 'Helpers/Props';
|
import { align, icons } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import HistoryFilterModal from './HistoryFilterModal';
|
|
||||||
import HistoryRowConnector from './HistoryRowConnector';
|
import HistoryRowConnector from './HistoryRowConnector';
|
||||||
|
|
||||||
class History extends Component {
|
class History extends Component {
|
||||||
@@ -34,7 +32,6 @@ class History extends Component {
|
|||||||
columns,
|
columns,
|
||||||
selectedFilterKey,
|
selectedFilterKey,
|
||||||
filters,
|
filters,
|
||||||
customFilters,
|
|
||||||
totalRecords,
|
totalRecords,
|
||||||
onFilterSelect,
|
onFilterSelect,
|
||||||
onFirstPagePress,
|
onFirstPagePress,
|
||||||
@@ -72,8 +69,7 @@ class History extends Component {
|
|||||||
alignMenu={align.RIGHT}
|
alignMenu={align.RIGHT}
|
||||||
selectedFilterKey={selectedFilterKey}
|
selectedFilterKey={selectedFilterKey}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
customFilters={customFilters}
|
customFilters={[]}
|
||||||
filterModalConnectorComponent={HistoryFilterModal}
|
|
||||||
onFilterSelect={onFilterSelect}
|
onFilterSelect={onFilterSelect}
|
||||||
/>
|
/>
|
||||||
</PageToolbarSection>
|
</PageToolbarSection>
|
||||||
@@ -87,9 +83,9 @@ class History extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetchingAny && hasError &&
|
!isFetchingAny && hasError &&
|
||||||
<Alert kind={kinds.DANGER}>
|
<div>
|
||||||
{translate('HistoryLoadError')}
|
{translate('UnableToLoadHistory')}
|
||||||
</Alert>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -97,9 +93,9 @@ class History extends Component {
|
|||||||
// wait for the episodes to populate because they are never coming.
|
// wait for the episodes to populate because they are never coming.
|
||||||
|
|
||||||
isPopulated && !hasError && !items.length &&
|
isPopulated && !hasError && !items.length &&
|
||||||
<Alert kind={kinds.INFO}>
|
<div>
|
||||||
{translate('NoHistoryFound')}
|
{translate('NoHistory')}
|
||||||
</Alert>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -147,9 +143,8 @@ History.propTypes = {
|
|||||||
moviesError: PropTypes.object,
|
moviesError: PropTypes.object,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
selectedFilterKey: PropTypes.string.isRequired,
|
||||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
totalRecords: PropTypes.number,
|
totalRecords: PropTypes.number,
|
||||||
onFilterSelect: PropTypes.func.isRequired,
|
onFilterSelect: PropTypes.func.isRequired,
|
||||||
onFirstPagePress: PropTypes.func.isRequired
|
onFirstPagePress: PropTypes.func.isRequired
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { connect } from 'react-redux';
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import withCurrentPage from 'Components/withCurrentPage';
|
import withCurrentPage from 'Components/withCurrentPage';
|
||||||
import * as historyActions from 'Store/Actions/historyActions';
|
import * as historyActions from 'Store/Actions/historyActions';
|
||||||
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
|
|
||||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||||
import History from './History';
|
import History from './History';
|
||||||
|
|
||||||
@@ -12,13 +11,11 @@ function createMapStateToProps() {
|
|||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.history,
|
(state) => state.history,
|
||||||
(state) => state.movies,
|
(state) => state.movies,
|
||||||
createCustomFiltersSelector('history'),
|
(history, movies) => {
|
||||||
(history, movies, customFilters) => {
|
|
||||||
return {
|
return {
|
||||||
isMoviesFetching: movies.isFetching,
|
isMoviesFetching: movies.isFetching,
|
||||||
isMoviesPopulated: movies.isPopulated,
|
isMoviesPopulated: movies.isPopulated,
|
||||||
moviesError: movies.error,
|
moviesError: movies.error,
|
||||||
customFilters,
|
|
||||||
...history
|
...history
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'cell': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -3,10 +3,9 @@ import React from 'react';
|
|||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './HistoryEventTypeCell.css';
|
import styles from './HistoryEventTypeCell.css';
|
||||||
|
|
||||||
function getIconName(eventType, data) {
|
function getIconName(eventType) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'grabbed':
|
||||||
return icons.DOWNLOADING;
|
return icons.DOWNLOADING;
|
||||||
@@ -17,7 +16,7 @@ function getIconName(eventType, data) {
|
|||||||
case 'downloadFailed':
|
case 'downloadFailed':
|
||||||
return icons.DOWNLOADING;
|
return icons.DOWNLOADING;
|
||||||
case 'movieFileDeleted':
|
case 'movieFileDeleted':
|
||||||
return data.reason === 'MissingFromDisk' ? icons.FILE_MISSING : icons.DELETE;
|
return icons.DELETE;
|
||||||
case 'movieFileRenamed':
|
case 'movieFileRenamed':
|
||||||
return icons.ORGANIZE;
|
return icons.ORGANIZE;
|
||||||
case 'downloadIgnored':
|
case 'downloadIgnored':
|
||||||
@@ -39,26 +38,26 @@ function getIconKind(eventType) {
|
|||||||
function getTooltip(eventType, data) {
|
function getTooltip(eventType, data) {
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'grabbed':
|
case 'grabbed':
|
||||||
return translate('MovieGrabbedHistoryTooltip', { indexer: data.indexer, downloadClient: data.downloadClient });
|
return `Movie grabbed from ${data.indexer} and sent to ${data.downloadClient}`;
|
||||||
case 'movieFolderImported':
|
case 'movieFolderImported':
|
||||||
return translate('MovieFolderImportedTooltip');
|
return 'Movie imported from movie folder';
|
||||||
case 'downloadFolderImported':
|
case 'downloadFolderImported':
|
||||||
return translate('MovieImportedTooltip');
|
return 'Movie downloaded successfully and picked up from download client';
|
||||||
case 'downloadFailed':
|
case 'downloadFailed':
|
||||||
return translate('MovieDownloadFailedTooltip');
|
return 'Movie download failed';
|
||||||
case 'movieFileDeleted':
|
case 'movieFileDeleted':
|
||||||
return data.reason === 'MissingFromDisk' ? translate('MovieFileMissingTooltip') : translate('MovieFileDeletedTooltip');
|
return 'Movie file deleted';
|
||||||
case 'movieFileRenamed':
|
case 'movieFileRenamed':
|
||||||
return translate('MovieFileRenamedTooltip');
|
return 'Movie file renamed';
|
||||||
case 'downloadIgnored':
|
case 'downloadIgnored':
|
||||||
return translate('MovieDownloadIgnoredTooltip');
|
return 'Movie Download Ignored';
|
||||||
default:
|
default:
|
||||||
return translate('UnknownEventTooltip');
|
return 'Unknown event';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function HistoryEventTypeCell({ eventType, data }) {
|
function HistoryEventTypeCell({ eventType, data }) {
|
||||||
const iconName = getIconName(eventType, data);
|
const iconName = getIconName(eventType);
|
||||||
const iconKind = getIconKind(eventType);
|
const iconKind = getIconKind(eventType);
|
||||||
const tooltip = getTooltip(eventType, data);
|
const tooltip = getTooltip(eventType, data);
|
||||||
|
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import React, { useCallback } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import AppState from 'App/State/AppState';
|
|
||||||
import FilterModal from 'Components/Filter/FilterModal';
|
|
||||||
import { setHistoryFilter } from 'Store/Actions/historyActions';
|
|
||||||
|
|
||||||
function createHistorySelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.history.items,
|
|
||||||
(queueItems) => {
|
|
||||||
return queueItems;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilterBuilderPropsSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.history.filterBuilderProps,
|
|
||||||
(filterBuilderProps) => {
|
|
||||||
return filterBuilderProps;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HistoryFilterModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function HistoryFilterModal(props: HistoryFilterModalProps) {
|
|
||||||
const sectionItems = useSelector(createHistorySelector());
|
|
||||||
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
|
|
||||||
const customFilterType = 'history';
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const dispatchSetFilter = useCallback(
|
|
||||||
(payload: unknown) => {
|
|
||||||
dispatch(setHistoryFilter(payload));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FilterModal
|
|
||||||
// TODO: Don't spread all the props
|
|
||||||
{...props}
|
|
||||||
sectionItems={sectionItems}
|
|
||||||
filterBuilderProps={filterBuilderProps}
|
|
||||||
customFilterType={customFilterType}
|
|
||||||
dispatchSetFilter={dispatchSetFilter}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -10,12 +10,6 @@
|
|||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customFormatScore {
|
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 55px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.releaseGroup {
|
.releaseGroup {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'customFormatScore': string;
|
|
||||||
'details': string;
|
|
||||||
'downloadClient': string;
|
|
||||||
'indexer': string;
|
|
||||||
'releaseGroup': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -4,13 +4,11 @@ import IconButton from 'Components/Link/IconButton';
|
|||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
import { icons } from 'Helpers/Props';
|
||||||
import { icons, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import MovieFormats from 'Movie/MovieFormats';
|
import MovieFormats from 'Movie/MovieFormats';
|
||||||
import MovieLanguages from 'Movie/MovieLanguages';
|
import MovieLanguage from 'Movie/MovieLanguage';
|
||||||
import MovieQuality from 'Movie/MovieQuality';
|
import MovieQuality from 'Movie/MovieQuality';
|
||||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
|
||||||
import HistoryDetailsModal from './Details/HistoryDetailsModal';
|
import HistoryDetailsModal from './Details/HistoryDetailsModal';
|
||||||
import HistoryEventTypeCell from './HistoryEventTypeCell';
|
import HistoryEventTypeCell from './HistoryEventTypeCell';
|
||||||
import styles from './HistoryRow.css';
|
import styles from './HistoryRow.css';
|
||||||
@@ -57,14 +55,12 @@ class HistoryRow extends Component {
|
|||||||
movie,
|
movie,
|
||||||
quality,
|
quality,
|
||||||
customFormats,
|
customFormats,
|
||||||
customFormatScore,
|
|
||||||
languages,
|
languages,
|
||||||
qualityCutoffNotMet,
|
qualityCutoffNotMet,
|
||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
date,
|
date,
|
||||||
data,
|
data,
|
||||||
downloadId,
|
|
||||||
isMarkingAsFailed,
|
isMarkingAsFailed,
|
||||||
columns,
|
columns,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
@@ -99,7 +95,7 @@ class HistoryRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'movieMetadata.sortTitle') {
|
if (name === 'movies.sortTitle') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<MovieTitleLink
|
<MovieTitleLink
|
||||||
@@ -113,7 +109,7 @@ class HistoryRow extends Component {
|
|||||||
if (name === 'languages') {
|
if (name === 'languages') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<MovieLanguages
|
<MovieLanguage
|
||||||
languages={languages}
|
languages={languages}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
@@ -172,24 +168,6 @@ class HistoryRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'customFormatScore') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.customFormatScore}
|
|
||||||
>
|
|
||||||
<Tooltip
|
|
||||||
anchor={formatCustomFormatScore(
|
|
||||||
customFormatScore,
|
|
||||||
customFormats.length
|
|
||||||
)}
|
|
||||||
tooltip={<MovieFormats formats={customFormats} />}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'releaseGroup') {
|
if (name === 'releaseGroup') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell
|
<TableRowCell
|
||||||
@@ -217,12 +195,10 @@ class HistoryRow extends Component {
|
|||||||
key={name}
|
key={name}
|
||||||
className={styles.details}
|
className={styles.details}
|
||||||
>
|
>
|
||||||
<div className={styles.actionContents}>
|
<IconButton
|
||||||
<IconButton
|
name={icons.INFO}
|
||||||
name={icons.INFO}
|
onPress={this.onDetailsPress}
|
||||||
onPress={this.onDetailsPress}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -236,7 +212,6 @@ class HistoryRow extends Component {
|
|||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
sourceTitle={sourceTitle}
|
sourceTitle={sourceTitle}
|
||||||
data={data}
|
data={data}
|
||||||
downloadId={downloadId}
|
|
||||||
isMarkingAsFailed={isMarkingAsFailed}
|
isMarkingAsFailed={isMarkingAsFailed}
|
||||||
shortDateFormat={shortDateFormat}
|
shortDateFormat={shortDateFormat}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
@@ -254,14 +229,12 @@ HistoryRow.propTypes = {
|
|||||||
movie: PropTypes.object.isRequired,
|
movie: PropTypes.object.isRequired,
|
||||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
quality: PropTypes.object.isRequired,
|
quality: PropTypes.object.isRequired,
|
||||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
customFormatScore: PropTypes.number.isRequired,
|
|
||||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||||
|
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
date: PropTypes.string.isRequired,
|
date: PropTypes.string.isRequired,
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
downloadId: PropTypes.string,
|
|
||||||
isMarkingAsFailed: PropTypes.bool,
|
isMarkingAsFailed: PropTypes.bool,
|
||||||
markAsFailedError: PropTypes.object,
|
markAsFailedError: PropTypes.object,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
@@ -270,8 +243,4 @@ HistoryRow.propTypes = {
|
|||||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
onMarkAsFailedPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
HistoryRow.defaultProps = {
|
|
||||||
customFormats: []
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HistoryRow;
|
export default HistoryRow;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
.torrent {
|
.torrent {
|
||||||
composes: label from '~Components/Label.css';
|
composes: label from '~Components/Label.css';
|
||||||
|
|
||||||
border-color: var(--torrentColor);
|
border-color: $torrentColor;
|
||||||
background-color: var(--torrentColor);
|
background-color: $torrentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.usenet {
|
.usenet {
|
||||||
composes: label from '~Components/Label.css';
|
composes: label from '~Components/Label.css';
|
||||||
|
|
||||||
border-color: var(--usenetColor);
|
border-color: $usenetColor;
|
||||||
background-color: var(--usenetColor);
|
background-color: $usenetColor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'torrent': string;
|
|
||||||
'usenet': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||||
@@ -14,7 +12,7 @@ import Table from 'Components/Table/Table';
|
|||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import TablePager from 'Components/Table/TablePager';
|
import TablePager from 'Components/Table/TablePager';
|
||||||
import { align, icons, kinds } from 'Helpers/Props';
|
import { align, icons } from 'Helpers/Props';
|
||||||
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
||||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
@@ -22,10 +20,9 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
|||||||
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
||||||
import selectAll from 'Utilities/Table/selectAll';
|
import selectAll from 'Utilities/Table/selectAll';
|
||||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||||
import QueueFilterModal from './QueueFilterModal';
|
|
||||||
import QueueOptionsConnector from './QueueOptionsConnector';
|
import QueueOptionsConnector from './QueueOptionsConnector';
|
||||||
import QueueRowConnector from './QueueRowConnector';
|
import QueueRowConnector from './QueueRowConnector';
|
||||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
|
||||||
|
|
||||||
class Queue extends Component {
|
class Queue extends Component {
|
||||||
|
|
||||||
@@ -78,23 +75,13 @@ class Queue extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextState = {};
|
|
||||||
|
|
||||||
if (prevProps.items !== items) {
|
|
||||||
nextState.items = items;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedIds = this.getSelectedIds();
|
const selectedIds = this.getSelectedIds();
|
||||||
const isPendingSelected = _.some(this.props.items, (item) => {
|
const isPendingSelected = _.some(this.props.items, (item) => {
|
||||||
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
|
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isPendingSelected !== this.state.isPendingSelected) {
|
if (isPendingSelected !== this.state.isPendingSelected) {
|
||||||
nextState.isPendingSelected = isPendingSelected;
|
this.setState({ isPendingSelected });
|
||||||
}
|
|
||||||
|
|
||||||
if (!_.isEmpty(nextState)) {
|
|
||||||
this.setState(nextState);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,16 +142,11 @@ class Queue extends Component {
|
|||||||
isMoviesPopulated,
|
isMoviesPopulated,
|
||||||
moviesError,
|
moviesError,
|
||||||
columns,
|
columns,
|
||||||
selectedFilterKey,
|
|
||||||
filters,
|
|
||||||
customFilters,
|
|
||||||
count,
|
|
||||||
totalRecords,
|
totalRecords,
|
||||||
isGrabbing,
|
isGrabbing,
|
||||||
isRemoving,
|
isRemoving,
|
||||||
isRefreshMonitoredDownloadsExecuting,
|
isRefreshMonitoredDownloadsExecuting,
|
||||||
onRefreshPress,
|
onRefreshPress,
|
||||||
onFilterSelect,
|
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -219,7 +201,6 @@ class Queue extends Component {
|
|||||||
>
|
>
|
||||||
<TableOptionsModalWrapper
|
<TableOptionsModalWrapper
|
||||||
columns={columns}
|
columns={columns}
|
||||||
maxPageSize={200}
|
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
optionsComponent={QueueOptionsConnector}
|
optionsComponent={QueueOptionsConnector}
|
||||||
>
|
>
|
||||||
@@ -228,47 +209,31 @@ class Queue extends Component {
|
|||||||
iconName={icons.TABLE}
|
iconName={icons.TABLE}
|
||||||
/>
|
/>
|
||||||
</TableOptionsModalWrapper>
|
</TableOptionsModalWrapper>
|
||||||
|
|
||||||
<FilterMenu
|
|
||||||
alignMenu={align.RIGHT}
|
|
||||||
selectedFilterKey={selectedFilterKey}
|
|
||||||
filters={filters}
|
|
||||||
customFilters={customFilters}
|
|
||||||
filterModalConnectorComponent={QueueFilterModal}
|
|
||||||
onFilterSelect={onFilterSelect}
|
|
||||||
/>
|
|
||||||
</PageToolbarSection>
|
</PageToolbarSection>
|
||||||
</PageToolbar>
|
</PageToolbar>
|
||||||
|
|
||||||
<PageContentBody>
|
<PageContentBody>
|
||||||
{
|
{
|
||||||
isRefreshing && !isAllPopulated ?
|
isRefreshing && !isAllPopulated &&
|
||||||
<LoadingIndicator /> :
|
<LoadingIndicator />
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!isRefreshing && hasError ?
|
!isRefreshing && hasError &&
|
||||||
<Alert kind={kinds.DANGER}>
|
<div>
|
||||||
{translate('QueueLoadError')}
|
{translate('FailedToLoadQueue')}
|
||||||
</Alert> :
|
</div>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isAllPopulated && !hasError && !items.length ?
|
isAllPopulated && !hasError && !items.length &&
|
||||||
<Alert kind={kinds.INFO}>
|
<div>
|
||||||
{
|
{translate('QueueIsEmpty')}
|
||||||
selectedFilterKey !== 'all' && count > 0 ?
|
</div>
|
||||||
translate('QueueFilterHasNoItems') :
|
|
||||||
translate('QueueIsEmpty')
|
|
||||||
}
|
|
||||||
</Alert> :
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isAllPopulated && !hasError && !!items.length ?
|
isAllPopulated && !hasError && !!items.length &&
|
||||||
<div>
|
<div>
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -303,21 +268,13 @@ class Queue extends Component {
|
|||||||
isFetching={isRefreshing}
|
isFetching={isRefreshing}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
</div> :
|
</div>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</PageContentBody>
|
</PageContentBody>
|
||||||
|
|
||||||
<RemoveQueueItemModal
|
<RemoveQueueItemsModal
|
||||||
isOpen={isConfirmRemoveModalOpen}
|
isOpen={isConfirmRemoveModalOpen}
|
||||||
selectedCount={selectedCount}
|
selectedCount={selectedCount}
|
||||||
canChangeCategory={isConfirmRemoveModalOpen && (
|
|
||||||
selectedIds.every((id) => {
|
|
||||||
const item = items.find((i) => i.id === id);
|
|
||||||
|
|
||||||
return !!(item && item.downloadClientHasPostImportCategory);
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
canIgnore={isConfirmRemoveModalOpen && (
|
canIgnore={isConfirmRemoveModalOpen && (
|
||||||
selectedIds.every((id) => {
|
selectedIds.every((id) => {
|
||||||
const item = items.find((i) => i.id === id);
|
const item = items.find((i) => i.id === id);
|
||||||
@@ -325,7 +282,7 @@ class Queue extends Component {
|
|||||||
return !!(item && item.movieId);
|
return !!(item && item.movieId);
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
pending={isConfirmRemoveModalOpen && (
|
allPending={isConfirmRemoveModalOpen && (
|
||||||
selectedIds.every((id) => {
|
selectedIds.every((id) => {
|
||||||
const item = items.find((i) => i.id === id);
|
const item = items.find((i) => i.id === id);
|
||||||
|
|
||||||
@@ -353,22 +310,13 @@ Queue.propTypes = {
|
|||||||
moviesError: PropTypes.object,
|
moviesError: PropTypes.object,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
|
||||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
count: PropTypes.number.isRequired,
|
|
||||||
totalRecords: PropTypes.number,
|
totalRecords: PropTypes.number,
|
||||||
isGrabbing: PropTypes.bool.isRequired,
|
isGrabbing: PropTypes.bool.isRequired,
|
||||||
isRemoving: PropTypes.bool.isRequired,
|
isRemoving: PropTypes.bool.isRequired,
|
||||||
isRefreshMonitoredDownloadsExecuting: PropTypes.bool.isRequired,
|
isRefreshMonitoredDownloadsExecuting: PropTypes.bool.isRequired,
|
||||||
onRefreshPress: PropTypes.func.isRequired,
|
onRefreshPress: PropTypes.func.isRequired,
|
||||||
onGrabSelectedPress: PropTypes.func.isRequired,
|
onGrabSelectedPress: PropTypes.func.isRequired,
|
||||||
onRemoveSelectedPress: PropTypes.func.isRequired,
|
onRemoveSelectedPress: PropTypes.func.isRequired
|
||||||
onFilterSelect: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
Queue.defaultProps = {
|
|
||||||
count: 0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Queue;
|
export default Queue;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import * as commandNames from 'Commands/commandNames';
|
|||||||
import withCurrentPage from 'Components/withCurrentPage';
|
import withCurrentPage from 'Components/withCurrentPage';
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
import * as queueActions from 'Store/Actions/queueActions';
|
import * as queueActions from 'Store/Actions/queueActions';
|
||||||
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
|
|
||||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||||
import Queue from './Queue';
|
import Queue from './Queue';
|
||||||
@@ -16,16 +15,12 @@ function createMapStateToProps() {
|
|||||||
(state) => state.movies,
|
(state) => state.movies,
|
||||||
(state) => state.queue.options,
|
(state) => state.queue.options,
|
||||||
(state) => state.queue.paged,
|
(state) => state.queue.paged,
|
||||||
(state) => state.queue.status.item,
|
|
||||||
createCustomFiltersSelector('queue'),
|
|
||||||
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
|
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
|
||||||
(movies, options, queue, status, customFilters, isRefreshMonitoredDownloadsExecuting) => {
|
(movies, options, queue, isRefreshMonitoredDownloadsExecuting) => {
|
||||||
return {
|
return {
|
||||||
count: options.includeUnknownMovieItems ? status.totalCount : status.count,
|
|
||||||
isMoviesFetching: movies.isFetching,
|
isMoviesFetching: movies.isFetching,
|
||||||
isMoviesPopulated: movies.isPopulated,
|
isMoviesPopulated: movies.isPopulated,
|
||||||
moviesError: movies.error,
|
moviesError: movies.error,
|
||||||
customFilters,
|
|
||||||
isRefreshMonitoredDownloadsExecuting,
|
isRefreshMonitoredDownloadsExecuting,
|
||||||
...options,
|
...options,
|
||||||
...queue
|
...queue
|
||||||
@@ -111,10 +106,6 @@ class QueueConnector extends Component {
|
|||||||
this.props.setQueueSort({ sortKey });
|
this.props.setQueueSort({ sortKey });
|
||||||
};
|
};
|
||||||
|
|
||||||
onFilterSelect = (selectedFilterKey) => {
|
|
||||||
this.props.setQueueFilter({ selectedFilterKey });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTableOptionChange = (payload) => {
|
onTableOptionChange = (payload) => {
|
||||||
this.props.setQueueTableOption(payload);
|
this.props.setQueueTableOption(payload);
|
||||||
|
|
||||||
@@ -149,7 +140,6 @@ class QueueConnector extends Component {
|
|||||||
onLastPagePress={this.onLastPagePress}
|
onLastPagePress={this.onLastPagePress}
|
||||||
onPageSelect={this.onPageSelect}
|
onPageSelect={this.onPageSelect}
|
||||||
onSortPress={this.onSortPress}
|
onSortPress={this.onSortPress}
|
||||||
onFilterSelect={this.onFilterSelect}
|
|
||||||
onTableOptionChange={this.onTableOptionChange}
|
onTableOptionChange={this.onTableOptionChange}
|
||||||
onRefreshPress={this.onRefreshPress}
|
onRefreshPress={this.onRefreshPress}
|
||||||
onGrabSelectedPress={this.onGrabSelectedPress}
|
onGrabSelectedPress={this.onGrabSelectedPress}
|
||||||
@@ -172,7 +162,6 @@ QueueConnector.propTypes = {
|
|||||||
gotoQueueLastPage: PropTypes.func.isRequired,
|
gotoQueueLastPage: PropTypes.func.isRequired,
|
||||||
gotoQueuePage: PropTypes.func.isRequired,
|
gotoQueuePage: PropTypes.func.isRequired,
|
||||||
setQueueSort: PropTypes.func.isRequired,
|
setQueueSort: PropTypes.func.isRequired,
|
||||||
setQueueFilter: PropTypes.func.isRequired,
|
|
||||||
setQueueTableOption: PropTypes.func.isRequired,
|
setQueueTableOption: PropTypes.func.isRequired,
|
||||||
clearQueue: PropTypes.func.isRequired,
|
clearQueue: PropTypes.func.isRequired,
|
||||||
grabQueueItems: PropTypes.func.isRequired,
|
grabQueueItems: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
.progressBarContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'progressBarContainer': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,71 +1,116 @@
|
|||||||
|
import moment from 'moment';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import { icons, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import QueueStatus from './QueueStatus';
|
|
||||||
import styles from './QueueDetails.css';
|
|
||||||
|
|
||||||
function QueueDetails(props) {
|
function QueueDetails(props) {
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
size,
|
size,
|
||||||
sizeleft,
|
sizeleft,
|
||||||
|
estimatedCompletionTime,
|
||||||
status,
|
status,
|
||||||
trackedDownloadState,
|
trackedDownloadState,
|
||||||
trackedDownloadStatus,
|
trackedDownloadStatus,
|
||||||
statusMessages,
|
|
||||||
errorMessage,
|
errorMessage,
|
||||||
progressBar
|
progressBar
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
||||||
const isDownloading = status === 'downloading';
|
|
||||||
const isPaused = status === 'paused';
|
|
||||||
const hasWarning = trackedDownloadStatus === 'warning';
|
|
||||||
const hasError = trackedDownloadStatus === 'error';
|
|
||||||
|
|
||||||
if (
|
|
||||||
(isDownloading || isPaused) &&
|
|
||||||
!hasWarning &&
|
|
||||||
!hasError
|
|
||||||
) {
|
|
||||||
const state = isPaused ? translate('Paused') : translate('Downloading');
|
|
||||||
|
|
||||||
if (progress < 5) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOADING}
|
|
||||||
title={`${state} - ${progress.toFixed(1)}% ${title}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (status === 'pending') {
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Icon
|
||||||
className={styles.progressBarContainer}
|
name={icons.PENDING}
|
||||||
anchor={progressBar}
|
title={translate('ReleaseWillBeProcessedInterp', [moment(estimatedCompletionTime).fromNow()])}
|
||||||
title={`${state} - ${progress.toFixed(1)}%`}
|
|
||||||
body={
|
|
||||||
<div>{title}</div>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.LEFT}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
if (status === 'completed') {
|
||||||
<QueueStatus
|
if (errorMessage) {
|
||||||
sourceTitle={title}
|
return (
|
||||||
status={status}
|
<Icon
|
||||||
trackedDownloadStatus={trackedDownloadStatus}
|
name={icons.DOWNLOAD}
|
||||||
trackedDownloadState={trackedDownloadState}
|
kind={kinds.DANGER}
|
||||||
statusMessages={statusMessages}
|
title={translate('ImportFailedInterp', [errorMessage])}
|
||||||
errorMessage={errorMessage}
|
/>
|
||||||
position={tooltipPositions.LEFT}
|
);
|
||||||
/>
|
}
|
||||||
);
|
|
||||||
|
if (trackedDownloadStatus === 'warning') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOAD}
|
||||||
|
kind={kinds.WARNING}
|
||||||
|
title={translate('UnableToImportCheckLogs')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importPending') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOAD}
|
||||||
|
kind={kinds.PURPLE}
|
||||||
|
title={`${translate('Downloaded')} - ${translate('WaitingToImport')}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importing') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOAD}
|
||||||
|
kind={kinds.PURPLE}
|
||||||
|
title={`${translate('Downloaded')} - ${translate('Importing')}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessage) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOADING}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title={translate('DownloadFailedInterp', [errorMessage])}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'failed') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOADING}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title={translate('DownloadFailedCheckDownloadClientForMoreDetails')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'warning') {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOADING}
|
||||||
|
kind={kinds.WARNING}
|
||||||
|
title={translate('DownloadWarningCheckDownloadClientForMoreDetails')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress < 5) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
name={icons.DOWNLOADING}
|
||||||
|
title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return progressBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueDetails.propTypes = {
|
QueueDetails.propTypes = {
|
||||||
@@ -76,14 +121,8 @@ QueueDetails.propTypes = {
|
|||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
trackedDownloadState: PropTypes.string.isRequired,
|
trackedDownloadState: PropTypes.string.isRequired,
|
||||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
trackedDownloadStatus: PropTypes.string.isRequired,
|
||||||
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
errorMessage: PropTypes.string,
|
errorMessage: PropTypes.string,
|
||||||
progressBar: PropTypes.node.isRequired
|
progressBar: PropTypes.node.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
QueueDetails.defaultProps = {
|
|
||||||
trackedDownloadStatus: 'ok',
|
|
||||||
trackedDownloadState: 'downloading'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QueueDetails;
|
export default QueueDetails;
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import React, { useCallback } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import AppState from 'App/State/AppState';
|
|
||||||
import FilterModal from 'Components/Filter/FilterModal';
|
|
||||||
import { setQueueFilter } from 'Store/Actions/queueActions';
|
|
||||||
|
|
||||||
function createQueueSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.queue.paged.items,
|
|
||||||
(queueItems) => {
|
|
||||||
return queueItems;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilterBuilderPropsSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.queue.paged.filterBuilderProps,
|
|
||||||
(filterBuilderProps) => {
|
|
||||||
return filterBuilderProps;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface QueueFilterModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function QueueFilterModal(props: QueueFilterModalProps) {
|
|
||||||
const sectionItems = useSelector(createQueueSelector());
|
|
||||||
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
|
|
||||||
const customFilterType = 'queue';
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const dispatchSetFilter = useCallback(
|
|
||||||
(payload: unknown) => {
|
|
||||||
dispatch(setQueueFilter(payload));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FilterModal
|
|
||||||
// TODO: Don't spread all the props
|
|
||||||
{...props}
|
|
||||||
sectionItems={sectionItems}
|
|
||||||
filterBuilderProps={filterBuilderProps}
|
|
||||||
customFilterType={customFilterType}
|
|
||||||
dispatchSetFilter={dispatchSetFilter}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -61,7 +61,7 @@ class QueueOptions extends Component {
|
|||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="includeUnknownMovieItems"
|
name="includeUnknownMovieItems"
|
||||||
value={includeUnknownMovieItems}
|
value={includeUnknownMovieItems}
|
||||||
helpText={translate('ShowUnknownMovieItemsHelpText')}
|
helpText={translate('IncludeUnknownMovieItemsHelpText')}
|
||||||
onChange={this.onOptionChange}
|
onChange={this.onOptionChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|||||||
@@ -16,15 +16,8 @@
|
|||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customFormatScore {
|
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 55px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 70px;
|
width: 70px;
|
||||||
text-align: right;
|
|
||||||
}
|
}
|
||||||
|
|||||||
11
frontend/src/Activity/Queue/QueueRow.css.d.ts
vendored
@@ -1,11 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'actions': string;
|
|
||||||
'customFormatScore': string;
|
|
||||||
'progress': string;
|
|
||||||
'protocol': string;
|
|
||||||
'quality': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -4,19 +4,17 @@ import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
|
|||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||||
import ProgressBar from 'Components/ProgressBar';
|
import ProgressBar from 'Components/ProgressBar';
|
||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
// import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||||
import MovieFormats from 'Movie/MovieFormats';
|
import MovieFormats from 'Movie/MovieFormats';
|
||||||
import MovieLanguages from 'Movie/MovieLanguages';
|
import MovieLanguage from 'Movie/MovieLanguage';
|
||||||
import MovieQuality from 'Movie/MovieQuality';
|
import MovieQuality from 'Movie/MovieQuality';
|
||||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import QueueStatusCell from './QueueStatusCell';
|
import QueueStatusCell from './QueueStatusCell';
|
||||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||||
@@ -44,14 +42,14 @@ class QueueRow extends Component {
|
|||||||
this.setState({ isRemoveQueueItemModalOpen: true });
|
this.setState({ isRemoveQueueItemModalOpen: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
onRemoveQueueItemModalConfirmed = (blocklist, skipRedownload) => {
|
onRemoveQueueItemModalConfirmed = (blocklist) => {
|
||||||
const {
|
const {
|
||||||
onRemoveQueueItemPress,
|
onRemoveQueueItemPress,
|
||||||
onQueueRowModalOpenOrClose
|
onQueueRowModalOpenOrClose
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
onQueueRowModalOpenOrClose(false);
|
onQueueRowModalOpenOrClose(false);
|
||||||
onRemoveQueueItemPress(blocklist, skipRedownload);
|
onRemoveQueueItemPress(blocklist);
|
||||||
|
|
||||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||||
};
|
};
|
||||||
@@ -90,15 +88,12 @@ class QueueRow extends Component {
|
|||||||
movie,
|
movie,
|
||||||
quality,
|
quality,
|
||||||
customFormats,
|
customFormats,
|
||||||
customFormatScore,
|
|
||||||
languages,
|
languages,
|
||||||
protocol,
|
protocol,
|
||||||
indexer,
|
indexer,
|
||||||
outputPath,
|
outputPath,
|
||||||
downloadClient,
|
downloadClient,
|
||||||
downloadClientHasPostImportCategory,
|
|
||||||
estimatedCompletionTime,
|
estimatedCompletionTime,
|
||||||
added,
|
|
||||||
timeleft,
|
timeleft,
|
||||||
size,
|
size,
|
||||||
sizeleft,
|
sizeleft,
|
||||||
@@ -133,7 +128,6 @@ class QueueRow extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
columns.map((column) => {
|
columns.map((column) => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
isVisible
|
isVisible
|
||||||
@@ -175,7 +169,7 @@ class QueueRow extends Component {
|
|||||||
if (name === 'languages') {
|
if (name === 'languages') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<MovieLanguages
|
<MovieLanguage
|
||||||
languages={languages}
|
languages={languages}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
@@ -206,24 +200,6 @@ class QueueRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'customFormatScore') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.customFormatScore}
|
|
||||||
>
|
|
||||||
<Tooltip
|
|
||||||
anchor={formatCustomFormatScore(
|
|
||||||
customFormatScore,
|
|
||||||
customFormats.length
|
|
||||||
)}
|
|
||||||
tooltip={<MovieFormats formats={customFormats} />}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'protocol') {
|
if (name === 'protocol') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
@@ -258,16 +234,6 @@ class QueueRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'year') {
|
|
||||||
return (
|
|
||||||
<TableRowCell key={name}>
|
|
||||||
{
|
|
||||||
movie ? movie.year : ''
|
|
||||||
}
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'title') {
|
if (name === 'title') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
@@ -317,15 +283,6 @@ class QueueRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'added') {
|
|
||||||
return (
|
|
||||||
<RelativeDateCellConnector
|
|
||||||
key={name}
|
|
||||||
date={added}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'actions') {
|
if (name === 'actions') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell
|
<TableRowCell
|
||||||
@@ -374,7 +331,6 @@ class QueueRow extends Component {
|
|||||||
<RemoveQueueItemModal
|
<RemoveQueueItemModal
|
||||||
isOpen={isRemoveQueueItemModalOpen}
|
isOpen={isRemoveQueueItemModalOpen}
|
||||||
sourceTitle={title}
|
sourceTitle={title}
|
||||||
canChangeCategory={!!downloadClientHasPostImportCategory}
|
|
||||||
canIgnore={!!movie}
|
canIgnore={!!movie}
|
||||||
isPending={isPending}
|
isPending={isPending}
|
||||||
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
||||||
@@ -398,18 +354,14 @@ QueueRow.propTypes = {
|
|||||||
movie: PropTypes.object,
|
movie: PropTypes.object,
|
||||||
quality: PropTypes.object.isRequired,
|
quality: PropTypes.object.isRequired,
|
||||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
customFormats: PropTypes.arrayOf(PropTypes.object),
|
||||||
customFormatScore: PropTypes.number.isRequired,
|
|
||||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
protocol: PropTypes.string.isRequired,
|
protocol: PropTypes.string.isRequired,
|
||||||
indexer: PropTypes.string,
|
indexer: PropTypes.string,
|
||||||
outputPath: PropTypes.string,
|
outputPath: PropTypes.string,
|
||||||
downloadClient: PropTypes.string,
|
downloadClient: PropTypes.string,
|
||||||
downloadClientHasPostImportCategory: PropTypes.bool,
|
|
||||||
estimatedCompletionTime: PropTypes.string,
|
estimatedCompletionTime: PropTypes.string,
|
||||||
added: PropTypes.string,
|
|
||||||
timeleft: PropTypes.string,
|
timeleft: PropTypes.string,
|
||||||
size: PropTypes.number,
|
size: PropTypes.number,
|
||||||
year: PropTypes.number,
|
|
||||||
sizeleft: PropTypes.number,
|
sizeleft: PropTypes.number,
|
||||||
showRelativeDates: PropTypes.bool.isRequired,
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
@@ -426,7 +378,6 @@ QueueRow.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
QueueRow.defaultProps = {
|
QueueRow.defaultProps = {
|
||||||
customFormats: [],
|
|
||||||
isGrabbing: false,
|
isGrabbing: false,
|
||||||
isRemoving: false
|
isRemoving: false
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
.noMessages {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'noMessages': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './QueueStatus.css';
|
|
||||||
|
|
||||||
function getDetailedPopoverBody(statusMessages) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
statusMessages.map(({ title, messages }) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={title}
|
|
||||||
className={messages.length ? undefined: styles.noMessages}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
messages.map((message) => {
|
|
||||||
return (
|
|
||||||
<li key={message}>
|
|
||||||
{message}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function QueueStatus(props) {
|
|
||||||
const {
|
|
||||||
sourceTitle,
|
|
||||||
status,
|
|
||||||
trackedDownloadStatus,
|
|
||||||
trackedDownloadState,
|
|
||||||
statusMessages,
|
|
||||||
errorMessage,
|
|
||||||
position,
|
|
||||||
canFlip
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const hasWarning = trackedDownloadStatus === 'warning';
|
|
||||||
const hasError = trackedDownloadStatus === 'error';
|
|
||||||
|
|
||||||
// status === 'downloading'
|
|
||||||
let iconName = icons.DOWNLOADING;
|
|
||||||
let iconKind = kinds.DEFAULT;
|
|
||||||
let title = translate('Downloading');
|
|
||||||
|
|
||||||
if (status === 'paused') {
|
|
||||||
iconName = icons.PAUSED;
|
|
||||||
title = translate('Paused');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'queued') {
|
|
||||||
iconName = icons.QUEUED;
|
|
||||||
title = translate('Queued');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'completed') {
|
|
||||||
iconName = icons.DOWNLOADED;
|
|
||||||
title = translate('Downloaded');
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importBlocked') {
|
|
||||||
title += ` - ${translate('UnableToImportAutomatically')}`;
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importPending') {
|
|
||||||
title += ` - ${translate('WaitingToImport')}`;
|
|
||||||
iconKind = kinds.PURPLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importing') {
|
|
||||||
title += ` - ${translate('Importing')}`;
|
|
||||||
iconKind = kinds.PURPLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'failedPending') {
|
|
||||||
title += ` - ${translate('WaitingToProcess')}`;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasWarning) {
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'delay') {
|
|
||||||
iconName = icons.PENDING;
|
|
||||||
title = translate('Pending');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'downloadClientUnavailable') {
|
|
||||||
iconName = icons.PENDING;
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
title = translate('PendingDownloadClientUnavailable');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'failed') {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('DownloadFailed');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'warning') {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
|
|
||||||
title = translate('DownloadWarning', { warningMessage });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasError) {
|
|
||||||
if (status === 'completed') {
|
|
||||||
iconName = icons.DOWNLOAD;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('ImportFailed', { sourceTitle });
|
|
||||||
} else {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('DownloadFailed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Icon
|
|
||||||
name={iconName}
|
|
||||||
kind={iconKind}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
title={title}
|
|
||||||
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
|
|
||||||
position={position}
|
|
||||||
canFlip={canFlip}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
QueueStatus.propTypes = {
|
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
|
||||||
status: PropTypes.string.isRequired,
|
|
||||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
|
||||||
trackedDownloadState: PropTypes.string.isRequired,
|
|
||||||
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
errorMessage: PropTypes.string,
|
|
||||||
position: PropTypes.oneOf(tooltipPositions.all).isRequired,
|
|
||||||
canFlip: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
QueueStatus.defaultProps = {
|
|
||||||
trackedDownloadStatus: 'ok',
|
|
||||||
trackedDownloadState: 'downloading',
|
|
||||||
canFlip: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QueueStatus;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'status': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,10 +1,39 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import { tooltipPositions } from 'Helpers/Props';
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
import QueueStatus from './QueueStatus';
|
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './QueueStatusCell.css';
|
import styles from './QueueStatusCell.css';
|
||||||
|
|
||||||
|
function getDetailedPopoverBody(statusMessages) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
statusMessages.map(({ title, messages }) => {
|
||||||
|
return (
|
||||||
|
<div key={title}>
|
||||||
|
{title}
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
messages.map((message) => {
|
||||||
|
return (
|
||||||
|
<li key={message}>
|
||||||
|
{message}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function QueueStatusCell(props) {
|
function QueueStatusCell(props) {
|
||||||
const {
|
const {
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
@@ -15,16 +44,97 @@ function QueueStatusCell(props) {
|
|||||||
errorMessage
|
errorMessage
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const hasWarning = trackedDownloadStatus === 'warning';
|
||||||
|
const hasError = trackedDownloadStatus === 'error';
|
||||||
|
|
||||||
|
// status === 'downloading'
|
||||||
|
let iconName = icons.DOWNLOADING;
|
||||||
|
let iconKind = kinds.DEFAULT;
|
||||||
|
let title = translate('Downloading');
|
||||||
|
|
||||||
|
if (status === 'paused') {
|
||||||
|
iconName = icons.PAUSED;
|
||||||
|
title = translate('Paused');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'queued') {
|
||||||
|
iconName = icons.QUEUED;
|
||||||
|
title = translate('Queued');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'completed') {
|
||||||
|
iconName = icons.DOWNLOADED;
|
||||||
|
title = translate('Downloaded');
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importPending') {
|
||||||
|
title += ` - ${translate('WaitingToImport')}`;
|
||||||
|
iconKind = kinds.PURPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importing') {
|
||||||
|
title += ` - ${translate('Importing')}`;
|
||||||
|
iconKind = kinds.PURPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'failedPending') {
|
||||||
|
title += ` - ${translate('WaitingToProcess')}`;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasWarning) {
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'delay') {
|
||||||
|
iconName = icons.PENDING;
|
||||||
|
title = translate('Pending');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'DownloadClientUnavailable') {
|
||||||
|
iconName = icons.PENDING;
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
title = `${translate('Pending')} - ${translate('DownloadClientUnavailable')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'failed') {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('DownloadFailed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'warning') {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
|
||||||
|
title = translate('DownloadWarning', [warningMessage]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
if (status === 'completed') {
|
||||||
|
iconName = icons.DOWNLOAD;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('ImportFailed', [sourceTitle]);
|
||||||
|
} else {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('DownloadFailed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowCell className={styles.status}>
|
<TableRowCell className={styles.status}>
|
||||||
<QueueStatus
|
<Popover
|
||||||
sourceTitle={sourceTitle}
|
anchor={
|
||||||
status={status}
|
<Icon
|
||||||
trackedDownloadStatus={trackedDownloadStatus}
|
name={iconName}
|
||||||
trackedDownloadState={trackedDownloadState}
|
kind={iconKind}
|
||||||
statusMessages={statusMessages}
|
/>
|
||||||
errorMessage={errorMessage}
|
}
|
||||||
|
title={title}
|
||||||
|
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
|
||||||
position={tooltipPositions.RIGHT}
|
position={tooltipPositions.RIGHT}
|
||||||
|
canFlip={false}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
@@ -40,8 +150,8 @@ QueueStatusCell.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
QueueStatusCell.defaultProps = {
|
QueueStatusCell.defaultProps = {
|
||||||
trackedDownloadStatus: 'ok',
|
trackedDownloadStatus: translate('Ok'),
|
||||||
trackedDownloadState: 'downloading'
|
trackedDownloadState: translate('Downloading')
|
||||||
};
|
};
|
||||||
|
|
||||||
export default QueueStatusCell;
|
export default QueueStatusCell;
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'message': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
150
frontend/src/Activity/Queue/RemoveQueueItemModal.js
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
|
class RemoveQueueItemModal extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
remove: true,
|
||||||
|
blocklist: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
resetState = function() {
|
||||||
|
this.setState({
|
||||||
|
remove: true,
|
||||||
|
blocklist: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onRemoveChange = ({ value }) => {
|
||||||
|
this.setState({ remove: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onBlocklistChange = ({ value }) => {
|
||||||
|
this.setState({ blocklist: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onRemoveConfirmed = () => {
|
||||||
|
const state = this.state;
|
||||||
|
|
||||||
|
this.resetState();
|
||||||
|
this.props.onRemovePress(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
onModalClose = () => {
|
||||||
|
this.resetState();
|
||||||
|
this.props.onModalClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
sourceTitle,
|
||||||
|
canIgnore,
|
||||||
|
isPending
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { remove, blocklist } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
size={sizes.MEDIUM}
|
||||||
|
onModalClose={this.onModalClose}
|
||||||
|
>
|
||||||
|
<ModalContent
|
||||||
|
onModalClose={this.onModalClose}
|
||||||
|
>
|
||||||
|
<ModalHeader>
|
||||||
|
{translate('Remove')} - {sourceTitle}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<div>
|
||||||
|
{translate('RemoveFromQueueText', [sourceTitle])}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
isPending ?
|
||||||
|
null :
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="remove"
|
||||||
|
value={remove}
|
||||||
|
helpTextWarning={translate('RemoveHelpTextWarning')}
|
||||||
|
isDisabled={!canIgnore}
|
||||||
|
onChange={this.onRemoveChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
}
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('BlocklistRelease')}</FormLabel>
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="blocklist"
|
||||||
|
value={blocklist}
|
||||||
|
helpText={translate('BlocklistHelpText')}
|
||||||
|
onChange={this.onBlocklistChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={this.onModalClose}>
|
||||||
|
{translate('Close')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
onPress={this.onRemoveConfirmed}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveQueueItemModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
|
canIgnore: PropTypes.bool.isRequired,
|
||||||
|
isPending: PropTypes.bool.isRequired,
|
||||||
|
onRemovePress: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemoveQueueItemModal;
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
import React, { useCallback, useMemo, useState } from 'react';
|
|
||||||
import FormGroup from 'Components/Form/FormGroup';
|
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
|
||||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './RemoveQueueItemModal.css';
|
|
||||||
|
|
||||||
interface RemovePressProps {
|
|
||||||
remove: boolean;
|
|
||||||
changeCategory: boolean;
|
|
||||||
blocklist: boolean;
|
|
||||||
skipRedownload: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RemoveQueueItemModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
sourceTitle: string;
|
|
||||||
canChangeCategory: boolean;
|
|
||||||
canIgnore: boolean;
|
|
||||||
isPending: boolean;
|
|
||||||
selectedCount?: number;
|
|
||||||
onRemovePress(props: RemovePressProps): void;
|
|
||||||
onModalClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
type RemovalMethod = 'removeFromClient' | 'changeCategory' | 'ignore';
|
|
||||||
type BlocklistMethod =
|
|
||||||
| 'doNotBlocklist'
|
|
||||||
| 'blocklistAndSearch'
|
|
||||||
| 'blocklistOnly';
|
|
||||||
|
|
||||||
function RemoveQueueItemModal(props: RemoveQueueItemModalProps) {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
sourceTitle,
|
|
||||||
canIgnore,
|
|
||||||
canChangeCategory,
|
|
||||||
isPending,
|
|
||||||
selectedCount,
|
|
||||||
onRemovePress,
|
|
||||||
onModalClose,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const multipleSelected = selectedCount && selectedCount > 1;
|
|
||||||
|
|
||||||
const [removalMethod, setRemovalMethod] =
|
|
||||||
useState<RemovalMethod>('removeFromClient');
|
|
||||||
const [blocklistMethod, setBlocklistMethod] =
|
|
||||||
useState<BlocklistMethod>('doNotBlocklist');
|
|
||||||
|
|
||||||
const { title, message } = useMemo(() => {
|
|
||||||
if (!selectedCount) {
|
|
||||||
return {
|
|
||||||
title: translate('RemoveQueueItem', { sourceTitle }),
|
|
||||||
message: translate('RemoveQueueItemConfirmation', { sourceTitle }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedCount === 1) {
|
|
||||||
return {
|
|
||||||
title: translate('RemoveSelectedItem'),
|
|
||||||
message: translate('RemoveSelectedItemQueueMessageText'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: translate('RemoveSelectedItems'),
|
|
||||||
message: translate('RemoveSelectedItemsQueueMessageText', {
|
|
||||||
selectedCount,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}, [sourceTitle, selectedCount]);
|
|
||||||
|
|
||||||
const removalMethodOptions = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: 'removeFromClient',
|
|
||||||
value: translate('RemoveFromDownloadClient'),
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('RemoveMultipleFromDownloadClientHint')
|
|
||||||
: translate('RemoveFromDownloadClientHint'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'changeCategory',
|
|
||||||
value: translate('ChangeCategory'),
|
|
||||||
isDisabled: !canChangeCategory,
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('ChangeCategoryMultipleHint')
|
|
||||||
: translate('ChangeCategoryHint'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'ignore',
|
|
||||||
value: multipleSelected
|
|
||||||
? translate('IgnoreDownloads')
|
|
||||||
: translate('IgnoreDownload'),
|
|
||||||
isDisabled: !canIgnore,
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('IgnoreDownloadsHint')
|
|
||||||
: translate('IgnoreDownloadHint'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [canChangeCategory, canIgnore, multipleSelected]);
|
|
||||||
|
|
||||||
const blocklistMethodOptions = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: 'doNotBlocklist',
|
|
||||||
value: translate('DoNotBlocklist'),
|
|
||||||
hint: translate('DoNotBlocklistHint'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'blocklistAndSearch',
|
|
||||||
value: translate('BlocklistAndSearch'),
|
|
||||||
isDisabled: isPending,
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('BlocklistAndSearchMultipleHint')
|
|
||||||
: translate('BlocklistAndSearchHint'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'blocklistOnly',
|
|
||||||
value: translate('BlocklistOnly'),
|
|
||||||
hint: multipleSelected
|
|
||||||
? translate('BlocklistMultipleOnlyHint')
|
|
||||||
: translate('BlocklistOnlyHint'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [isPending, multipleSelected]);
|
|
||||||
|
|
||||||
const handleRemovalMethodChange = useCallback(
|
|
||||||
({ value }: { value: RemovalMethod }) => {
|
|
||||||
setRemovalMethod(value);
|
|
||||||
},
|
|
||||||
[setRemovalMethod]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleBlocklistMethodChange = useCallback(
|
|
||||||
({ value }: { value: BlocklistMethod }) => {
|
|
||||||
setBlocklistMethod(value);
|
|
||||||
},
|
|
||||||
[setBlocklistMethod]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleConfirmRemove = useCallback(() => {
|
|
||||||
onRemovePress({
|
|
||||||
remove: removalMethod === 'removeFromClient',
|
|
||||||
changeCategory: removalMethod === 'changeCategory',
|
|
||||||
blocklist: blocklistMethod !== 'doNotBlocklist',
|
|
||||||
skipRedownload: blocklistMethod === 'blocklistOnly',
|
|
||||||
});
|
|
||||||
|
|
||||||
setRemovalMethod('removeFromClient');
|
|
||||||
setBlocklistMethod('doNotBlocklist');
|
|
||||||
}, [
|
|
||||||
removalMethod,
|
|
||||||
blocklistMethod,
|
|
||||||
setRemovalMethod,
|
|
||||||
setBlocklistMethod,
|
|
||||||
onRemovePress,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleModalClose = useCallback(() => {
|
|
||||||
setRemovalMethod('removeFromClient');
|
|
||||||
setBlocklistMethod('doNotBlocklist');
|
|
||||||
|
|
||||||
onModalClose();
|
|
||||||
}, [setRemovalMethod, setBlocklistMethod, onModalClose]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={handleModalClose}>
|
|
||||||
<ModalContent onModalClose={handleModalClose}>
|
|
||||||
<ModalHeader>{title}</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<div className={styles.message}>{message}</div>
|
|
||||||
|
|
||||||
{isPending ? null : (
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('RemoveQueueItemRemovalMethod')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.SELECT}
|
|
||||||
name="removalMethod"
|
|
||||||
value={removalMethod}
|
|
||||||
values={removalMethodOptions}
|
|
||||||
isDisabled={!canChangeCategory && !canIgnore}
|
|
||||||
helpTextWarning={translate(
|
|
||||||
'RemoveQueueItemRemovalMethodHelpTextWarning'
|
|
||||||
)}
|
|
||||||
onChange={handleRemovalMethodChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>
|
|
||||||
{multipleSelected
|
|
||||||
? translate('BlocklistReleases')
|
|
||||||
: translate('BlocklistRelease')}
|
|
||||||
</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.SELECT}
|
|
||||||
name="blocklistMethod"
|
|
||||||
value={blocklistMethod}
|
|
||||||
values={blocklistMethodOptions}
|
|
||||||
helpText={translate('BlocklistReleaseHelpText')}
|
|
||||||
onChange={handleBlocklistMethodChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={handleModalClose}>{translate('Close')}</Button>
|
|
||||||
|
|
||||||
<Button kind={kinds.DANGER} onPress={handleConfirmRemove}>
|
|
||||||
{translate('Remove')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RemoveQueueItemModal;
|
|
||||||
154
frontend/src/Activity/Queue/RemoveQueueItemsModal.js
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './RemoveQueueItemsModal.css';
|
||||||
|
|
||||||
|
class RemoveQueueItemsModal extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
remove: true,
|
||||||
|
blocklist: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
resetState = function() {
|
||||||
|
this.setState({
|
||||||
|
remove: true,
|
||||||
|
blocklist: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onRemoveChange = ({ value }) => {
|
||||||
|
this.setState({ remove: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onBlocklistChange = ({ value }) => {
|
||||||
|
this.setState({ blocklist: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onRemoveConfirmed = () => {
|
||||||
|
const state = this.state;
|
||||||
|
|
||||||
|
this.resetState();
|
||||||
|
this.props.onRemovePress(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
onModalClose = () => {
|
||||||
|
this.resetState();
|
||||||
|
this.props.onModalClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
selectedCount,
|
||||||
|
canIgnore,
|
||||||
|
allPending
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { remove, blocklist } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
size={sizes.MEDIUM}
|
||||||
|
onModalClose={this.onModalClose}
|
||||||
|
>
|
||||||
|
<ModalContent
|
||||||
|
onModalClose={this.onModalClose}
|
||||||
|
>
|
||||||
|
<ModalHeader>
|
||||||
|
{selectedCount > 1 ? translate('RemoveSelectedItems') : translate('RemoveSelectedItem')}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<div className={styles.message}>
|
||||||
|
{selectedCount > 1 ? translate('AreYouSureYouWantToRemoveSelectedItemsFromQueue', selectedCount) : translate('AreYouSureYouWantToRemoveSelectedItemFromQueue')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
allPending ?
|
||||||
|
null :
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="remove"
|
||||||
|
value={remove}
|
||||||
|
helpTextWarning={translate('RemoveHelpTextWarning')}
|
||||||
|
isDisabled={!canIgnore}
|
||||||
|
onChange={this.onRemoveChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
}
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>
|
||||||
|
{selectedCount > 1 ? translate('BlocklistReleases') : translate('BlocklistRelease')}
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="blocklist"
|
||||||
|
value={blocklist}
|
||||||
|
helpText={translate('BlocklistHelpText')}
|
||||||
|
onChange={this.onBlocklistChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={this.onModalClose}>
|
||||||
|
{translate('Close')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
onPress={this.onRemoveConfirmed}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveQueueItemsModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
selectedCount: PropTypes.number.isRequired,
|
||||||
|
canIgnore: PropTypes.bool.isRequired,
|
||||||
|
allPending: PropTypes.bool.isRequired,
|
||||||
|
onRemovePress: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemoveQueueItemsModal;
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'timeleft': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import formatTime from 'Utilities/Date/formatTime';
|
import formatTime from 'Utilities/Date/formatTime';
|
||||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||||
@@ -28,13 +25,11 @@ function TimeleftCell(props) {
|
|||||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowCell className={styles.timeleft}>
|
<TableRowCell
|
||||||
<Tooltip
|
className={styles.timeleft}
|
||||||
anchor={<Icon name={icons.INFO} />}
|
title={translate('DelayingDownloadUntilInterp', [date, time])}
|
||||||
tooltip={translate('DelayingDownloadUntil', { date, time })}
|
>
|
||||||
kind={kinds.INVERSE}
|
-
|
||||||
position={tooltipPositions.TOP}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -44,13 +39,11 @@ function TimeleftCell(props) {
|
|||||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowCell className={styles.timeleft}>
|
<TableRowCell
|
||||||
<Tooltip
|
className={styles.timeleft}
|
||||||
anchor={<Icon name={icons.INFO} />}
|
title={translate('RetryingDownloadInterp', [date, time])}
|
||||||
tooltip={translate('RetryingDownloadOn', { date, time })}
|
>
|
||||||
kind={kinds.INVERSE}
|
-
|
||||||
position={tooltipPositions.TOP}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||