mirror of
https://github.com/Readarr/Readarr.git
synced 2026-03-07 13:40:02 -05:00
Compare commits
124 Commits
sonarr-pul
...
test-rebas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0952d4a0c | ||
|
|
579f1eae2d | ||
|
|
091ebb5cee | ||
|
|
d3a96fac12 | ||
|
|
da0b60dd96 | ||
|
|
3bfdd4aafa | ||
|
|
da2bc75eab | ||
|
|
1d922f3ce6 | ||
|
|
b3bdaeeffa | ||
|
|
604c354284 | ||
|
|
ec0fc6f3e1 | ||
|
|
2d28359627 | ||
|
|
45d0f4bdb7 | ||
|
|
ba1f2a5a7b | ||
|
|
0ef8f2b2f7 | ||
|
|
91d6fe90cd | ||
|
|
baa728f2c1 | ||
|
|
d20f9ea269 | ||
|
|
db809f579c | ||
|
|
4970ed5323 | ||
|
|
fa653bf546 | ||
|
|
f496d96907 | ||
|
|
cfc52d12ea | ||
|
|
40f156c16e | ||
|
|
a322d9ed6d | ||
|
|
604666f961 | ||
|
|
851ff948af | ||
|
|
b6f052f39c | ||
|
|
e6236823cf | ||
|
|
32c759d32b | ||
|
|
993d5818a4 | ||
|
|
2b93aaa9b2 | ||
|
|
4ec873bf7a | ||
|
|
b574048733 | ||
|
|
5c4dfc5e7b | ||
|
|
5545ae94ac | ||
|
|
15e0b8dd4d | ||
|
|
ac4a82abf4 | ||
|
|
cb07186970 | ||
|
|
e5409dbff6 | ||
|
|
59d6a77b74 | ||
|
|
db0151c39b | ||
|
|
4ac646685c | ||
|
|
d01d112bfe | ||
|
|
77ef282916 | ||
|
|
e5f483eadc | ||
|
|
8e78bf71a1 | ||
|
|
0c581c6146 | ||
|
|
cabec3c625 | ||
|
|
a0ef1ebaad | ||
|
|
5abfee1bf8 | ||
|
|
f46d5534f4 | ||
|
|
35225c799b | ||
|
|
89584666ff | ||
|
|
a776336b8c | ||
|
|
39a0cb3f43 | ||
|
|
2abee1970f | ||
|
|
cdf648670d | ||
|
|
810a02090b | ||
|
|
4282b84fb6 | ||
|
|
42acea55fd | ||
|
|
c7e7d48ca3 | ||
|
|
b1fbdebba9 | ||
|
|
b4bfd7d07f | ||
|
|
470ba66d4a | ||
|
|
400fa4a960 | ||
|
|
e9de0420dd | ||
|
|
56a623bad9 | ||
|
|
26bf0c0542 | ||
|
|
33de5d2049 | ||
|
|
1e3b775837 | ||
|
|
efa77c1823 | ||
|
|
3428ee3ca2 | ||
|
|
27c9337071 | ||
|
|
1fbaefc054 | ||
|
|
b1cfa90a9f | ||
|
|
3e01c3089c | ||
|
|
2e80c86e23 | ||
|
|
31837958c1 | ||
|
|
a22297c197 | ||
|
|
6e595bfad3 | ||
|
|
8ffcc8a711 | ||
|
|
4b3db6f596 | ||
|
|
778085544c | ||
|
|
81a20ab0e5 | ||
|
|
770176e127 | ||
|
|
2f56814f58 | ||
|
|
4247605dc9 | ||
|
|
3d4c8df6f7 | ||
|
|
a5e69528a9 | ||
|
|
074bcb4ebf | ||
|
|
2bd4965763 | ||
|
|
fdef79a9dc | ||
|
|
1423ebdb07 | ||
|
|
06d3e47232 | ||
|
|
255c19bb83 | ||
|
|
4104efa9a0 | ||
|
|
5f1b928cd9 | ||
|
|
49b085bac6 | ||
|
|
5a4034ec6a | ||
|
|
ed1175ed86 | ||
|
|
31287eb0d7 | ||
|
|
664905b4f2 | ||
|
|
a4223e8dbf | ||
|
|
3af8c2f504 | ||
|
|
67086060b1 | ||
|
|
099fd3166c | ||
|
|
43363cbd08 | ||
|
|
7d0eeca9a9 | ||
|
|
aa7d289a48 | ||
|
|
c0f8d92c6e | ||
|
|
2732ea43ad | ||
|
|
bc059c3f32 | ||
|
|
172f8e4b31 | ||
|
|
2e9087d4c3 | ||
|
|
d004f95297 | ||
|
|
566d84e22d | ||
|
|
8dd3c14132 | ||
|
|
acb3470988 | ||
|
|
953d8a92c2 | ||
|
|
129245cc5b | ||
|
|
4ecd7eaf08 | ||
|
|
698eedb51a | ||
|
|
a94623d95b |
@@ -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": "Readarr",
|
||||
"image": "mcr.microsoft.com/devcontainers/dotnet:1-6.0",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"nodeGypDependencies": true,
|
||||
"version": "16",
|
||||
"nvmVersion": "latest"
|
||||
}
|
||||
},
|
||||
"forwardPorts": [8787],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["esbenp.prettier-vscode"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,10 @@ indent_size = 4
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
# Avoid "this." and "Me." if not necessary
|
||||
dotnet_style_qualification_for_field = false:warning
|
||||
dotnet_style_qualification_for_property = false:warning
|
||||
dotnet_style_qualification_for_method = false:warning
|
||||
dotnet_style_qualification_for_event = false:warning
|
||||
dotnet_style_qualification_for_field = false:refactoring
|
||||
dotnet_style_qualification_for_property = false:refactoring
|
||||
dotnet_style_qualification_for_method = false:refactoring
|
||||
dotnet_style_qualification_for_event = false:refactoring
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
@@ -32,22 +32,17 @@ csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_switch_labels = true
|
||||
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.required_prefix = _
|
||||
|
||||
# Prefer "var" everywhere
|
||||
csharp_style_var_for_built_in_types = true
|
||||
csharp_style_var_when_type_is_apparent = true
|
||||
csharp_style_var_elsewhere = true
|
||||
# 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
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
|
||||
# Stylecop Rules
|
||||
dotnet_diagnostic.SA0001.severity = none
|
||||
@@ -115,13 +110,13 @@ dotnet_diagnostic.SA1643.severity = none
|
||||
dotnet_diagnostic.SA1648.severity = none
|
||||
dotnet_diagnostic.SA1649.severity = none
|
||||
dotnet_diagnostic.SA1651.severity = none
|
||||
dotnet_diagnostic.SX1101.severity = warning
|
||||
dotnet_diagnostic.SX1309.severity = warning
|
||||
|
||||
# Microsoft Analyzers that fail and need to be sorted thru
|
||||
dotnet_diagnostic.ASP0000.severity = suggestion
|
||||
dotnet_diagnostic.CA1000.severity = suggestion
|
||||
dotnet_diagnostic.CA1001.severity = suggestion
|
||||
dotnet_diagnostic.CA1002.severity = suggestion
|
||||
dotnet_diagnostic.CA1003.severity = suggestion
|
||||
dotnet_diagnostic.CA1008.severity = suggestion
|
||||
dotnet_diagnostic.CA1010.severity = suggestion
|
||||
@@ -168,16 +163,10 @@ dotnet_diagnostic.CA1304.severity = suggestion
|
||||
dotnet_diagnostic.CA1305.severity = suggestion
|
||||
dotnet_diagnostic.CA1307.severity = suggestion
|
||||
dotnet_diagnostic.CA1308.severity = suggestion
|
||||
dotnet_diagnostic.CA1309.severity = suggestion
|
||||
dotnet_diagnostic.CA1310.severity = suggestion
|
||||
dotnet_diagnostic.CA1401.severity = suggestion
|
||||
dotnet_diagnostic.CA1416.severity = suggestion
|
||||
dotnet_diagnostic.CA1507.severity = suggestion
|
||||
dotnet_diagnostic.CA1508.severity = suggestion
|
||||
dotnet_diagnostic.CA1707.severity = suggestion
|
||||
dotnet_diagnostic.CA1708.severity = suggestion
|
||||
dotnet_diagnostic.CA1710.severity = suggestion
|
||||
dotnet_diagnostic.CA1711.severity = suggestion
|
||||
dotnet_diagnostic.CA1712.severity = suggestion
|
||||
dotnet_diagnostic.CA1714.severity = suggestion
|
||||
dotnet_diagnostic.CA1715.severity = suggestion
|
||||
@@ -186,14 +175,12 @@ dotnet_diagnostic.CA1717.severity = suggestion
|
||||
dotnet_diagnostic.CA1720.severity = suggestion
|
||||
dotnet_diagnostic.CA1721.severity = suggestion
|
||||
dotnet_diagnostic.CA1724.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.CA1810.severity = suggestion
|
||||
dotnet_diagnostic.CA1812.severity = suggestion
|
||||
dotnet_diagnostic.CA1813.severity = suggestion
|
||||
dotnet_diagnostic.CA1814.severity = suggestion
|
||||
dotnet_diagnostic.CA1815.severity = suggestion
|
||||
dotnet_diagnostic.CA1816.severity = suggestion
|
||||
@@ -223,7 +210,6 @@ dotnet_diagnostic.CA2101.severity = suggestion
|
||||
dotnet_diagnostic.CA2119.severity = suggestion
|
||||
dotnet_diagnostic.CA2153.severity = suggestion
|
||||
dotnet_diagnostic.CA2200.severity = suggestion
|
||||
dotnet_diagnostic.CA2201.severity = suggestion
|
||||
dotnet_diagnostic.CA2207.severity = suggestion
|
||||
dotnet_diagnostic.CA2208.severity = suggestion
|
||||
dotnet_diagnostic.CA2211.severity = suggestion
|
||||
@@ -269,13 +255,11 @@ dotnet_diagnostic.CA5374.severity = suggestion
|
||||
dotnet_diagnostic.CA5379.severity = suggestion
|
||||
dotnet_diagnostic.CA5384.severity = suggestion
|
||||
dotnet_diagnostic.CA5385.severity = suggestion
|
||||
dotnet_diagnostic.CA5392.severity = suggestion
|
||||
dotnet_diagnostic.CA5394.severity = suggestion
|
||||
dotnet_diagnostic.CA5397.severity = suggestion
|
||||
|
||||
|
||||
|
||||
[*.{js,html,hbs,less,css,ts,tsx}]
|
||||
[*.{js,html,js,hbs,less,css}]
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
9
.esprintrc
Normal file
9
.esprintrc
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"paths": [
|
||||
"frontend/src/**/*.js"
|
||||
],
|
||||
"ignored": [
|
||||
"**/node_modules/**/*"
|
||||
],
|
||||
"port": 5004
|
||||
}
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -3,7 +3,8 @@
|
||||
|
||||
# Explicitly set bash scripts to have unix endings
|
||||
*.sh text eol=lf
|
||||
distribution/osx/Readarr text eol=lf
|
||||
distribution/debian/* text eol=lf
|
||||
macOS/Readarr text eol=lf
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
|
||||
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Support Requests will be closed immediately, if you are unsure go to our Reddit or Discord first. Exceptions do not mean you found a bug!
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Platform Information (please complete the following information):**
|
||||
- OS: [e.g. Windows 10 2004 / Ubuntu 20.10]
|
||||
- Docker: [Yes/No]
|
||||
- Mono or.NET Core Version: [e.g. Mono 5.8 or .Net Core 3.1.10] (found under System -> Status)
|
||||
- Browser and Version [e.g. chrome 86.0.4240.198] (Only needed for UI issues)
|
||||
- Readarr Version [e.g. 0.3.0.430]
|
||||
- Readarr Branch [e.g. master]
|
||||
|
||||
**Trace Logs**
|
||||
Turn on Trace logs under Settings -> General and wait for the bug to occur again. **Upload the full log file here (or another site (e.g. pastebin) and link it). Issues will be closed, if they do not include this!**
|
||||
82
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
82
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,82 +0,0 @@
|
||||
name: Bug Report
|
||||
description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Discord first'
|
||||
labels: ['Type: Bug', 'Status: Needs Triage']
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
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.
|
||||
options:
|
||||
- label: I have searched the existing open and closed issues
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: A concise description of what you're experiencing.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. In this environment...
|
||||
2. With this config...
|
||||
3. Run '...'
|
||||
4. See error...
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment
|
||||
description: |
|
||||
examples:
|
||||
- **OS**: Ubuntu 20.04
|
||||
- **Readarr**: Readarr 0.1.0.432
|
||||
- **Docker Install**: Yes
|
||||
- **Using Reverse Proxy**: No
|
||||
- **Browser**: Firefox 90 (If UI related)
|
||||
- **Database**: Sqlite 3.36.0
|
||||
value: |
|
||||
- OS:
|
||||
- Readarr:
|
||||
- Docker Install:
|
||||
- Using Reverse Proxy:
|
||||
- Browser:
|
||||
- Database:
|
||||
render: markdown
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: What branch are you running?
|
||||
options:
|
||||
- Master
|
||||
- Develop
|
||||
- Nightly
|
||||
- Other (This issue will be closed)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Trace Logs?
|
||||
description: |
|
||||
Trace Logs (https://wiki.servarr.com/readarr/troubleshooting#logging-and-log-files)
|
||||
***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.
|
||||
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Trace Logs have been provided as applicable. Reports may be closed if the required logs are not provided.
|
||||
description: Trace logs are generally required for all bug reports and contain `trace`. Info logs are invalid for bug reports and do not contain `debug` nor `trace`
|
||||
options:
|
||||
- label: I have read and followed the steps in the wiki link above and provided the required trace logs - the logs contain `trace` - that are relevant and show this issue.
|
||||
required: true
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Support via Discord
|
||||
url: https://readarr.com/discord
|
||||
url: https://discord.gg/rkEXY2Rbgn
|
||||
about: Chat with users and devs on support and setup related topics.
|
||||
- name: Support via Reddit
|
||||
url: https://reddit.com/r/Readarr
|
||||
about: Discuss and search thru support topics.
|
||||
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
38
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
38
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Feature Request
|
||||
description: 'Suggest an idea for Readarr'
|
||||
labels: ['Type: Feature Request', 'Status: Needs Triage']
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
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.
|
||||
options:
|
||||
- label: I have searched the existing open and closed issues
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe
|
||||
description: A clear and concise description of what the problem is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: |
|
||||
Links? References? Mockups? Anything that will give us more context about the feature you are encountering!
|
||||
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
required: true
|
||||
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,16 +1,14 @@
|
||||
#### Database Migration
|
||||
YES - XXXX | NO
|
||||
YES | NO
|
||||
|
||||
#### Description
|
||||
A few sentences describing the overall goals of the pull request's commits.
|
||||
|
||||
#### Screenshot (if UI related)
|
||||
|
||||
#### Todos
|
||||
- [ ] Tests
|
||||
- [ ] Translation Keys (./src/NzbDrone.Core/Localization/Core/en.json)
|
||||
- [ ] [Wiki Updates](https://wiki.servarr.com)
|
||||
- [ ] Wiki Updates
|
||||
|
||||
|
||||
#### Issues Fixed or Closed by this PR
|
||||
|
||||
* Fixes #XXXX
|
||||
*
|
||||
|
||||
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@@ -1,12 +0,0 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for more information:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
# https://containers.dev/guide/dependabot
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "devcontainers"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
16
.github/label-actions.yml
vendored
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://readarr.com/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/readarr/troubleshooting#logging-and-log-files).
|
||||
28
.github/labeler.yml
vendored
28
.github/labeler.yml
vendored
@@ -1,28 +0,0 @@
|
||||
'Area: API':
|
||||
- src/Readarr.Api.V1/**/*
|
||||
|
||||
'Area: Db-migration':
|
||||
- src/NzbDrone.Core/Datastore/Migration/*
|
||||
|
||||
'Area: Download Clients':
|
||||
- src/NzbDrone.Core/Download/Clients/**/*
|
||||
|
||||
'Area: Import Lists':
|
||||
- src/NzbDrone.Core/ImportLists/**/*
|
||||
|
||||
'Area: Indexer':
|
||||
- src/NzbDrone.Core/Indexers/**/*
|
||||
|
||||
'Area: Notifications':
|
||||
- src/NzbDrone.Core/Notifications/**/*
|
||||
|
||||
'Area: Organizer':
|
||||
- src/NzbDrone.Core/Organizer/**/*
|
||||
|
||||
'Area: Parser':
|
||||
- src/NzbDrone.Core/Parser/**/*
|
||||
|
||||
'Area: UI':
|
||||
- frontend/**/*
|
||||
- package.json
|
||||
- yarn.lock
|
||||
17
.github/workflows/label-actions.yml
vendored
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
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
|
||||
21
.github/workflows/lock.yml
vendored
21
.github/workflows/lock.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: 'Lock threads'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '90'
|
||||
exclude-issue-created-before: ''
|
||||
exclude-any-issue-labels: ''
|
||||
add-issue-labels: ''
|
||||
issue-comment: ''
|
||||
issue-lock-reason: 'resolved'
|
||||
process-only: ''
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -125,7 +125,6 @@ coverage*.xml
|
||||
coverage*.json
|
||||
setup/Output/
|
||||
*.~is
|
||||
.mono
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
@@ -149,6 +148,3 @@ _temp_*/**/*
|
||||
## Merge any idea folder
|
||||
*/**/.idea
|
||||
*.iml
|
||||
|
||||
# API doc generation
|
||||
.config/
|
||||
|
||||
7
.vscode/extensions.json
vendored
7
.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-dotnettools.csdevkit",
|
||||
"ms-vscode-remote.remote-containers"
|
||||
]
|
||||
}
|
||||
26
.vscode/launch.json
vendored
26
.vscode/launch.json
vendored
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
|
||||
"name": "Run Readarr",
|
||||
"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/Readarr",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"*.yaml": "home-assistant"
|
||||
}
|
||||
}
|
||||
44
.vscode/tasks.json
vendored
44
.vscode/tasks.json
vendored
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build dotnet",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"msbuild",
|
||||
"-restore",
|
||||
"${workspaceFolder}/src/Readarr.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/Readarr.sln",
|
||||
"-property:GenerateFullPaths=true",
|
||||
"-consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/src/Readarr.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@readarr.com>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
@@ -1,13 +1,50 @@
|
||||
# How to Contribute
|
||||
# How to Contribute #
|
||||
|
||||
We're always looking for people to help make Readarr even better, there are a number of ways to contribute.
|
||||
|
||||
This file has been moved to the wiki for the latest details please see the [contributing wiki page](https://wiki.servarr.com/readarr/contributing).
|
||||
## Documentation ##
|
||||
Setup guides, FAQ, the more information we have on the [wiki](https://wiki.servarr.com/Readarr) the better.
|
||||
|
||||
## Documentation
|
||||
## Development ##
|
||||
|
||||
Setup guides, [FAQ](https://wiki.servarr.com/readarr/faq), the more information we have on the [wiki](https://wiki.servarr.com/readarr) the better.
|
||||
### Tools required ###
|
||||
- Visual Studio 2019 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/).
|
||||
- HTML/Javascript editor of choice (VS Code/Sublime Text/Webstorm/Atom/etc)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
- [NodeJS](https://nodejs.org/en/download/) (Node 10.X.X or higher)
|
||||
- [Yarn](https://yarnpkg.com/)
|
||||
- .NET Core 3.1.
|
||||
|
||||
## Development
|
||||
### Getting started ###
|
||||
|
||||
See the [Wiki Page](https://wiki.servarr.com/readarr/contributing)
|
||||
1. Fork Readarr
|
||||
2. Clone the repository into your development machine. [*info*](https://help.github.com/articles/working-with-repositories)
|
||||
3. Grab the submodules `git submodule init && git submodule update`
|
||||
4. Install the required Node Packages `yarn install`
|
||||
5. Start gulp to monitor your dev environment for any changes that need post processing using `yarn start` command.
|
||||
6. Build the project in Visual Studio, Setting startup project to `Readarr.Console` and framework to `netcoreapp31`
|
||||
7. Debug the project in Visual Studio
|
||||
8. Open http://localhost:8787
|
||||
|
||||
### Contributing Code ###
|
||||
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Readarr/Readarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
|
||||
- Rebase from Readarr's develop branch, don't merge
|
||||
- Make meaningful commits, or squash them
|
||||
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
|
||||
- Reach out to us on the discord if you have any questions
|
||||
- Add tests (unit/integration)
|
||||
- Commit with *nix line endings for consistency (We checkout Windows and commit *nix)
|
||||
- One feature/bug fix per pull request to keep things clean and easy to understand
|
||||
- Use 4 spaces instead of tabs, this is the default for VS 2019 and WebStorm (to my knowledge)
|
||||
|
||||
### Pull Requesting ###
|
||||
- Only make pull requests to develop, never master, if you make a PR to master we'll comment on it and close it
|
||||
- You're probably going to get some comments or questions from us, they will be to ensure consistency and maintainability
|
||||
- We'll try to respond to pull requests as soon as possible, if its been a day or two, please reach out to us, we may have missed it
|
||||
- Each PR should come from its own [feature branch](http://martinfowler.com/bliki/FeatureBranch.html) not develop in your fork, it should have a meaningful branch name (what is being added/fixed)
|
||||
- new-feature (Good)
|
||||
- fix-bug (Good)
|
||||
- patch (Bad)
|
||||
- develop (Bad)
|
||||
|
||||
If you have any questions about any of this, please let us know.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 2.0 KiB |
65
README.md
65
README.md
@@ -1,76 +1,53 @@
|
||||
# Readarr
|
||||
|
||||
[](https://dev.azure.com/Readarr/Readarr/_build/latest?definitionId=1&branchName=develop)
|
||||
[](https://translate.servarr.com/engage/readarr/?utm_source=widget)
|
||||
[](https://wiki.servarr.com/readarr/installation#docker)
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
[](#mega-sponsors)
|
||||
[](https://hub.docker.com/r/hotio/readarr)
|
||||

|
||||
[](#backers) [](#sponsors)
|
||||
|
||||
### Readarr is currently in beta testing and is generally still in a work in progress. Features may be broken, incomplete, or cause spontaneous combustion
|
||||
### Readarr is in early stages of development, alpha/beta binary builds are not yet available. Use of any test builds isn't recommend, and may have detrimental effects on your library.
|
||||
|
||||
Readarr is an ebook and audiobook collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new books from your favorite authors and will grab, sort, and rename them.
|
||||
Note that only one type of a given book is supported. If you want both an audiobook and ebook of a given book you will need multiple instances.
|
||||
Readarr is a ebook (and maybe eventually magazine/audiobook) collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new books from your favorite authors and will grab, sort and rename them.
|
||||
|
||||
## Major Features Include
|
||||
## Major Features Include:
|
||||
|
||||
* Can watch for better quality of the ebooks and audiobooks you have and do an automatic upgrade. *e.g. from PDF to AZW3*
|
||||
* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc.
|
||||
* Automatically detects new books
|
||||
* Can scan your existing library and download any missing books
|
||||
* Automatically detects new books.
|
||||
* Can scan your existing library and download any missing books.
|
||||
* Automatic failed download handling will try another release if one fails
|
||||
* Manual search so you can pick any release or to see why a release was not downloaded automatically
|
||||
* Advanced customization for profiles, such that Readarr will always download the copy you want
|
||||
* Fully configurable book renaming
|
||||
* SABnzbd, NZBGet, QBittorrent, Deluge, rTorrent, Transmission, uTorrent, and other download clients are supported and integrated
|
||||
* Full integration with Calibre (add to library, conversion) (Requires Calibre Content Server)
|
||||
* Full integration with SABnzbd and NZBGet
|
||||
* Full integration with Calibre (add to library, conversion)
|
||||
* And a beautiful UI
|
||||
|
||||
## Support
|
||||
|
||||
[](https://wiki.servarr.com/readarr)
|
||||
[](https://readarr.com/discord)
|
||||
[](https://discord.gg/rkEXY2Rbgn)
|
||||
[](https://github.com/Readarr/Readarr/issues)
|
||||
[](https://wiki.servarr.com/Readarr)
|
||||
|
||||
Note: GitHub Issues are for Bugs and Feature Requests Only
|
||||
## Contributors
|
||||
|
||||
[](https://github.com/Readarr/Readarr/issues)
|
||||
|
||||
## Contributors & Developers
|
||||
|
||||
[API Documentation](https://readarr.com/docs/api/)
|
||||
|
||||
This project exists thanks to all the people who contribute.
|
||||
- [Contribute (GitHub)](CONTRIBUTING.md)
|
||||
- [Contribution (Wiki Article)](https://wiki.servarr.com/readarr/contributing)
|
||||
|
||||
[](https://github.com/Readarr/Readarr/graphs/contributors)
|
||||
This project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).
|
||||
<a href="https://github.com/Readarr/Readarr/graphs/contributors"><img src="https://opencollective.com/Readarr/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
## Backers
|
||||
|
||||
Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/Readarr#backer)
|
||||
Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/readarr#backer)
|
||||
|
||||
[](https://opencollective.com/Readarr#backer)
|
||||
<img src="https://opencollective.com/readarr/backers.svg?width=890"></a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor](https://opencollective.com/readarr#sponsor)
|
||||
|
||||
[](https://opencollective.com/readarr#sponsor)
|
||||
<img src="https://opencollective.com/readarr/sponsors.svg?width=890"></a>
|
||||
|
||||
## Mega Sponsors
|
||||
|
||||
[](https://opencollective.com/readarr#mega-sponsor)
|
||||
|
||||
## DigitalOcean
|
||||
|
||||
This project is also supported by DigitalOcean
|
||||
<p>
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||
</a>
|
||||
</p>
|
||||
<img src="https://opencollective.com/readarr/tiers/mega-sponsor.svg?width=890"></a>
|
||||
|
||||
### License
|
||||
|
||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||
* Copyright 2010-2022
|
||||
* Copyright 2010-2021
|
||||
|
||||
@@ -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.
|
||||
File diff suppressed because it is too large
Load Diff
183
build.sh
183
build.sh
@@ -1,4 +1,4 @@
|
||||
#! /usr/bin/env bash
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
outputFolder='_output'
|
||||
@@ -23,26 +23,7 @@ UpdateVersionNumber()
|
||||
echo "Updating Version Info"
|
||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$READARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" distribution/osx/Readarr.app/Contents/Info.plist
|
||||
fi
|
||||
}
|
||||
|
||||
EnableExtraPlatformsInSDK()
|
||||
{
|
||||
SDK_PATH=$(dotnet --list-sdks | grep -P '6\.\d\.\d+' | head -1 | sed 's/\(6\.[0-9]*\.[0-9]*\).*\[\(.*\)\]/\2\/\1/g')
|
||||
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
||||
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
||||
echo "Extra platforms already enabled"
|
||||
else
|
||||
echo "Enabling extra platform support"
|
||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
||||
fi
|
||||
}
|
||||
|
||||
EnableExtraPlatforms()
|
||||
{
|
||||
if grep -qv freebsd-x64 src/Directory.Build.props; then
|
||||
sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64;linux-x86</RuntimeIdentifiers>^g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" macOS/Readarr.app/Contents/Info.plist
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -76,6 +57,9 @@ Build()
|
||||
platform=Posix
|
||||
fi
|
||||
|
||||
dotnet clean $slnFile -c Debug
|
||||
dotnet clean $slnFile -c Release
|
||||
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -t:PublishAllRids
|
||||
@@ -93,11 +77,11 @@ YarnInstall()
|
||||
ProgressEnd 'yarn install'
|
||||
}
|
||||
|
||||
RunWebpack()
|
||||
RunGulp()
|
||||
{
|
||||
ProgressStart 'Running webpack'
|
||||
yarn run build --env production
|
||||
ProgressEnd 'Running webpack'
|
||||
ProgressStart 'Running gulp'
|
||||
yarn run build --production
|
||||
ProgressEnd 'Running gulp'
|
||||
}
|
||||
|
||||
PackageFiles()
|
||||
@@ -136,7 +120,7 @@ PackageLinux()
|
||||
|
||||
echo "Adding Readarr.Mono to UpdatePackage"
|
||||
cp $folder/Readarr.Mono.* $folder/Readarr.Update
|
||||
if [ "$framework" = "net6.0" ]; then
|
||||
if [ "$framework" = "netcoreapp3.1" ]; then
|
||||
cp $folder/Mono.Posix.NETStandard.* $folder/Readarr.Update
|
||||
cp $folder/libMonoPosixHelper.* $folder/Readarr.Update
|
||||
fi
|
||||
@@ -147,13 +131,17 @@ PackageLinux()
|
||||
PackageMacOS()
|
||||
{
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
ProgressStart "Creating MacOS Package for $framework $runtime"
|
||||
ProgressStart "Creating MacOS Package for $framework"
|
||||
|
||||
local folder=$artifactsFolder/$runtime/$framework/Readarr
|
||||
local folder=$artifactsFolder/macos/$framework/Readarr
|
||||
|
||||
PackageFiles "$folder" "$framework" "$runtime"
|
||||
PackageFiles "$folder" "$framework" "osx-x64"
|
||||
|
||||
if [ "$framework" = "net462" ]; then
|
||||
echo "Adding Startup script"
|
||||
cp macOS/Readarr $folder
|
||||
fi
|
||||
|
||||
echo "Removing Service helpers"
|
||||
rm -f $folder/ServiceUninstall.*
|
||||
@@ -164,7 +152,7 @@ PackageMacOS()
|
||||
|
||||
echo "Adding Readarr.Mono to UpdatePackage"
|
||||
cp $folder/Readarr.Mono.* $folder/Readarr.Update
|
||||
if [ "$framework" = "net6.0" ]; then
|
||||
if [ "$framework" = "netcoreapp3.1" ]; then
|
||||
cp $folder/Mono.Posix.NETStandard.* $folder/Readarr.Update
|
||||
cp $folder/libMonoPosixHelper.* $folder/Readarr.Update
|
||||
fi
|
||||
@@ -175,19 +163,18 @@ PackageMacOS()
|
||||
PackageMacOSApp()
|
||||
{
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
ProgressStart "Creating macOS App Package for $framework $runtime"
|
||||
ProgressStart "Creating macOS App Package for $framework"
|
||||
|
||||
local folder="$artifactsFolder/$runtime-app/$framework"
|
||||
local folder=$artifactsFolder/macos-app/$framework
|
||||
|
||||
rm -rf $folder
|
||||
mkdir -p $folder
|
||||
cp -r distribution/osx/Readarr.app $folder
|
||||
cp -r macOS/Readarr.app $folder
|
||||
mkdir -p $folder/Readarr.app/Contents/MacOS
|
||||
|
||||
echo "Copying Binaries"
|
||||
cp -r $artifactsFolder/$runtime/$framework/Readarr/* $folder/Readarr.app/Contents/MacOS
|
||||
cp -r $artifactsFolder/macos/$framework/Readarr/* $folder/Readarr.app/Contents/MacOS
|
||||
|
||||
echo "Removing Update Folder"
|
||||
rm -r $folder/Readarr.app/Contents/MacOS/Readarr.Update
|
||||
@@ -199,13 +186,12 @@ PackageWindows()
|
||||
{
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
ProgressStart "Creating $runtime Package for $framework"
|
||||
|
||||
ProgressStart "Creating Windows Package for $framework"
|
||||
|
||||
local folder=$artifactsFolder/$runtime/$framework/Readarr
|
||||
|
||||
PackageFiles "$folder" "$framework" "$runtime"
|
||||
cp -r $outputFolder/$framework-windows/$runtime/publish/* $folder
|
||||
|
||||
echo "Removing Readarr.Mono"
|
||||
rm -f $folder/Readarr.Mono.*
|
||||
@@ -215,7 +201,7 @@ PackageWindows()
|
||||
echo "Adding Readarr.Windows to UpdatePackage"
|
||||
cp $folder/Readarr.Windows.* $folder/Readarr.Update
|
||||
|
||||
ProgressEnd "Creating $runtime Package for $framework"
|
||||
ProgressEnd 'Creating Windows Package'
|
||||
}
|
||||
|
||||
Package()
|
||||
@@ -227,45 +213,19 @@ Package()
|
||||
IFS='-' read -ra SPLIT <<< "$runtime"
|
||||
|
||||
case "${SPLIT[0]}" in
|
||||
linux|freebsd*)
|
||||
linux)
|
||||
PackageLinux "$framework" "$runtime"
|
||||
;;
|
||||
win)
|
||||
PackageWindows "$framework" "$runtime"
|
||||
;;
|
||||
osx)
|
||||
PackageMacOS "$framework" "$runtime"
|
||||
PackageMacOSApp "$framework" "$runtime"
|
||||
PackageMacOS "$framework"
|
||||
PackageMacOSApp "$framework"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
BuildInstaller()
|
||||
{
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
./_inno/ISCC.exe distribution/windows/setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
||||
}
|
||||
|
||||
InstallInno()
|
||||
{
|
||||
ProgressStart "Installing portable Inno Setup"
|
||||
|
||||
rm -rf _inno
|
||||
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.0}.exe"
|
||||
mkdir _inno
|
||||
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
|
||||
rm innosetup.exe
|
||||
|
||||
ProgressEnd "Installed portable Inno Setup"
|
||||
}
|
||||
|
||||
RemoveInno()
|
||||
{
|
||||
rm -rf _inno
|
||||
}
|
||||
|
||||
PackageTests()
|
||||
{
|
||||
local framework="$1"
|
||||
@@ -297,10 +257,7 @@ if [ $# -eq 0 ]; then
|
||||
BACKEND=YES
|
||||
FRONTEND=YES
|
||||
PACKAGES=YES
|
||||
INSTALLER=NO
|
||||
LINT=YES
|
||||
ENABLE_EXTRA_PLATFORMS=NO
|
||||
ENABLE_EXTRA_PLATFORMS_IN_SDK=NO
|
||||
fi
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
@@ -312,14 +269,6 @@ case $key in
|
||||
BACKEND=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--enable-bsd|--enable-extra-platforms)
|
||||
ENABLE_EXTRA_PLATFORMS=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--enable-extra-platforms-in-sdk)
|
||||
ENABLE_EXTRA_PLATFORMS_IN_SDK=YES
|
||||
shift # past argument
|
||||
;;
|
||||
-r|--runtime)
|
||||
RID="$2"
|
||||
shift # past argument
|
||||
@@ -338,10 +287,6 @@ case $key in
|
||||
PACKAGES=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--installer)
|
||||
INSTALLER=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--lint)
|
||||
LINT=YES
|
||||
shift # past argument
|
||||
@@ -361,81 +306,55 @@ esac
|
||||
done
|
||||
set -- "${POSITIONAL[@]}" # restore positional parameters
|
||||
|
||||
if [ "$ENABLE_EXTRA_PLATFORMS_IN_SDK" = "YES" ];
|
||||
then
|
||||
EnableExtraPlatformsInSDK
|
||||
fi
|
||||
|
||||
if [ "$BACKEND" = "YES" ];
|
||||
then
|
||||
UpdateVersionNumber
|
||||
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ];
|
||||
then
|
||||
EnableExtraPlatforms
|
||||
fi
|
||||
Build
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
PackageTests "net6.0" "win-x64"
|
||||
PackageTests "net6.0" "win-x86"
|
||||
PackageTests "net6.0" "linux-x64"
|
||||
PackageTests "net6.0" "linux-musl-x64"
|
||||
PackageTests "net6.0" "osx-x64"
|
||||
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ];
|
||||
then
|
||||
PackageTests "net6.0" "freebsd-x64"
|
||||
PackageTests "net6.0" "linux-x86"
|
||||
fi
|
||||
PackageTests "netcoreapp3.1" "win-x64"
|
||||
PackageTests "netcoreapp3.1" "win-x86"
|
||||
PackageTests "netcoreapp3.1" "linux-x64"
|
||||
PackageTests "netcoreapp3.1" "linux-musl-x64"
|
||||
PackageTests "netcoreapp3.1" "osx-x64"
|
||||
PackageTests "net462" "linux-x64"
|
||||
else
|
||||
PackageTests "$FRAMEWORK" "$RID"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$LINT" = "YES" || "$FRONTEND" = "YES" ]];
|
||||
if [ "$FRONTEND" = "YES" ];
|
||||
then
|
||||
YarnInstall
|
||||
RunGulp
|
||||
fi
|
||||
|
||||
if [ "$LINT" = "YES" ];
|
||||
then
|
||||
if [ -z "$FRONTEND" ];
|
||||
then
|
||||
YarnInstall
|
||||
fi
|
||||
|
||||
LintUI
|
||||
fi
|
||||
|
||||
if [ "$FRONTEND" = "YES" ];
|
||||
then
|
||||
RunWebpack
|
||||
fi
|
||||
|
||||
if [ "$PACKAGES" = "YES" ];
|
||||
then
|
||||
UpdateVersionNumber
|
||||
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
Package "net6.0" "win-x64"
|
||||
Package "net6.0" "win-x86"
|
||||
Package "net6.0" "linux-x64"
|
||||
Package "net6.0" "linux-musl-x64"
|
||||
Package "net6.0" "linux-arm64"
|
||||
Package "net6.0" "linux-musl-arm64"
|
||||
Package "net6.0" "linux-arm"
|
||||
Package "net6.0" "linux-musl-arm"
|
||||
Package "net6.0" "osx-x64"
|
||||
Package "net6.0" "osx-arm64"
|
||||
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ];
|
||||
then
|
||||
Package "net6.0" "freebsd-x64"
|
||||
Package "net6.0" "linux-x86"
|
||||
fi
|
||||
Package "netcoreapp3.1" "win-x64"
|
||||
Package "netcoreapp3.1" "win-x86"
|
||||
Package "netcoreapp3.1" "linux-x64"
|
||||
Package "netcoreapp3.1" "linux-musl-x64"
|
||||
Package "netcoreapp3.1" "linux-arm64"
|
||||
Package "netcoreapp3.1" "linux-musl-arm64"
|
||||
Package "netcoreapp3.1" "linux-arm"
|
||||
Package "netcoreapp3.1" "osx-x64"
|
||||
Package "net462" "linux-x64"
|
||||
else
|
||||
Package "$FRAMEWORK" "$RID"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$INSTALLER" = "YES" ];
|
||||
then
|
||||
InstallInno
|
||||
BuildInstaller "net6.0" "win-x64"
|
||||
BuildInstaller "net6.0" "win-x86"
|
||||
RemoveInno
|
||||
fi
|
||||
|
||||
5
debian/changelog
vendored
Normal file
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
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
12
debian/control
vendored
Normal file
12
debian/control
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Section: web
|
||||
Priority: optional
|
||||
Maintainer: Sonarr <contact@nzbdrone.com>
|
||||
Source: nzbdrone
|
||||
Homepage: https://readarr.com
|
||||
Vcs-Git: git@github.com:readarr/Readarr.git
|
||||
Vcs-Browser: https://github.com/readarr/Readarr
|
||||
|
||||
Package: nzbdrone
|
||||
Architecture: all
|
||||
Depends: libmono-cil-dev (>= 3.2), sqlite3 (>= 3.7), mediainfo (>= 0.7.52)
|
||||
Description: Readarr is a music collection manager
|
||||
24
debian/copyright
vendored
Executable file
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/readarr/Readarr
|
||||
|
||||
Files: *
|
||||
Copyright: 2010-2016 Readarr <hello@readarr.com>
|
||||
|
||||
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
1
debian/install
vendored
Executable file
@@ -0,0 +1 @@
|
||||
nzbdrone_bin/* opt/NzbDrone
|
||||
13
debian/rules
vendored
Executable file
13
debian/rules
vendored
Executable 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 $@
|
||||
Binary file not shown.
@@ -1,84 +0,0 @@
|
||||
; Script generated by the Inno Setup Script Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define AppName "Readarr"
|
||||
#define AppPublisher "Team Readarr"
|
||||
#define AppURL "https://readarr.com/"
|
||||
#define ForumsURL "https://readarr.com/discord"
|
||||
#define AppExeName "Readarr.exe"
|
||||
#define BaseVersion GetEnv('MAJORVERSION')
|
||||
#define BuildNumber GetEnv('MINORVERSION')
|
||||
#define BuildVersion GetEnv('READARRVERSION')
|
||||
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application.
|
||||
; Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppId={{EA316CFC-40C5-4104-A7E1-AFA4D42702D8}
|
||||
AppName={#AppName}
|
||||
AppVersion={#BaseVersion}
|
||||
AppPublisher={#AppPublisher}
|
||||
AppPublisherURL={#AppURL}
|
||||
AppSupportURL={#ForumsURL}
|
||||
AppUpdatesURL={#AppURL}
|
||||
DefaultDirName={commonappdata}\Readarr
|
||||
DisableDirPage=yes
|
||||
DefaultGroupName={#AppName}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputBaseFilename=Readarr.{#BuildVersion}.{#Runtime}
|
||||
SolidCompression=yes
|
||||
AppCopyright=Creative Commons 3.0 License
|
||||
AllowUNCPath=False
|
||||
UninstallDisplayIcon={app}\bin\Readarr.exe
|
||||
DisableReadyPage=True
|
||||
CompressionThreads=2
|
||||
Compression=lzma2/normal
|
||||
AppContact={#ForumsURL}
|
||||
VersionInfoVersion={#BaseVersion}.{#BuildNumber}
|
||||
SetupLogging=yes
|
||||
OutputDir=output
|
||||
WizardStyle=modern
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopIcon"; Description: "{cm:CreateDesktopIcon}"
|
||||
Name: "windowsService"; Description: "Install Windows Service (Starts when the computer starts as the LocalService user, you will need to change the user to access network shares)"; GroupDescription: "Start automatically"; Flags: exclusive
|
||||
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||
|
||||
[Dirs]
|
||||
Name: "{app}"; Permissions: users-modify
|
||||
|
||||
[Files]
|
||||
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Readarr\Readarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
|
||||
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Readarr\*"; Excludes: "Readarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#AppName}"; Filename: "{app}\bin\{#AppExeName}"; Parameters: "/icon"
|
||||
Name: "{commondesktop}\{#AppName}"; Filename: "{app}\bin\{#AppExeName}"; Parameters: "/icon"; Tasks: desktopIcon
|
||||
Name: "{userstartup}\{#AppName}"; Filename: "{app}\bin\Readarr.exe"; WorkingDir: "{app}\bin"; Tasks: startupShortcut
|
||||
|
||||
[InstallDelete]
|
||||
Name: "{app}\bin"; Type: filesandordirs
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\bin\Readarr.Console.exe"; StatusMsg: "Removing previous Windows Service"; Parameters: "/u /exitimmediately"; Flags: runhidden waituntilterminated;
|
||||
Filename: "{app}\bin\Readarr.Console.exe"; Description: "Enable Access from Other Devices"; StatusMsg: "Enabling Remote access"; Parameters: "/registerurl /exitimmediately"; Flags: postinstall runascurrentuser runhidden waituntilterminated; Tasks: startupShortcut none;
|
||||
Filename: "{app}\bin\Readarr.Console.exe"; StatusMsg: "Installing Windows Service"; Parameters: "/i /exitimmediately"; Flags: runhidden waituntilterminated; Tasks: windowsService
|
||||
Filename: "{app}\bin\Readarr.exe"; Description: "Open Readarr Web UI"; Flags: postinstall skipifsilent nowait; Tasks: windowsService;
|
||||
Filename: "{app}\bin\Readarr.exe"; Description: "Start Readarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none;
|
||||
|
||||
[UninstallRun]
|
||||
Filename: "{app}\bin\readarr.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
|
||||
|
||||
[Code]
|
||||
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Exec('net', 'stop readarr', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||
Exec('sc', 'delete readarr', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||
end;
|
||||
48
docs.sh
48
docs.sh
@@ -1,48 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
FRAMEWORK="net6.0"
|
||||
PLATFORM=$1
|
||||
|
||||
if [ "$PLATFORM" = "Windows" ]; then
|
||||
RUNTIME="win-x64"
|
||||
elif [ "$PLATFORM" = "Linux" ]; then
|
||||
RUNTIME="linux-x64"
|
||||
elif [ "$PLATFORM" = "Mac" ]; then
|
||||
RUNTIME="osx-x64"
|
||||
else
|
||||
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
outputFolder='_output'
|
||||
testPackageFolder='_tests'
|
||||
|
||||
rm -rf $outputFolder
|
||||
rm -rf $testPackageFolder
|
||||
|
||||
slnFile=src/Readarr.sln
|
||||
|
||||
platform=Posix
|
||||
|
||||
if [ "$PLATFORM" = "Windows" ]; then
|
||||
application=Readarr.Console.dll
|
||||
else
|
||||
application=Readarr.dll
|
||||
fi
|
||||
|
||||
dotnet clean $slnFile -c Debug
|
||||
dotnet clean $slnFile -c Release
|
||||
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
||||
|
||||
dotnet new tool-manifest
|
||||
dotnet tool install --version 6.6.2 Swashbuckle.AspNetCore.Cli
|
||||
|
||||
dotnet tool run swagger tofile --output ./src/Readarr.Api.V1/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v1 &
|
||||
|
||||
sleep 45
|
||||
|
||||
kill %1
|
||||
|
||||
exit 0
|
||||
25
frontend/.csscomb.json
Normal file
25
frontend/.csscomb.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"remove-empty-rulesets": true,
|
||||
"always-semicolon": true,
|
||||
"color-case": "lower",
|
||||
"block-indent": " ",
|
||||
"color-shorthand": false,
|
||||
"element-case": "lower",
|
||||
"eof-newline": true,
|
||||
"leading-zero": true,
|
||||
"quotes": "double",
|
||||
"sort-order-fallback": "abc",
|
||||
"space-before-colon": "",
|
||||
"space-after-colon": " ",
|
||||
"space-before-combinator": " ",
|
||||
"space-after-combinator": " ",
|
||||
"space-between-declarations": "\n",
|
||||
"space-before-opening-brace": " ",
|
||||
"space-after-opening-brace": "\n",
|
||||
"space-after-selector-delimiter": " ",
|
||||
"space-before-selector-delimiter": "",
|
||||
"space-before-closing-brace": "\n",
|
||||
"strip-spaces": true,
|
||||
"tab-size": true,
|
||||
"unitless-zero": false
|
||||
}
|
||||
335
frontend/.esformatter
Normal file
335
frontend/.esformatter
Normal file
@@ -0,0 +1,335 @@
|
||||
{
|
||||
"indent": {
|
||||
"value": " ",
|
||||
"FunctionExpression": 1,
|
||||
"ArrayExpression": 1,
|
||||
"ObjectExpression": 1
|
||||
},
|
||||
"lineBreak": {
|
||||
"value": "\n",
|
||||
|
||||
"before": {
|
||||
"ArrayPatternClosing": 0,
|
||||
"ArrayPatternComma": 0,
|
||||
"ArrayPatternOpening": 0,
|
||||
"ArrowFunctionExpressionArrow": 0,
|
||||
"ArrowFunctionExpressionClosingBrace": ">=1",
|
||||
"ArrowFunctionExpressionOpeningBrace": 0,
|
||||
"AssignmentExpression": ">=1",
|
||||
"AssignmentOperator": 0,
|
||||
"BlockStatement": 0,
|
||||
"BreakKeyword": ">=1",
|
||||
"CallExpression": -1,
|
||||
"CallExpressionClosingParentheses": -1,
|
||||
"CallExpressionOpeningParentheses": 0,
|
||||
"CatchClosingBrace": ">=1",
|
||||
"CatchKeyword": 0,
|
||||
"CatchOpeningBrace": 0,
|
||||
"ClassDeclaration": ">=1",
|
||||
"ClassDeclarationClosingBrace": ">=1",
|
||||
"ClassDeclarationOpeningBrace": 0,
|
||||
"ConditionalExpression": ">=1",
|
||||
"DeleteOperator": ">=1",
|
||||
"DoWhileStatement": ">=1",
|
||||
"DoWhileStatementClosingBrace": ">=1",
|
||||
"DoWhileStatementOpeningBrace": 0,
|
||||
"ElseIfStatement": 0,
|
||||
"ElseIfStatementClosingBrace": ">=1",
|
||||
"ElseIfStatementOpeningBrace": 0,
|
||||
"ElseStatement": 0,
|
||||
"ElseStatementClosingBrace": ">=1",
|
||||
"ElseStatementOpeningBrace": 0,
|
||||
"EmptyStatement": -1,
|
||||
"EndOfFile": -1,
|
||||
"FinallyClosingBrace": ">=1",
|
||||
"FinallyKeyword": -1,
|
||||
"FinallyOpeningBrace": 0,
|
||||
"ForInStatement": ">=1",
|
||||
"ForInStatementClosingBrace": ">=1",
|
||||
"ForInStatementExpressionClosing": 0,
|
||||
"ForInStatementExpressionOpening": 0,
|
||||
"ForInStatementOpeningBrace": 0,
|
||||
"ForStatement": ">=1",
|
||||
"ForStatementClosingBrace": ">=1",
|
||||
"ForStatementExpressionClosing": "<2",
|
||||
"ForStatementExpressionOpening": 0,
|
||||
"ForStatementOpeningBrace": 0,
|
||||
"FunctionDeclaration": ">=1",
|
||||
"FunctionDeclarationClosingBrace": ">=1",
|
||||
"FunctionDeclarationOpeningBrace": 0,
|
||||
"FunctionExpression": 0,
|
||||
"FunctionExpressionClosingBrace": 1,
|
||||
"FunctionExpressionOpeningBrace":0,
|
||||
"IIFEClosingParentheses": 0,
|
||||
"IfStatement": ">=1",
|
||||
"IfStatementClosingBrace": ">=1",
|
||||
"IfStatementOpeningBrace": 0,
|
||||
"LogicalExpression": -1,
|
||||
"MemberExpressionClosing": 0,
|
||||
"MemberExpressionOpening": 0,
|
||||
"MemberExpressionPeriod": -1,
|
||||
"MethodDefinition": ">=1",
|
||||
"ObjectExpressionClosingBrace": "<=1",
|
||||
"ObjectPatternClosingBrace": 0,
|
||||
"ObjectPatternComma": 0,
|
||||
"ObjectPatternOpeningBrace": 0,
|
||||
"ParameterDefault": 0,
|
||||
"Property": "<=2",
|
||||
"PropertyValue": 0,
|
||||
"ReturnStatement": -1,
|
||||
"SwitchClosingBrace": ">=1",
|
||||
"SwitchOpeningBrace": 0,
|
||||
"ThisExpression": -1,
|
||||
"ThrowStatement": ">=1",
|
||||
"TryClosingBrace": ">=1",
|
||||
"TryKeyword": -1,
|
||||
"TryOpeningBrace": 0,
|
||||
"VariableDeclaration": ">=1",
|
||||
"VariableDeclarationSemiColon": 0,
|
||||
"VariableDeclarationWithoutInit": ">=1",
|
||||
"VariableName": ">=1",
|
||||
"VariableValue": 0,
|
||||
"WhileStatement": ">=1",
|
||||
"WhileStatementClosingBrace": ">=1",
|
||||
"WhileStatementOpeningBrace": 0
|
||||
},
|
||||
|
||||
"after": {
|
||||
"ArrayPatternClosing": 0,
|
||||
"ArrayPatternComma": 0,
|
||||
"ArrayPatternOpening": 0,
|
||||
"ArrowFunctionExpressionArrow": 0,
|
||||
"ArrowFunctionExpressionClosingBrace": -1,
|
||||
"ArrowFunctionExpressionOpeningBrace": ">=1",
|
||||
"AssignmentExpression": ">=1",
|
||||
"AssignmentOperator": 0,
|
||||
"BlockStatement": 0,
|
||||
"BreakKeyword": -1,
|
||||
"CallExpression": -1,
|
||||
"CallExpressionClosingParentheses": -1,
|
||||
"CallExpressionOpeningParentheses": -1,
|
||||
"CatchClosingBrace": ">=0",
|
||||
"CatchKeyword": 0,
|
||||
"CatchOpeningBrace": ">=1",
|
||||
"ClassDeclaration": ">=1",
|
||||
"ClassDeclarationClosingBrace": ">=1",
|
||||
"ClassDeclarationOpeningBrace": ">=1",
|
||||
"ConditionalExpression": ">=1",
|
||||
"DeleteOperator": ">=1",
|
||||
"DoWhileStatement": ">=1",
|
||||
"DoWhileStatementClosingBrace": 0,
|
||||
"DoWhileStatementOpeningBrace": ">=1",
|
||||
"ElseIfStatement": ">=1",
|
||||
"ElseIfStatementClosingBrace": ">=1",
|
||||
"ElseIfStatementOpeningBrace": ">=1",
|
||||
"ElseStatement": ">=1",
|
||||
"ElseStatementClosingBrace": ">=1",
|
||||
"ElseStatementOpeningBrace": ">=1",
|
||||
"EmptyStatement": -1,
|
||||
"FinallyClosingBrace": ">=1",
|
||||
"FinallyKeyword": -1,
|
||||
"FinallyOpeningBrace": ">=1",
|
||||
"ForInStatement": ">=1",
|
||||
"ForInStatementClosingBrace": ">=1",
|
||||
"ForInStatementExpressionClosing": -1,
|
||||
"ForInStatementExpressionOpening": "<2",
|
||||
"ForInStatementOpeningBrace": ">=1",
|
||||
"ForStatement": ">=1",
|
||||
"ForStatementClosingBrace": ">=1",
|
||||
"ForStatementExpressionClosing": -1,
|
||||
"ForStatementExpressionOpening": "<2",
|
||||
"ForStatementOpeningBrace": ">=1",
|
||||
"FunctionDeclaration": ">=1",
|
||||
"FunctionDeclarationClosingBrace": ">=1",
|
||||
"FunctionDeclarationOpeningBrace": ">=1",
|
||||
"FunctionExpression": 0,
|
||||
"FunctionExpressionClosingBrace": -1,
|
||||
"FunctionExpressionOpeningBrace": 1,
|
||||
"IIFEOpeningParentheses": 0,
|
||||
"IfStatement": ">=1",
|
||||
"IfStatementClosingBrace": ">=1",
|
||||
"IfStatementOpeningBrace": ">=1",
|
||||
"LogicalExpression": -1,
|
||||
"MemberExpressionClosing": 0,
|
||||
"MemberExpressionOpening": 0,
|
||||
"MemberExpressionPeriod": 0,
|
||||
"MethodDefinition": ">=1",
|
||||
"ObjectExpressionOpeningBrace": "<=1",
|
||||
"ObjectPatternClosingBrace": 0,
|
||||
"ObjectPatternComma": 0,
|
||||
"ObjectPatternOpeningBrace": 0,
|
||||
"ParameterDefault": 0,
|
||||
"Property": -1,
|
||||
"PropertyName": 0,
|
||||
"ReturnStatement": -1,
|
||||
"SwitchCaseColon": ">=1",
|
||||
"SwitchClosingBrace": ">=1",
|
||||
"SwitchOpeningBrace": ">=1",
|
||||
"ThisExpression": 0,
|
||||
"ThrowStatement": ">=1",
|
||||
"TryClosingBrace": 0,
|
||||
"TryKeyword": -1,
|
||||
"TryOpeningBrace": ">=1",
|
||||
"VariableDeclaration": ">=1",
|
||||
"VariableDeclarationSemiColon": ">=1",
|
||||
"VariableValue": -1,
|
||||
"WhileStatement": ">=1",
|
||||
"WhileStatementClosingBrace": ">=1",
|
||||
"WhileStatementOpeningBrace": ">=1"
|
||||
}
|
||||
},
|
||||
"whiteSpace": {
|
||||
"value": " ",
|
||||
"removeTrailing": 1,
|
||||
"before": {
|
||||
"ArgumentComma": 0,
|
||||
"ArgumentList": 0,
|
||||
"ArgumentListArrayExpression": 0,
|
||||
"ArgumentListFunctionExpression": 1,
|
||||
"ArgumentListObjectExpression": 0,
|
||||
"ArrayExpressionClosing": 0,
|
||||
"ArrayExpressionComma": 0,
|
||||
"ArrayExpressionOpening": 1,
|
||||
"AssignmentOperator": 1,
|
||||
"BinaryExpression": 0,
|
||||
"BinaryExpressionOperator": 1,
|
||||
"BlockComment": 1,
|
||||
"CallExpression": 1,
|
||||
"CatchClosingBrace": 1,
|
||||
"CatchKeyword": 1,
|
||||
"CatchOpeningBrace": 1,
|
||||
"CatchParameterList": 0,
|
||||
"CommaOperator": 0,
|
||||
"ConditionalExpressionAlternate": 1,
|
||||
"ConditionalExpressionConsequent": 1,
|
||||
"DoWhileStatementClosingBrace": 1,
|
||||
"DoWhileStatementConditional": 1,
|
||||
"DoWhileStatementOpeningBrace": 1,
|
||||
"ElseIfStatementClosingBrace": 1,
|
||||
"ElseIfStatementOpeningBrace": 1,
|
||||
"ElseStatementClosingBrace": 1,
|
||||
"ElseStatementOpeningBrace": 1,
|
||||
"EmptyStatement": 0,
|
||||
"ExpressionClosingParentheses": 0,
|
||||
"FinallyClosingBrace": 1,
|
||||
"FinallyKeyword": -1,
|
||||
"FinallyOpeningBrace": 1,
|
||||
"ForInStatement": 1,
|
||||
"ForInStatementClosingBrace": 1,
|
||||
"ForInStatementExpressionClosing": 0,
|
||||
"ForInStatementExpressionOpening": 1,
|
||||
"ForInStatementOpeningBrace": 1,
|
||||
"ForStatement": 1,
|
||||
"ForStatementClosingBrace": 1,
|
||||
"ForStatementExpressionClosing": 0,
|
||||
"ForStatementExpressionOpening": 1,
|
||||
"ForStatementOpeningBrace": 1,
|
||||
"ForStatementSemicolon": 0,
|
||||
"FunctionDeclarationClosingBrace": 1,
|
||||
"FunctionDeclarationOpeningBrace": 1,
|
||||
"FunctionExpressionClosingBrace": 1,
|
||||
"FunctionExpressionOpeningBrace": 1,
|
||||
"IfStatementClosingBrace": 1,
|
||||
"IfStatementConditionalClosing": 0,
|
||||
"IfStatementConditionalOpening": 1,
|
||||
"IfStatementOpeningBrace": 1,
|
||||
"LineComment": 1,
|
||||
"LogicalExpressionOperator": 1,
|
||||
"MemberExpressionClosing": 0,
|
||||
"ObjectExpressionClosingBrace": 1,
|
||||
"ParameterComma": 0,
|
||||
"ParameterList": 0,
|
||||
"Property": 1,
|
||||
"PropertyName": 1,
|
||||
"PropertyValue": 1,
|
||||
"SwitchDiscriminantClosing": 0,
|
||||
"SwitchDiscriminantOpening": 1,
|
||||
"ThrowKeyword": 1,
|
||||
"TryClosingBrace": 1,
|
||||
"TryKeyword": -1,
|
||||
"TryOpeningBrace": 1,
|
||||
"UnaryExpressionOperator": 0,
|
||||
"VariableName": 1,
|
||||
"VariableValue": 1,
|
||||
"WhileStatementClosingBrace": 1,
|
||||
"WhileStatementConditionalClosing": 0,
|
||||
"WhileStatementConditionalOpening": 1,
|
||||
"WhileStatementOpeningBrace": 1
|
||||
},
|
||||
"after": {
|
||||
"ArgumentComma": 1,
|
||||
"ArgumentList": 0,
|
||||
"ArgumentListArrayExpression": 1,
|
||||
"ArgumentListFunctionExpression": 1,
|
||||
"ArgumentListObjectExpression": 0,
|
||||
"ArrayExpressionClosing": 0,
|
||||
"ArrayExpressionComma": 1,
|
||||
"ArrayExpressionOpening": 0,
|
||||
"AssignmentOperator": 1,
|
||||
"BinaryExpression": 0,
|
||||
"BinaryExpressionOperator": 1,
|
||||
"BlockComment": 1,
|
||||
"CallExpression": 0,
|
||||
"CatchClosingBrace": 1,
|
||||
"CatchKeyword": 1,
|
||||
"CatchOpeningBrace": 1,
|
||||
"CatchParameterList": 0,
|
||||
"CommaOperator": 1,
|
||||
"ConditionalExpressionConsequent": 1,
|
||||
"ConditionalExpressionTest": 1,
|
||||
"DoWhileStatementBody": 1,
|
||||
"DoWhileStatementClosingBrace": 1,
|
||||
"DoWhileStatementOpeningBrace": 1,
|
||||
"ElseIfStatementClosingBrace": 1,
|
||||
"ElseIfStatementOpeningBrace": 1,
|
||||
"ElseStatementClosingBrace": 1,
|
||||
"ElseStatementOpeningBrace": 1,
|
||||
"EmptyStatement": 0,
|
||||
"ExpressionOpeningParentheses": 0,
|
||||
"FinallyClosingBrace": 1,
|
||||
"FinallyKeyword": -1,
|
||||
"FinallyOpeningBrace": 1,
|
||||
"ForInStatement": 1,
|
||||
"ForInStatementClosingBrace": 1,
|
||||
"ForInStatementExpressionClosing": 1,
|
||||
"ForInStatementExpressionOpening": 0,
|
||||
"ForInStatementOpeningBrace": 1,
|
||||
"ForStatement": 1,
|
||||
"ForStatementClosingBrace": 1,
|
||||
"ForStatementExpressionClosing": 1,
|
||||
"ForStatementExpressionOpening": 0,
|
||||
"ForStatementOpeningBrace": 1,
|
||||
"ForStatementSemicolon": 1,
|
||||
"FunctionDeclarationClosingBrace": 0,
|
||||
"FunctionDeclarationOpeningBrace": 0,
|
||||
"FunctionExpressionClosingBrace": 0,
|
||||
"FunctionExpressionOpeningBrace": 0,
|
||||
"FunctionName": 0,
|
||||
"FunctionReservedWord": 0,
|
||||
"IfStatementClosingBrace": 1,
|
||||
"IfStatementConditionalClosing": 0,
|
||||
"IfStatementConditionalOpening": 0,
|
||||
"IfStatementOpeningBrace": 1,
|
||||
"LogicalExpressionOperator": 1,
|
||||
"MemberExpressionOpening": 0,
|
||||
"ObjectExpressionClosingBrace": 0,
|
||||
"ObjectExpressionOpeningBrace": 1,
|
||||
"ParameterComma": 1,
|
||||
"ParameterList": 0,
|
||||
"PropertyName": 0,
|
||||
"PropertyValue": 0,
|
||||
"SwitchDiscriminantClosing": 1,
|
||||
"SwitchDiscriminantOpening": 0,
|
||||
"ThrowKeyword": 1,
|
||||
"TryClosingBrace": 1,
|
||||
"TryKeyword": -1,
|
||||
"TryOpeningBrace": 1,
|
||||
"UnaryExpressionOperator": 0,
|
||||
"VariableName": 1,
|
||||
"WhileStatementClosingBrace": 1,
|
||||
"WhileStatementConditionalClosing": 1,
|
||||
"WhileStatementConditionalOpening": 0,
|
||||
"WhileStatementOpeningBrace": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
**/JsLibraries/**
|
||||
**/*.css.d.ts
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
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
|
||||
.readdirSync(path.join(frontendFolder, 'src'), { withFileTypes: true })
|
||||
.readdirSync('frontend/src', { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => dirent.name)
|
||||
.join('|');
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
|
||||
parser: '@babel/eslint-parser',
|
||||
parser: 'babel-eslint',
|
||||
|
||||
env: {
|
||||
browser: true,
|
||||
@@ -34,9 +25,6 @@ module.exports = {
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: 'module',
|
||||
babelOptions: {
|
||||
configFile: `${frontendFolder}/babel.config.js`
|
||||
},
|
||||
ecmaFeatures: {
|
||||
modules: true,
|
||||
impliedStrict: true
|
||||
@@ -46,11 +34,8 @@ module.exports = {
|
||||
plugins: [
|
||||
'filenames',
|
||||
'react',
|
||||
'react-hooks',
|
||||
'simple-import-sort',
|
||||
'import',
|
||||
'@typescript-eslint',
|
||||
'prettier'
|
||||
'import'
|
||||
],
|
||||
|
||||
settings: {
|
||||
@@ -233,7 +218,7 @@ module.exports = {
|
||||
'consistent-this': ['error', 'self'],
|
||||
'eol-last': 'error',
|
||||
'func-names': 'off',
|
||||
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
|
||||
'func-style': ['error', 'declaration'],
|
||||
indent: ['error', 2, { SwitchCase: 1 }],
|
||||
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
||||
'keyword-spacing': ['error', { before: true, after: true }],
|
||||
@@ -286,7 +271,7 @@ module.exports = {
|
||||
|
||||
// ImportSort
|
||||
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/sort': 'error',
|
||||
'import/newline-after-import': 'error',
|
||||
|
||||
// React
|
||||
@@ -318,17 +303,13 @@ module.exports = {
|
||||
'react/react-in-jsx-scope': 2,
|
||||
'react/self-closing-comp': 2,
|
||||
'react/sort-comp': 2,
|
||||
'react/jsx-wrap-multilines': 2,
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'error'
|
||||
'react/jsx-wrap-multilines': 2
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'*.js'
|
||||
],
|
||||
files: ['*.js'],
|
||||
rules: {
|
||||
'simple-import-sort/imports': [
|
||||
'simple-import-sort/sort': [
|
||||
'error',
|
||||
{
|
||||
groups: [
|
||||
@@ -341,52 +322,6 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'*.ts',
|
||||
'*.tsx'
|
||||
],
|
||||
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './tsconfig.json'
|
||||
},
|
||||
|
||||
extends: [
|
||||
'prettier'
|
||||
],
|
||||
|
||||
rules: Object.assign(typescriptEslintRecommended.rules, {
|
||||
'no-shadow': 'off',
|
||||
// These should be enabled after cleaning things up
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'prettier/prettier': 'error',
|
||||
'simple-import-sort/imports': [
|
||||
'error',
|
||||
{
|
||||
groups: [
|
||||
// Packages
|
||||
// Absolute Paths
|
||||
// Relative Paths
|
||||
// Css
|
||||
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'*.css.d.ts'
|
||||
],
|
||||
rules: {
|
||||
'filenames/match-exported': 'off',
|
||||
'init-declarations': 'off',
|
||||
'prettier/prettier': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
12
frontend/.jsbeautifyrc
Normal file
12
frontend/.jsbeautifyrc
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"js": {
|
||||
"indent_size": 2,
|
||||
"indent_char": " ",
|
||||
"indent_level": 2,
|
||||
"indent_with_tabs": false,
|
||||
"preserve_newlines": true,
|
||||
"brace_style": "collapse",
|
||||
"max_preserve_newlines": 2,
|
||||
"jslint_happy": true
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
# Ignore everything recursively
|
||||
*
|
||||
|
||||
# But not the .ts files
|
||||
!*.ts*
|
||||
|
||||
*css.d.ts
|
||||
|
||||
# Check subdirectories too
|
||||
!*/
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "auto",
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"plugins": [
|
||||
"stylelint-order"
|
||||
],
|
||||
"ignoreFiles": [
|
||||
"frontend/src/Styles/scaffolding.css",
|
||||
"**/*.js"
|
||||
],
|
||||
"rules": {
|
||||
"plugins": [
|
||||
"stylelint-order"
|
||||
],
|
||||
"ignoreFiles": [
|
||||
"frontend/src/Styles/scaffolding.css",
|
||||
"**/*.js"
|
||||
],
|
||||
"rules": {
|
||||
"at-rule-empty-line-before": [
|
||||
"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": [
|
||||
true,
|
||||
{
|
||||
@@ -25,36 +28,83 @@
|
||||
}
|
||||
],
|
||||
"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-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-named": "never",
|
||||
"color-no-invalid-hex": true,
|
||||
"comment-whitespace-inside": "always",
|
||||
"declaration-bang-space-after": "never",
|
||||
"declaration-bang-space-before": "always",
|
||||
"declaration-block-no-duplicate-properties": [
|
||||
true,
|
||||
{
|
||||
"ignoreProperties": [
|
||||
"composes"
|
||||
"composes"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declaration-block-no-redundant-longhand-properties": 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-trailing-semicolon": "always",
|
||||
"declaration-colon-space-after": "always",
|
||||
"declaration-colon-space-before": "never",
|
||||
"font-family-name-quotes": "always-unless-keyword",
|
||||
"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-name-case": "lower",
|
||||
"function-parentheses-newline-inside": "never-multi-line",
|
||||
"function-parentheses-space-inside": "never",
|
||||
"function-url-quotes": "always",
|
||||
"function-url-scheme-disallowed-list": [
|
||||
"data"
|
||||
],
|
||||
"function-whitespace-after": "always",
|
||||
"indentation": 2,
|
||||
"keyframe-declaration-no-important": true,
|
||||
"length-zero-no-unit": true,
|
||||
"max-empty-lines": 1,
|
||||
"max-line-length": [
|
||||
100,
|
||||
{
|
||||
"ignore": [
|
||||
"non-comments"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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-range-operator-space-after": "always",
|
||||
"media-feature-range-operator-space-before": "always",
|
||||
"no-empty-source": true,
|
||||
"no-eol-whitespace": true,
|
||||
"no-extra-semicolons": 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": [
|
||||
"custom-properties",
|
||||
"dollar-variables",
|
||||
@@ -82,7 +132,6 @@
|
||||
"right",
|
||||
"bottom",
|
||||
"left",
|
||||
"inset",
|
||||
"z-index",
|
||||
"display",
|
||||
"visibility",
|
||||
@@ -294,33 +343,54 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"property-case": "lower",
|
||||
"property-no-vendor-prefix": true,
|
||||
"rule-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"first-nested"
|
||||
"first-nested"
|
||||
],
|
||||
"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-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-class": 3,
|
||||
"selector-max-compound-selectors": 3,
|
||||
"selector-max-empty-lines": 0,
|
||||
"selector-max-id": 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-no-unknown": true,
|
||||
"selector-type-case": "lower",
|
||||
"selector-type-no-unknown": true,
|
||||
"shorthand-property-no-redundant-values": true,
|
||||
"string-no-newline": true,
|
||||
"string-quotes": "single",
|
||||
"time-min-milliseconds": 100,
|
||||
"unit-case": "lower",
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
7
frontend/.vscode/extensions.json
vendored
7
frontend/.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"stylelint.vscode-stylelint",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
23
frontend/.vscode/settings.json
vendored
23
frontend/.vscode/settings.json
vendored
@@ -1,23 +0,0 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.insertFinalNewline": true,
|
||||
|
||||
"files.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.d.css": true
|
||||
},
|
||||
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true
|
||||
},
|
||||
|
||||
"typescript.preferences.quoteStyle": "single",
|
||||
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact"
|
||||
],
|
||||
}
|
||||
@@ -2,25 +2,22 @@ const loose = true;
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
'@babel/plugin-transform-logical-assignment-operators',
|
||||
|
||||
// Stage 1
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
['@babel/plugin-transform-optional-chaining', { loose }],
|
||||
['@babel/plugin-transform-nullish-coalescing-operator', { loose }],
|
||||
['@babel/plugin-proposal-optional-chaining', { loose }],
|
||||
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
|
||||
|
||||
// Stage 2
|
||||
'@babel/plugin-transform-export-namespace-from',
|
||||
'@babel/plugin-proposal-export-namespace-from',
|
||||
|
||||
// Stage 3
|
||||
['@babel/plugin-transform-class-properties', { loose }],
|
||||
['@babel/plugin-proposal-class-properties', { loose }],
|
||||
'@babel/plugin-syntax-dynamic-import'
|
||||
],
|
||||
env: {
|
||||
development: {
|
||||
presets: [
|
||||
['@babel/preset-react', { development: true }],
|
||||
'@babel/preset-typescript'
|
||||
['@babel/preset-react', { development: true }]
|
||||
],
|
||||
plugins: [
|
||||
'babel-plugin-inline-classnames'
|
||||
@@ -28,8 +25,7 @@ module.exports = {
|
||||
},
|
||||
production: {
|
||||
presets: [
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript'
|
||||
'@babel/preset-react'
|
||||
],
|
||||
plugins: [
|
||||
'babel-plugin-transform-react-remove-prop-types'
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const FileManagerPlugin = require('filemanager-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
|
||||
module.exports = (env) => {
|
||||
const uiFolder = 'UI';
|
||||
const frontendFolder = path.join(__dirname, '..');
|
||||
const srcFolder = path.join(frontendFolder, 'src');
|
||||
const isProduction = !!env.production;
|
||||
const isProfiling = isProduction && !!env.profile;
|
||||
const inlineWebWorkers = 'no-fallback';
|
||||
|
||||
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
|
||||
|
||||
console.log('Source Folder:', srcFolder);
|
||||
console.log('Output Folder:', distFolder);
|
||||
console.log('isProduction:', isProduction);
|
||||
console.log('isProfiling:', isProfiling);
|
||||
|
||||
const config = {
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
devtool: isProduction ? 'source-map' : 'eval-source-map',
|
||||
|
||||
stats: {
|
||||
children: false
|
||||
},
|
||||
|
||||
watchOptions: {
|
||||
ignored: /node_modules/
|
||||
},
|
||||
|
||||
entry: {
|
||||
index: 'index.ts'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.ts',
|
||||
'.tsx',
|
||||
'.js'
|
||||
],
|
||||
modules: [
|
||||
srcFolder,
|
||||
path.join(srcFolder, 'Shims'),
|
||||
'node_modules'
|
||||
],
|
||||
alias: {
|
||||
jquery: 'jquery/dist/jquery.min',
|
||||
'react-middle-truncate': 'react-middle-truncate/lib/react-middle-truncate'
|
||||
},
|
||||
fallback: {
|
||||
buffer: false,
|
||||
http: false,
|
||||
https: false,
|
||||
url: false,
|
||||
util: false,
|
||||
net: false
|
||||
}
|
||||
},
|
||||
|
||||
output: {
|
||||
path: distFolder,
|
||||
publicPath: '/',
|
||||
filename: isProduction ? '[name]-[contenthash].js' : '[name].js',
|
||||
sourceMapFilename: '[file].map'
|
||||
},
|
||||
|
||||
optimization: {
|
||||
moduleIds: 'deterministic',
|
||||
chunkIds: isProduction ? 'deterministic' : 'named'
|
||||
},
|
||||
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
|
||||
experiments: {
|
||||
topLevelAwait: true
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
__DEV__: !isProduction,
|
||||
'process.env.NODE_ENV': isProduction ? JSON.stringify('production') : JSON.stringify('development')
|
||||
}),
|
||||
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'Content/styles.css',
|
||||
chunkFilename: isProduction ? 'Content/[id]-[chunkhash].css' : 'Content/[id].css'
|
||||
}),
|
||||
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'frontend/src/index.ejs',
|
||||
filename: 'index.html',
|
||||
publicPath: '/',
|
||||
inject: false
|
||||
}),
|
||||
|
||||
new FileManagerPlugin({
|
||||
events: {
|
||||
onEnd: {
|
||||
copy: [
|
||||
// HTML
|
||||
{
|
||||
source: 'frontend/src/*.html',
|
||||
destination: distFolder
|
||||
},
|
||||
|
||||
// Fonts
|
||||
{
|
||||
source: 'frontend/src/Content/Fonts/*.*',
|
||||
destination: path.join(distFolder, 'Content/Fonts')
|
||||
},
|
||||
|
||||
// Icon Images
|
||||
{
|
||||
source: 'frontend/src/Content/Images/Icons/*.*',
|
||||
destination: path.join(distFolder, 'Content/Images/Icons')
|
||||
},
|
||||
|
||||
// Images
|
||||
{
|
||||
source: 'frontend/src/Content/Images/*.*',
|
||||
destination: path.join(distFolder, 'Content/Images')
|
||||
},
|
||||
|
||||
// Robots
|
||||
{
|
||||
source: 'frontend/src/Content/robots.txt',
|
||||
destination: path.join(distFolder, 'Content/robots.txt')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
new ForkTsCheckerWebpackPlugin(),
|
||||
|
||||
new LiveReloadPlugin()
|
||||
],
|
||||
|
||||
resolveLoader: {
|
||||
modules: [
|
||||
'node_modules',
|
||||
'frontend/build/webpack/'
|
||||
]
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.worker\.js$/,
|
||||
use: {
|
||||
loader: 'worker-loader',
|
||||
options: {
|
||||
filename: '[name].js',
|
||||
inline: inlineWebWorkers
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: [/\.jsx?$/, /\.tsx?$/],
|
||||
exclude: /(node_modules|JsLibraries)/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
configFile: `${frontendFolder}/babel.config.js`,
|
||||
envName: isProduction ? 'production' : 'development',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: false,
|
||||
loose: true,
|
||||
debug: false,
|
||||
useBuiltIns: 'entry',
|
||||
corejs: 3
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// CSS Modules
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /(node_modules|globals.css)/,
|
||||
use: [
|
||||
{ loader: MiniCssExtractPlugin.loader },
|
||||
{ loader: 'css-modules-typescript-loader' },
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: {
|
||||
localIdentName: isProduction ? '[name]/[local]/[hash:base64:5]' : '[name]/[local]'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
config: 'frontend/postcss.config.js'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Global styles
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: /(node_modules|globals.css)/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Fonts
|
||||
{
|
||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10240,
|
||||
mimetype: 'application/font-woff',
|
||||
emitFile: false,
|
||||
name: 'Content/Fonts/[name].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
emitFile: false,
|
||||
name: 'Content/Fonts/[name].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if (isProfiling) {
|
||||
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
||||
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
||||
|
||||
config.optimization = {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
sourceMap: true, // Must be set to true if using source-maps in production
|
||||
mangle: false,
|
||||
keep_classnames: true,
|
||||
keep_fnames: true
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
18
frontend/gulp/build.js
Normal file
18
frontend/gulp/build.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const gulp = require('gulp');
|
||||
|
||||
require('./clean');
|
||||
require('./copy');
|
||||
require('./webpack');
|
||||
|
||||
gulp.task('build',
|
||||
gulp.series('clean',
|
||||
gulp.parallel(
|
||||
'webpack',
|
||||
'copyHtml',
|
||||
'copyFonts',
|
||||
'copyImages',
|
||||
'copyRobots'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
8
frontend/gulp/clean.js
Normal file
8
frontend/gulp/clean.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const gulp = require('gulp');
|
||||
const del = require('del');
|
||||
|
||||
const paths = require('./helpers/paths');
|
||||
|
||||
gulp.task('clean', () => {
|
||||
return del([paths.dest.root]);
|
||||
});
|
||||
42
frontend/gulp/copy.js
Normal file
42
frontend/gulp/copy.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const print = require('gulp-print').default;
|
||||
const cache = require('gulp-cached');
|
||||
const livereload = require('gulp-livereload');
|
||||
const paths = require('./helpers/paths.js');
|
||||
|
||||
gulp.task('copyHtml', () => {
|
||||
return gulp.src(paths.src.html, { base: paths.src.root })
|
||||
.pipe(cache('copyHtml'))
|
||||
.pipe(print())
|
||||
.pipe(gulp.dest(paths.dest.root))
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
||||
gulp.task('copyFonts', () => {
|
||||
return gulp.src(
|
||||
path.join(paths.src.fonts, '**', '*.*'), { base: paths.src.root }
|
||||
)
|
||||
.pipe(cache('copyFonts'))
|
||||
.pipe(print())
|
||||
.pipe(gulp.dest(paths.dest.root))
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
||||
gulp.task('copyImages', () => {
|
||||
return gulp.src(
|
||||
path.join(paths.src.images, '**', '*.*'), { base: paths.src.root }
|
||||
)
|
||||
.pipe(cache('copyImages'))
|
||||
.pipe(print())
|
||||
.pipe(gulp.dest(paths.dest.root))
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
||||
gulp.task('copyRobots', () => {
|
||||
return gulp.src(paths.src.robots, { base: paths.src.root })
|
||||
.pipe(cache('copyRobots'))
|
||||
.pipe(print())
|
||||
.pipe(gulp.dest(paths.dest.root))
|
||||
.pipe(livereload());
|
||||
});
|
||||
5
frontend/gulp/gulpFile.js
Normal file
5
frontend/gulp/gulpFile.js
Normal file
@@ -0,0 +1,5 @@
|
||||
require('./build.js');
|
||||
require('./clean.js');
|
||||
require('./copy.js');
|
||||
require('./watch.js');
|
||||
require('./webpack.js');
|
||||
6
frontend/gulp/helpers/errorHandler.js
Normal file
6
frontend/gulp/helpers/errorHandler.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const colors = require('ansi-colors');
|
||||
|
||||
module.exports = function errorHandler(error) {
|
||||
console.log(colors.red(`Error (${error.plugin}): ${error.message}`));
|
||||
this.emit('end');
|
||||
};
|
||||
24
frontend/gulp/helpers/paths.js
Normal file
24
frontend/gulp/helpers/paths.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const root = './frontend/src';
|
||||
|
||||
const paths = {
|
||||
src: {
|
||||
root,
|
||||
html: `${root}/*.html`,
|
||||
scripts: `${root}/**/*.js`,
|
||||
content: `${root}/Content/`,
|
||||
fonts: `${root}/Content/Fonts/`,
|
||||
images: `${root}/Content/Images/`,
|
||||
robots: `${root}/Content/robots.txt`,
|
||||
exclude: {
|
||||
libs: `!${root}/JsLibraries/**`
|
||||
}
|
||||
},
|
||||
dest: {
|
||||
root: './_output/UI/',
|
||||
content: './_output/UI/Content/',
|
||||
fonts: './_output/UI/Content/Fonts/',
|
||||
images: './_output/UI/Content/Images/'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = paths;
|
||||
19
frontend/gulp/watch.js
Normal file
19
frontend/gulp/watch.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const gulp = require('gulp');
|
||||
const livereload = require('gulp-livereload');
|
||||
const gulpWatch = require('gulp-watch');
|
||||
const paths = require('./helpers/paths.js');
|
||||
|
||||
require('./copy.js');
|
||||
require('./webpack.js');
|
||||
|
||||
function watch() {
|
||||
livereload.listen({ start: true });
|
||||
|
||||
gulp.task('webpackWatch')();
|
||||
gulpWatch(paths.src.html, gulp.series('copyHtml'));
|
||||
gulpWatch(`${paths.src.fonts}**/*.*`, gulp.series('copyFonts'));
|
||||
gulpWatch(`${paths.src.images}**/*.*`, gulp.series('copyImages'));
|
||||
gulpWatch(paths.src.robots, gulp.series('copyRobots'));
|
||||
}
|
||||
|
||||
gulp.task('watch', gulp.series('build', watch));
|
||||
271
frontend/gulp/webpack.js
Normal file
271
frontend/gulp/webpack.js
Normal file
@@ -0,0 +1,271 @@
|
||||
const gulp = require('gulp');
|
||||
const webpackStream = require('webpack-stream');
|
||||
const livereload = require('gulp-livereload');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const errorHandler = require('./helpers/errorHandler');
|
||||
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const HtmlWebpackPluginHtmlTags = require('html-webpack-plugin/lib/html-tags');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
const uiFolder = 'UI';
|
||||
const frontendFolder = path.join(__dirname, '..');
|
||||
const srcFolder = path.join(frontendFolder, 'src');
|
||||
const isProduction = process.argv.indexOf('--production') > -1;
|
||||
const isProfiling = isProduction && process.argv.indexOf('--profile') > -1;
|
||||
const inlineWebWorkers = 'no-fallback';
|
||||
|
||||
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
|
||||
|
||||
console.log('Source Folder:', srcFolder);
|
||||
console.log('Output Folder:', distFolder);
|
||||
console.log('isProduction:', isProduction);
|
||||
console.log('isProfiling:', isProfiling);
|
||||
|
||||
const cssVarsFiles = [
|
||||
'../src/Styles/Variables/colors',
|
||||
'../src/Styles/Variables/dimensions',
|
||||
'../src/Styles/Variables/fonts',
|
||||
'../src/Styles/Variables/animations',
|
||||
'../src/Styles/Variables/zIndexes'
|
||||
].map(require.resolve);
|
||||
|
||||
// Override the way HtmlWebpackPlugin injects the scripts
|
||||
// TODO: Find a better way to get these paths without
|
||||
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets, assetTags) {
|
||||
const head = assetTags.headTags.map((v) => {
|
||||
const href = v.attributes.href
|
||||
.replace('\\', '/')
|
||||
.replace('%5C', '/');
|
||||
|
||||
v.attributes = { rel: 'stylesheet', type: 'text/css', href: `/${href}` };
|
||||
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
|
||||
});
|
||||
const body = assetTags.bodyTags.map((v) => {
|
||||
v.attributes = { src: `/${v.attributes.src}` };
|
||||
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
|
||||
});
|
||||
|
||||
return html
|
||||
.replace('<!-- webpack bundles head -->', head.join('\r\n '))
|
||||
.replace('<!-- webpack bundles body -->', body.join('\r\n '));
|
||||
};
|
||||
|
||||
const plugins = [
|
||||
new OptimizeCssAssetsPlugin({}),
|
||||
|
||||
new webpack.DefinePlugin({
|
||||
__DEV__: !isProduction,
|
||||
'process.env.NODE_ENV': isProduction ? JSON.stringify('production') : JSON.stringify('development')
|
||||
}),
|
||||
|
||||
new MiniCssExtractPlugin({
|
||||
filename: path.join('Content', 'styles.css')
|
||||
}),
|
||||
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'frontend/src/index.html',
|
||||
filename: 'index.html'
|
||||
})
|
||||
];
|
||||
|
||||
const config = {
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
devtool: '#source-map',
|
||||
|
||||
stats: {
|
||||
children: false
|
||||
},
|
||||
|
||||
watchOptions: {
|
||||
ignored: /node_modules/
|
||||
},
|
||||
|
||||
entry: {
|
||||
index: 'index.js'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
modules: [
|
||||
srcFolder,
|
||||
path.join(srcFolder, 'Shims'),
|
||||
'node_modules'
|
||||
],
|
||||
alias: {
|
||||
jquery: 'jquery/src/jquery'
|
||||
}
|
||||
},
|
||||
|
||||
output: {
|
||||
path: distFolder,
|
||||
filename: '[name].js',
|
||||
sourceMapFilename: '[file].map'
|
||||
},
|
||||
|
||||
optimization: {
|
||||
chunkIds: 'named',
|
||||
splitChunks: {
|
||||
chunks: 'initial'
|
||||
}
|
||||
},
|
||||
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
|
||||
plugins,
|
||||
|
||||
resolveLoader: {
|
||||
modules: [
|
||||
'node_modules',
|
||||
'frontend/gulp/webpack/'
|
||||
]
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.worker\.js$/,
|
||||
use: {
|
||||
loader: 'worker-loader',
|
||||
options: {
|
||||
filename: '[name].js',
|
||||
inline: inlineWebWorkers
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js?$/,
|
||||
exclude: /(node_modules|JsLibraries)/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
configFile: `${frontendFolder}/babel.config.js`,
|
||||
envName: isProduction ? 'production' : 'development',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: false,
|
||||
loose: true,
|
||||
debug: false,
|
||||
useBuiltIns: 'entry',
|
||||
corejs: 3
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// CSS Modules
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /(node_modules|globals.css)/,
|
||||
use: [
|
||||
{ loader: MiniCssExtractPlugin.loader },
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: {
|
||||
localIdentName: '[name]/[local]/[hash:base64:5]'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
ident: 'postcss',
|
||||
config: {
|
||||
ctx: {
|
||||
cssVarsFiles
|
||||
},
|
||||
path: 'frontend/postcss.config.js'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Global styles
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: /(node_modules|globals.css)/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Fonts
|
||||
{
|
||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10240,
|
||||
mimetype: 'application/font-woff',
|
||||
emitFile: false,
|
||||
name: 'Content/Fonts/[name].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
emitFile: false,
|
||||
name: 'Content/Fonts/[name].[ext]'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if (isProfiling) {
|
||||
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
||||
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
||||
|
||||
config.optimization.minimizer = [
|
||||
new TerserPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
sourceMap: true, // Must be set to true if using source-maps in production
|
||||
terserOptions: {
|
||||
mangle: false,
|
||||
keep_classnames: true,
|
||||
keep_fnames: true
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
gulp.task('webpack', () => {
|
||||
return webpackStream(config)
|
||||
.pipe(gulp.dest('_output/UI'));
|
||||
});
|
||||
|
||||
gulp.task('webpackWatch', () => {
|
||||
config.watch = true;
|
||||
|
||||
return webpackStream(config, webpack)
|
||||
.on('error', errorHandler)
|
||||
.pipe(gulp.dest('_output/UI'))
|
||||
.on('error', errorHandler)
|
||||
.pipe(livereload())
|
||||
.on('error', errorHandler);
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line filenames/match-exported
|
||||
const loaderUtils = require('loader-utils');
|
||||
|
||||
module.exports = function cssVariablesLoader(source) {
|
||||
@@ -1,31 +1,23 @@
|
||||
const reload = require('require-nocache')(module);
|
||||
|
||||
const cssVarsFiles = [
|
||||
'./src/Styles/Variables/dimensions',
|
||||
'./src/Styles/Variables/fonts',
|
||||
'./src/Styles/Variables/animations',
|
||||
'./src/Styles/Variables/zIndexes'
|
||||
].map(require.resolve);
|
||||
module.exports = (ctx, configPath, options) => {
|
||||
const config = {
|
||||
plugins: {
|
||||
'postcss-mixins': {
|
||||
mixinsDir: [
|
||||
'frontend/src/Styles/Mixins'
|
||||
]
|
||||
},
|
||||
'postcss-simple-vars': {
|
||||
variables: () =>
|
||||
ctx.options.cssVarsFiles.reduce((acc, vars) => {
|
||||
return Object.assign(acc, reload(vars));
|
||||
}, {})
|
||||
},
|
||||
'postcss-color-function': {},
|
||||
'postcss-nested': {}
|
||||
}
|
||||
};
|
||||
|
||||
const mixinsFiles = [
|
||||
'frontend/src/Styles/Mixins/cover.css',
|
||||
'frontend/src/Styles/Mixins/linkOverlay.css',
|
||||
'frontend/src/Styles/Mixins/scroller.css',
|
||||
'frontend/src/Styles/Mixins/truncate.css'
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
['postcss-mixins', {
|
||||
mixinsFiles
|
||||
}],
|
||||
['postcss-simple-vars', {
|
||||
variables: () =>
|
||||
cssVarsFiles.reduce((acc, vars) => {
|
||||
return Object.assign(acc, reload(vars));
|
||||
}, {})
|
||||
}],
|
||||
'postcss-color-function',
|
||||
'postcss-nested'
|
||||
]
|
||||
return config;
|
||||
};
|
||||
|
||||
4
frontend/src/.vscode/settings.json
vendored
Normal file
4
frontend/src/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.insertFinalNewline": true
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
@@ -15,14 +14,13 @@ import TablePager from 'Components/Table/TablePager';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
||||
import selectAll from 'Utilities/Table/selectAll';
|
||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||
import BlocklistRowConnector from './BlocklistRowConnector';
|
||||
import BlacklistRowConnector from './BlacklistRowConnector';
|
||||
|
||||
class Blocklist extends Component {
|
||||
class Blacklist extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -62,33 +60,33 @@ class Blocklist extends Component {
|
||||
|
||||
getSelectedIds = () => {
|
||||
return getSelectedIds(this.state.selectedState);
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSelectAllChange = ({ value }) => {
|
||||
this.setState(selectAll(this.state.selectedState, value));
|
||||
};
|
||||
}
|
||||
|
||||
onSelectedChange = ({ id, value, shiftKey = false }) => {
|
||||
this.setState((state) => {
|
||||
return toggleSelected(state, this.props.items, id, value, shiftKey);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
onRemoveSelectedPress = () => {
|
||||
this.setState({ isConfirmRemoveModalOpen: true });
|
||||
};
|
||||
}
|
||||
|
||||
onRemoveSelectedConfirmed = () => {
|
||||
this.props.onRemoveSelected(this.getSelectedIds());
|
||||
this.setState({ isConfirmRemoveModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
onConfirmRemoveModalClose = () => {
|
||||
this.setState({ isConfirmRemoveModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -104,8 +102,8 @@ class Blocklist extends Component {
|
||||
columns,
|
||||
totalRecords,
|
||||
isRemoving,
|
||||
isClearingBlocklistExecuting,
|
||||
onClearBlocklistPress,
|
||||
isClearingBlacklistExecuting,
|
||||
onClearBlacklistPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
@@ -122,11 +120,11 @@ class Blocklist extends Component {
|
||||
const selectedIds = this.getSelectedIds();
|
||||
|
||||
return (
|
||||
<PageContent title={translate('Blocklist')}>
|
||||
<PageContent title="Blacklist">
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={translate('RemoveSelected')}
|
||||
label="Remove Selected"
|
||||
iconName={icons.REMOVE}
|
||||
isDisabled={!selectedIds.length}
|
||||
isSpinning={isRemoving}
|
||||
@@ -134,10 +132,10 @@ class Blocklist extends Component {
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('Clear')}
|
||||
label="Clear"
|
||||
iconName={icons.CLEAR}
|
||||
isSpinning={isClearingBlocklistExecuting}
|
||||
onPress={onClearBlocklistPress}
|
||||
isSpinning={isClearingBlacklistExecuting}
|
||||
onPress={onClearBlacklistPress}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
|
||||
@@ -147,7 +145,7 @@ class Blocklist extends Component {
|
||||
columns={columns}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
label="Options"
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
@@ -162,16 +160,14 @@ class Blocklist extends Component {
|
||||
|
||||
{
|
||||
!isAnyFetching && !!error &&
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('UnableToLoadBlocklist')}
|
||||
</Alert>
|
||||
<div>Unable to load blacklist</div>
|
||||
}
|
||||
|
||||
{
|
||||
isAllPopulated && !error && !items.length &&
|
||||
<Alert kind={kinds.INFO}>
|
||||
{translate('NoHistoryBlocklist')}
|
||||
</Alert>
|
||||
<div>
|
||||
No history blacklist
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
@@ -189,7 +185,7 @@ class Blocklist extends Component {
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<BlocklistRowConnector
|
||||
<BlacklistRowConnector
|
||||
key={item.id}
|
||||
isSelected={selectedState[item.id] || false}
|
||||
columns={columns}
|
||||
@@ -214,9 +210,9 @@ class Blocklist extends Component {
|
||||
<ConfirmModal
|
||||
isOpen={isConfirmRemoveModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('RemoveSelected')}
|
||||
message={translate('RemoveSelectedItemBlocklistMessageText')}
|
||||
confirmLabel={translate('RemoveSelected')}
|
||||
title="Remove Selected"
|
||||
message={'Are you sure you want to remove the selected items from the blacklist?'}
|
||||
confirmLabel="Remove Selected"
|
||||
onConfirm={this.onRemoveSelectedConfirmed}
|
||||
onCancel={this.onConfirmRemoveModalClose}
|
||||
/>
|
||||
@@ -225,7 +221,7 @@ class Blocklist extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
Blocklist.propTypes = {
|
||||
Blacklist.propTypes = {
|
||||
isAuthorFetching: PropTypes.bool.isRequired,
|
||||
isAuthorPopulated: PropTypes.bool.isRequired,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
@@ -235,9 +231,9 @@ Blocklist.propTypes = {
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
totalRecords: PropTypes.number,
|
||||
isRemoving: PropTypes.bool.isRequired,
|
||||
isClearingBlocklistExecuting: PropTypes.bool.isRequired,
|
||||
isClearingBlacklistExecuting: PropTypes.bool.isRequired,
|
||||
onRemoveSelected: PropTypes.func.isRequired,
|
||||
onClearBlocklistPress: PropTypes.func.isRequired
|
||||
onClearBlacklistPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Blocklist;
|
||||
export default Blacklist;
|
||||
@@ -4,34 +4,34 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import withCurrentPage from 'Components/withCurrentPage';
|
||||
import * as blocklistActions from 'Store/Actions/blocklistActions';
|
||||
import * as blacklistActions from 'Store/Actions/blacklistActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||
import Blocklist from './Blocklist';
|
||||
import Blacklist from './Blacklist';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.blocklist,
|
||||
(state) => state.blacklist,
|
||||
(state) => state.authors,
|
||||
createCommandExecutingSelector(commandNames.CLEAR_BLOCKLIST),
|
||||
(blocklist, authors, isClearingBlocklistExecuting) => {
|
||||
createCommandExecutingSelector(commandNames.CLEAR_BLACKLIST),
|
||||
(blacklist, authors, isClearingBlacklistExecuting) => {
|
||||
return {
|
||||
isAuthorFetching: authors.isFetching,
|
||||
isAuthorPopulated: authors.isPopulated,
|
||||
isClearingBlocklistExecuting,
|
||||
...blocklist
|
||||
isClearingBlacklistExecuting,
|
||||
...blacklist
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...blocklistActions,
|
||||
...blacklistActions,
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class BlocklistConnector extends Component {
|
||||
class BlacklistConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -39,27 +39,27 @@ class BlocklistConnector extends Component {
|
||||
componentDidMount() {
|
||||
const {
|
||||
useCurrentPage,
|
||||
fetchBlocklist,
|
||||
gotoBlocklistFirstPage
|
||||
fetchBlacklist,
|
||||
gotoBlacklistFirstPage
|
||||
} = this.props;
|
||||
|
||||
registerPagePopulator(this.repopulate);
|
||||
|
||||
if (useCurrentPage) {
|
||||
fetchBlocklist();
|
||||
fetchBlacklist();
|
||||
} else {
|
||||
gotoBlocklistFirstPage();
|
||||
gotoBlacklistFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.isClearingBlocklistExecuting && !this.props.isClearingBlocklistExecuting) {
|
||||
this.props.gotoBlocklistFirstPage();
|
||||
if (prevProps.isClearingBlacklistExecuting && !this.props.isClearingBlacklistExecuting) {
|
||||
this.props.gotoBlacklistFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.clearBlocklist();
|
||||
this.props.clearBlacklist();
|
||||
unregisterPagePopulator(this.repopulate);
|
||||
}
|
||||
|
||||
@@ -67,57 +67,57 @@ class BlocklistConnector extends Component {
|
||||
// Control
|
||||
|
||||
repopulate = () => {
|
||||
this.props.fetchBlocklist();
|
||||
};
|
||||
this.props.fetchBlacklist();
|
||||
}
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onFirstPagePress = () => {
|
||||
this.props.gotoBlocklistFirstPage();
|
||||
};
|
||||
this.props.gotoBlacklistFirstPage();
|
||||
}
|
||||
|
||||
onPreviousPagePress = () => {
|
||||
this.props.gotoBlocklistPreviousPage();
|
||||
};
|
||||
this.props.gotoBlacklistPreviousPage();
|
||||
}
|
||||
|
||||
onNextPagePress = () => {
|
||||
this.props.gotoBlocklistNextPage();
|
||||
};
|
||||
this.props.gotoBlacklistNextPage();
|
||||
}
|
||||
|
||||
onLastPagePress = () => {
|
||||
this.props.gotoBlocklistLastPage();
|
||||
};
|
||||
this.props.gotoBlacklistLastPage();
|
||||
}
|
||||
|
||||
onPageSelect = (page) => {
|
||||
this.props.gotoBlocklistPage({ page });
|
||||
};
|
||||
this.props.gotoBlacklistPage({ page });
|
||||
}
|
||||
|
||||
onRemoveSelected = (ids) => {
|
||||
this.props.removeBlocklistItems({ ids });
|
||||
};
|
||||
this.props.removeBlacklistItems({ ids });
|
||||
}
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.setBlocklistSort({ sortKey });
|
||||
};
|
||||
this.props.setBlacklistSort({ sortKey });
|
||||
}
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setBlocklistTableOption(payload);
|
||||
this.props.setBlacklistTableOption(payload);
|
||||
|
||||
if (payload.pageSize) {
|
||||
this.props.gotoBlocklistFirstPage();
|
||||
this.props.gotoBlacklistFirstPage();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onClearBlocklistPress = () => {
|
||||
this.props.executeCommand({ name: commandNames.CLEAR_BLOCKLIST });
|
||||
};
|
||||
onClearBlacklistPress = () => {
|
||||
this.props.executeCommand({ name: commandNames.CLEAR_BLACKLIST });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Blocklist
|
||||
<Blacklist
|
||||
onFirstPagePress={this.onFirstPagePress}
|
||||
onPreviousPagePress={this.onPreviousPagePress}
|
||||
onNextPagePress={this.onNextPagePress}
|
||||
@@ -126,30 +126,30 @@ class BlocklistConnector extends Component {
|
||||
onRemoveSelected={this.onRemoveSelected}
|
||||
onSortPress={this.onSortPress}
|
||||
onTableOptionChange={this.onTableOptionChange}
|
||||
onClearBlocklistPress={this.onClearBlocklistPress}
|
||||
onClearBlacklistPress={this.onClearBlacklistPress}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BlocklistConnector.propTypes = {
|
||||
BlacklistConnector.propTypes = {
|
||||
useCurrentPage: PropTypes.bool.isRequired,
|
||||
isClearingBlocklistExecuting: PropTypes.bool.isRequired,
|
||||
isClearingBlacklistExecuting: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchBlocklist: PropTypes.func.isRequired,
|
||||
gotoBlocklistFirstPage: PropTypes.func.isRequired,
|
||||
gotoBlocklistPreviousPage: PropTypes.func.isRequired,
|
||||
gotoBlocklistNextPage: PropTypes.func.isRequired,
|
||||
gotoBlocklistLastPage: PropTypes.func.isRequired,
|
||||
gotoBlocklistPage: PropTypes.func.isRequired,
|
||||
removeBlocklistItems: PropTypes.func.isRequired,
|
||||
setBlocklistSort: PropTypes.func.isRequired,
|
||||
setBlocklistTableOption: PropTypes.func.isRequired,
|
||||
clearBlocklist: PropTypes.func.isRequired,
|
||||
fetchBlacklist: PropTypes.func.isRequired,
|
||||
gotoBlacklistFirstPage: PropTypes.func.isRequired,
|
||||
gotoBlacklistPreviousPage: PropTypes.func.isRequired,
|
||||
gotoBlacklistNextPage: PropTypes.func.isRequired,
|
||||
gotoBlacklistLastPage: PropTypes.func.isRequired,
|
||||
gotoBlacklistPage: PropTypes.func.isRequired,
|
||||
removeBlacklistItems: PropTypes.func.isRequired,
|
||||
setBlacklistSort: PropTypes.func.isRequired,
|
||||
setBlacklistTableOption: PropTypes.func.isRequired,
|
||||
clearBlacklist: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withCurrentPage(
|
||||
connect(createMapStateToProps, mapDispatchToProps)(BlocklistConnector)
|
||||
connect(createMapStateToProps, mapDispatchToProps)(BlacklistConnector)
|
||||
);
|
||||
@@ -8,9 +8,8 @@ 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 translate from 'Utilities/String/translate';
|
||||
|
||||
class BlocklistDetailsModal extends Component {
|
||||
class BlacklistDetailsModal extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -40,19 +39,19 @@ class BlocklistDetailsModal extends Component {
|
||||
<ModalBody>
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('Protocol')}
|
||||
title="Protocol"
|
||||
data={protocol}
|
||||
/>
|
||||
|
||||
{
|
||||
!!message &&
|
||||
<DescriptionListItem
|
||||
title={translate('Indexer')}
|
||||
title="Indexer"
|
||||
data={indexer}
|
||||
/>
|
||||
}
|
||||
@@ -60,7 +59,7 @@ class BlocklistDetailsModal extends Component {
|
||||
{
|
||||
!!message &&
|
||||
<DescriptionListItem
|
||||
title={translate('Message')}
|
||||
title="Message"
|
||||
data={message}
|
||||
/>
|
||||
}
|
||||
@@ -78,7 +77,7 @@ class BlocklistDetailsModal extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
BlocklistDetailsModal.propTypes = {
|
||||
BlacklistDetailsModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
@@ -87,4 +86,4 @@ BlocklistDetailsModal.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default BlocklistDetailsModal;
|
||||
export default BlacklistDetailsModal;
|
||||
@@ -1,7 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import AuthorNameLink from 'Author/AuthorNameLink';
|
||||
import BookFormats from 'Book/BookFormats';
|
||||
import BookQuality from 'Book/BookQuality';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
@@ -9,11 +8,10 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import BlocklistDetailsModal from './BlocklistDetailsModal';
|
||||
import styles from './BlocklistRow.css';
|
||||
import BlacklistDetailsModal from './BlacklistDetailsModal';
|
||||
import styles from './BlacklistRow.css';
|
||||
|
||||
class BlocklistRow extends Component {
|
||||
class BlacklistRow extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -31,11 +29,11 @@ class BlocklistRow extends Component {
|
||||
|
||||
onDetailsPress = () => {
|
||||
this.setState({ isDetailsModalOpen: true });
|
||||
};
|
||||
}
|
||||
|
||||
onDetailsModalClose = () => {
|
||||
this.setState({ isDetailsModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -46,7 +44,6 @@ class BlocklistRow extends Component {
|
||||
author,
|
||||
sourceTitle,
|
||||
quality,
|
||||
customFormats,
|
||||
date,
|
||||
protocol,
|
||||
indexer,
|
||||
@@ -80,7 +77,7 @@ class BlocklistRow extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'authorMetadata.sortName') {
|
||||
if (name === 'authors.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<AuthorNameLink
|
||||
@@ -112,16 +109,6 @@ class BlocklistRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<BookFormats
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'date') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
@@ -154,7 +141,7 @@ class BlocklistRow extends Component {
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
title={translate('RemoveFromBlocklist')}
|
||||
title="Remove from blacklist"
|
||||
name={icons.REMOVE}
|
||||
kind={kinds.DANGER}
|
||||
onPress={onRemovePress}
|
||||
@@ -167,7 +154,7 @@ class BlocklistRow extends Component {
|
||||
})
|
||||
}
|
||||
|
||||
<BlocklistDetailsModal
|
||||
<BlacklistDetailsModal
|
||||
isOpen={this.state.isDetailsModalOpen}
|
||||
sourceTitle={sourceTitle}
|
||||
protocol={protocol}
|
||||
@@ -181,12 +168,11 @@ class BlocklistRow extends Component {
|
||||
|
||||
}
|
||||
|
||||
BlocklistRow.propTypes = {
|
||||
BlacklistRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
author: PropTypes.object.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
date: PropTypes.string.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
indexer: PropTypes.string,
|
||||
@@ -197,4 +183,4 @@ BlocklistRow.propTypes = {
|
||||
onRemovePress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default BlocklistRow;
|
||||
export default BlacklistRow;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { removeBlocklistItem } from 'Store/Actions/blocklistActions';
|
||||
import { removeBlacklistItem } from 'Store/Actions/blacklistActions';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import BlocklistRow from './BlocklistRow';
|
||||
import BlacklistRow from './BlacklistRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
@@ -18,9 +18,9 @@ function createMapStateToProps() {
|
||||
function createMapDispatchToProps(dispatch, props) {
|
||||
return {
|
||||
onRemovePress() {
|
||||
dispatch(removeBlocklistItem({ id: props.id }));
|
||||
dispatch(removeBlacklistItem({ id: props.id }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(createMapStateToProps, createMapDispatchToProps)(BlocklistRow);
|
||||
export default connect(createMapStateToProps, createMapDispatchToProps)(BlacklistRow);
|
||||
@@ -1,9 +0,0 @@
|
||||
// This file is automatically generated.
|
||||
// Please do not change this file!
|
||||
interface CssExports {
|
||||
'actions': string;
|
||||
'indexer': string;
|
||||
'quality': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
@@ -1,7 +0,0 @@
|
||||
// This file is automatically generated.
|
||||
// Please do not change this file!
|
||||
interface CssExports {
|
||||
'description': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
@@ -9,8 +9,6 @@ import Link from 'Components/Link/Link';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||
import formatAge from 'Utilities/Number/formatAge';
|
||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './HistoryDetails.css';
|
||||
|
||||
function getDetailedList(statusMessages) {
|
||||
@@ -66,10 +64,8 @@ function HistoryDetails(props) {
|
||||
const {
|
||||
indexer,
|
||||
releaseGroup,
|
||||
customFormatScore,
|
||||
nzbInfoUrl,
|
||||
downloadClient,
|
||||
downloadClientName,
|
||||
downloadId,
|
||||
age,
|
||||
ageHours,
|
||||
@@ -77,20 +73,18 @@ function HistoryDetails(props) {
|
||||
publishedDate
|
||||
} = data;
|
||||
|
||||
const downloadClientNameInfo = downloadClientName ?? downloadClient;
|
||||
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!indexer &&
|
||||
<DescriptionListItem
|
||||
title={translate('Indexer')}
|
||||
title="Indexer"
|
||||
data={indexer}
|
||||
/>
|
||||
}
|
||||
@@ -99,22 +93,13 @@ function HistoryDetails(props) {
|
||||
!!releaseGroup &&
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('ReleaseGroup')}
|
||||
title="Release Group"
|
||||
data={releaseGroup}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
customFormatScore && customFormatScore !== '0' ?
|
||||
<DescriptionListItem
|
||||
title={translate('CustomFormatScore')}
|
||||
data={formatCustomFormatScore(customFormatScore)}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
nzbInfoUrl ?
|
||||
!!nzbInfoUrl &&
|
||||
<span>
|
||||
<DescriptionListItemTitle>
|
||||
Info URL
|
||||
@@ -123,23 +108,21 @@ function HistoryDetails(props) {
|
||||
<DescriptionListItemDescription>
|
||||
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
|
||||
</DescriptionListItemDescription>
|
||||
</span> :
|
||||
null
|
||||
</span>
|
||||
}
|
||||
|
||||
{
|
||||
downloadClientNameInfo ?
|
||||
!!downloadClient &&
|
||||
<DescriptionListItem
|
||||
title={translate('DownloadClient')}
|
||||
data={downloadClientNameInfo}
|
||||
/> :
|
||||
null
|
||||
title="Download Client"
|
||||
data={downloadClient}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
!!downloadId &&
|
||||
<DescriptionListItem
|
||||
title={translate('GrabID')}
|
||||
title="Grab ID"
|
||||
data={downloadId}
|
||||
/>
|
||||
}
|
||||
@@ -147,7 +130,7 @@ function HistoryDetails(props) {
|
||||
{
|
||||
!!indexer &&
|
||||
<DescriptionListItem
|
||||
title={translate('AgeWhenGrabbed')}
|
||||
title="Age (when grabbed)"
|
||||
data={formatAge(age, ageHours, ageMinutes)}
|
||||
/>
|
||||
}
|
||||
@@ -155,7 +138,7 @@ function HistoryDetails(props) {
|
||||
{
|
||||
!!publishedDate &&
|
||||
<DescriptionListItem
|
||||
title={translate('PublishedDate')}
|
||||
title="Published Date"
|
||||
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
||||
/>
|
||||
}
|
||||
@@ -172,14 +155,14 @@ function HistoryDetails(props) {
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!message &&
|
||||
<DescriptionListItem
|
||||
title={translate('Message')}
|
||||
title="Message"
|
||||
data={message}
|
||||
/>
|
||||
}
|
||||
@@ -189,7 +172,6 @@ function HistoryDetails(props) {
|
||||
|
||||
if (eventType === 'bookFileImported') {
|
||||
const {
|
||||
customFormatScore,
|
||||
droppedPath,
|
||||
importedPath
|
||||
} = data;
|
||||
@@ -198,7 +180,7 @@ function HistoryDetails(props) {
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
@@ -206,28 +188,18 @@ function HistoryDetails(props) {
|
||||
!!droppedPath &&
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Source')}
|
||||
title="Source"
|
||||
data={droppedPath}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
importedPath ?
|
||||
!!importedPath &&
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('ImportedTo')}
|
||||
title="Imported To"
|
||||
data={importedPath}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
customFormatScore && customFormatScore !== '0' ?
|
||||
<DescriptionListItem
|
||||
title={translate('CustomFormatScore')}
|
||||
data={formatCustomFormatScore(customFormatScore)}
|
||||
/> :
|
||||
null
|
||||
/>
|
||||
}
|
||||
</DescriptionList>
|
||||
);
|
||||
@@ -235,21 +207,20 @@ function HistoryDetails(props) {
|
||||
|
||||
if (eventType === 'bookFileDeleted') {
|
||||
const {
|
||||
reason,
|
||||
customFormatScore
|
||||
reason
|
||||
} = data;
|
||||
|
||||
let reasonMessage = '';
|
||||
|
||||
switch (reason) {
|
||||
case 'Manual':
|
||||
reasonMessage = translate('FileWasDeletedByViaUI');
|
||||
reasonMessage = 'File was deleted by via UI';
|
||||
break;
|
||||
case 'MissingFromDisk':
|
||||
reasonMessage = translate('MissingFromDisk');
|
||||
reasonMessage = 'Readarr was unable to find the file on disk so it was removed';
|
||||
break;
|
||||
case 'Upgrade':
|
||||
reasonMessage = translate('FileWasDeletedByUpgrade');
|
||||
reasonMessage = 'File was deleted to import an upgrade';
|
||||
break;
|
||||
default:
|
||||
reasonMessage = '';
|
||||
@@ -258,23 +229,14 @@ function HistoryDetails(props) {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('Reason')}
|
||||
title="Reason"
|
||||
data={reasonMessage}
|
||||
/>
|
||||
|
||||
{
|
||||
customFormatScore && customFormatScore !== '0' ?
|
||||
<DescriptionListItem
|
||||
title={translate('CustomFormatScore')}
|
||||
data={formatCustomFormatScore(customFormatScore)}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
@@ -288,12 +250,12 @@ function HistoryDetails(props) {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('SourcePath')}
|
||||
title="Source Path"
|
||||
data={sourcePath}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('DestinationPath')}
|
||||
title="Destination Path"
|
||||
data={path}
|
||||
/>
|
||||
</DescriptionList>
|
||||
@@ -309,7 +271,7 @@ function HistoryDetails(props) {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('Path')}
|
||||
title="Path"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
{
|
||||
@@ -324,7 +286,7 @@ function HistoryDetails(props) {
|
||||
})
|
||||
}
|
||||
<DescriptionListItem
|
||||
title={translate('ExistingTagsScrubbed')}
|
||||
title="Existing tags scrubbed"
|
||||
data={tagsScrubbed === 'True' ? <Icon name={icons.CHECK} /> : <Icon name={icons.REMOVE} />}
|
||||
/>
|
||||
</DescriptionList>
|
||||
@@ -339,14 +301,14 @@ function HistoryDetails(props) {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!statusMessages &&
|
||||
<DescriptionListItem
|
||||
title={translate('ImportFailures')}
|
||||
title="Import failures"
|
||||
data={getDetailedList(JSON.parse(statusMessages))}
|
||||
/>
|
||||
}
|
||||
@@ -370,14 +332,14 @@ function HistoryDetails(props) {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!indexer &&
|
||||
<DescriptionListItem
|
||||
title={translate('Indexer')}
|
||||
title="Indexer"
|
||||
data={indexer}
|
||||
/>
|
||||
}
|
||||
@@ -385,7 +347,7 @@ function HistoryDetails(props) {
|
||||
{
|
||||
!!releaseGroup &&
|
||||
<DescriptionListItem
|
||||
title={translate('ReleaseGroup')}
|
||||
title="Release Group"
|
||||
data={releaseGroup}
|
||||
/>
|
||||
}
|
||||
@@ -406,7 +368,7 @@ function HistoryDetails(props) {
|
||||
{
|
||||
!!downloadClient &&
|
||||
<DescriptionListItem
|
||||
title={translate('DownloadClient')}
|
||||
title="Download Client"
|
||||
data={downloadClient}
|
||||
/>
|
||||
}
|
||||
@@ -414,7 +376,7 @@ function HistoryDetails(props) {
|
||||
{
|
||||
!!downloadId &&
|
||||
<DescriptionListItem
|
||||
title={translate('GrabID')}
|
||||
title="Grab ID"
|
||||
data={downloadId}
|
||||
/>
|
||||
}
|
||||
@@ -422,7 +384,7 @@ function HistoryDetails(props) {
|
||||
{
|
||||
!!indexer &&
|
||||
<DescriptionListItem
|
||||
title={translate('AgeWhenGrabbed')}
|
||||
title="Age (when grabbed)"
|
||||
data={formatAge(age, ageHours, ageMinutes)}
|
||||
/>
|
||||
}
|
||||
@@ -430,7 +392,7 @@ function HistoryDetails(props) {
|
||||
{
|
||||
!!publishedDate &&
|
||||
<DescriptionListItem
|
||||
title={translate('PublishedDate')}
|
||||
title="Published Date"
|
||||
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
||||
/>
|
||||
}
|
||||
@@ -447,14 +409,14 @@ function HistoryDetails(props) {
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
|
||||
{
|
||||
!!message &&
|
||||
<DescriptionListItem
|
||||
title={translate('Message')}
|
||||
title="Message"
|
||||
data={message}
|
||||
/>
|
||||
}
|
||||
@@ -466,7 +428,7 @@ function HistoryDetails(props) {
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
descriptionClassName={styles.description}
|
||||
title={translate('Name')}
|
||||
title="Name"
|
||||
data={sourceTitle}
|
||||
/>
|
||||
</DescriptionList>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// This file is automatically generated.
|
||||
// Please do not change this file!
|
||||
interface CssExports {
|
||||
'markAsFailedButton': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
@@ -1,6 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
@@ -12,12 +11,33 @@ import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import TablePager from 'Components/Table/TablePager';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import { align, icons } from 'Helpers/Props';
|
||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import HistoryRowConnector from './HistoryRowConnector';
|
||||
|
||||
class History extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
// Don't update when fetching has completed if items have changed,
|
||||
// before books start fetching or when books start fetching.
|
||||
|
||||
if (
|
||||
(
|
||||
this.props.isFetching &&
|
||||
nextProps.isPopulated &&
|
||||
hasDifferentItems(this.props.items, nextProps.items)
|
||||
) ||
|
||||
(!this.props.isBooksFetching && nextProps.isBooksFetching)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -46,11 +66,11 @@ class History extends Component {
|
||||
const hasError = error || booksError;
|
||||
|
||||
return (
|
||||
<PageContent title={translate('History')}>
|
||||
<PageContent title="History">
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={translate('Refresh')}
|
||||
label="Refresh"
|
||||
iconName={icons.REFRESH}
|
||||
isSpinning={isFetching}
|
||||
onPress={onFirstPagePress}
|
||||
@@ -63,7 +83,7 @@ class History extends Component {
|
||||
columns={columns}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
label="Options"
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
@@ -86,9 +106,7 @@ class History extends Component {
|
||||
|
||||
{
|
||||
!isFetchingAny && hasError &&
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('UnableToLoadHistory')}
|
||||
</Alert>
|
||||
<div>Unable to load history</div>
|
||||
}
|
||||
|
||||
{
|
||||
@@ -96,9 +114,9 @@ class History extends Component {
|
||||
// wait for the books to populate because they are never coming.
|
||||
|
||||
isPopulated && !hasError && !items.length &&
|
||||
<Alert kind={kinds.INFO}>
|
||||
{translate('NoHistory')}
|
||||
</Alert>
|
||||
<div>
|
||||
No history found
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -3,7 +3,10 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import withCurrentPage from 'Components/withCurrentPage';
|
||||
import { clearBooks, fetchBooks } from 'Store/Actions/bookActions';
|
||||
import * as historyActions from 'Store/Actions/historyActions';
|
||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import selectUniqueIds from 'Utilities/Object/selectUniqueIds';
|
||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||
import History from './History';
|
||||
|
||||
@@ -26,7 +29,9 @@ function createMapStateToProps() {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...historyActions
|
||||
...historyActions,
|
||||
fetchBooks,
|
||||
clearBooks
|
||||
};
|
||||
|
||||
class HistoryConnector extends Component {
|
||||
@@ -50,9 +55,21 @@ class HistoryConnector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (hasDifferentItems(prevProps.items, this.props.items)) {
|
||||
const bookIds = selectUniqueIds(this.props.items, 'bookId');
|
||||
if (bookIds.length) {
|
||||
this.props.fetchBooks({ bookIds });
|
||||
} else {
|
||||
this.props.clearBooks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
unregisterPagePopulator(this.repopulate);
|
||||
this.props.clearHistory();
|
||||
this.props.clearBooks();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -60,38 +77,38 @@ class HistoryConnector extends Component {
|
||||
|
||||
repopulate = () => {
|
||||
this.props.fetchHistory();
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onFirstPagePress = () => {
|
||||
this.props.gotoHistoryFirstPage();
|
||||
};
|
||||
}
|
||||
|
||||
onPreviousPagePress = () => {
|
||||
this.props.gotoHistoryPreviousPage();
|
||||
};
|
||||
}
|
||||
|
||||
onNextPagePress = () => {
|
||||
this.props.gotoHistoryNextPage();
|
||||
};
|
||||
}
|
||||
|
||||
onLastPagePress = () => {
|
||||
this.props.gotoHistoryLastPage();
|
||||
};
|
||||
}
|
||||
|
||||
onPageSelect = (page) => {
|
||||
this.props.gotoHistoryPage({ page });
|
||||
};
|
||||
}
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.setHistorySort({ sortKey });
|
||||
};
|
||||
}
|
||||
|
||||
onFilterSelect = (selectedFilterKey) => {
|
||||
this.props.setHistoryFilter({ selectedFilterKey });
|
||||
};
|
||||
}
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setHistoryTableOption(payload);
|
||||
@@ -99,7 +116,7 @@ class HistoryConnector extends Component {
|
||||
if (payload.pageSize) {
|
||||
this.props.gotoHistoryFirstPage();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -133,7 +150,9 @@ HistoryConnector.propTypes = {
|
||||
setHistorySort: PropTypes.func.isRequired,
|
||||
setHistoryFilter: PropTypes.func.isRequired,
|
||||
setHistoryTableOption: PropTypes.func.isRequired,
|
||||
clearHistory: PropTypes.func.isRequired
|
||||
clearHistory: PropTypes.func.isRequired,
|
||||
fetchBooks: PropTypes.func.isRequired,
|
||||
clearBooks: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withCurrentPage(
|
||||
|
||||
@@ -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;
|
||||
@@ -23,6 +23,8 @@ function getIconName(eventType) {
|
||||
return icons.RETAG;
|
||||
case 'bookImportIncomplete':
|
||||
return icons.DOWNLOADED;
|
||||
case 'downloadImported':
|
||||
return icons.DOWNLOADED;
|
||||
case 'downloadIgnored':
|
||||
return icons.IGNORE;
|
||||
default:
|
||||
@@ -59,6 +61,8 @@ function getTooltip(eventType, data) {
|
||||
return 'Book file tags updated';
|
||||
case 'bookImportIncomplete':
|
||||
return 'Files downloaded but not all could be imported';
|
||||
case 'downloadImported':
|
||||
return 'Download completed and successfully imported';
|
||||
case 'downloadIgnored':
|
||||
return 'Book Download Ignored';
|
||||
default:
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.customFormatScore {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 55px;
|
||||
}
|
||||
|
||||
.releaseGroup {
|
||||
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;
|
||||
@@ -1,16 +1,13 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import AuthorNameLink from 'Author/AuthorNameLink';
|
||||
import BookFormats from 'Book/BookFormats';
|
||||
import BookQuality from 'Book/BookQuality';
|
||||
import BookTitleLink from 'Book/BookTitleLink';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||
import { icons, tooltipPositions } from 'Helpers/Props';
|
||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import HistoryDetailsModal from './Details/HistoryDetailsModal';
|
||||
import HistoryEventTypeCell from './HistoryEventTypeCell';
|
||||
import styles from './HistoryRow.css';
|
||||
@@ -43,11 +40,11 @@ class HistoryRow extends Component {
|
||||
|
||||
onDetailsPress = () => {
|
||||
this.setState({ isDetailsModalOpen: true });
|
||||
};
|
||||
}
|
||||
|
||||
onDetailsModalClose = () => {
|
||||
this.setState({ isDetailsModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -57,8 +54,6 @@ class HistoryRow extends Component {
|
||||
author,
|
||||
book,
|
||||
quality,
|
||||
customFormats,
|
||||
customFormatScore,
|
||||
qualityCutoffNotMet,
|
||||
eventType,
|
||||
sourceTitle,
|
||||
@@ -98,7 +93,7 @@ class HistoryRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'authorMetadata.sortName') {
|
||||
if (name === 'authors.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<AuthorNameLink
|
||||
@@ -132,16 +127,6 @@ class HistoryRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<BookFormats
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'date') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
@@ -173,24 +158,6 @@ class HistoryRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'customFormatScore') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.customFormatScore}
|
||||
>
|
||||
<Tooltip
|
||||
anchor={formatCustomFormatScore(
|
||||
customFormatScore,
|
||||
customFormats.length
|
||||
)}
|
||||
tooltip={<BookFormats formats={customFormats} />}
|
||||
position={tooltipPositions.BOTTOM}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'releaseGroup') {
|
||||
return (
|
||||
<TableRowCell
|
||||
@@ -202,28 +169,16 @@ class HistoryRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'sourceTitle') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
>
|
||||
{sourceTitle}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'details') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.details}
|
||||
>
|
||||
<div className={styles.actionContents}>
|
||||
<IconButton
|
||||
name={icons.INFO}
|
||||
onPress={this.onDetailsPress}
|
||||
/>
|
||||
</div>
|
||||
<IconButton
|
||||
name={icons.INFO}
|
||||
onPress={this.onDetailsPress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
@@ -254,8 +209,6 @@ HistoryRow.propTypes = {
|
||||
author: PropTypes.object.isRequired,
|
||||
book: PropTypes.object,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
||||
customFormatScore: PropTypes.number.isRequired,
|
||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||
eventType: PropTypes.string.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
@@ -269,8 +222,4 @@ HistoryRow.propTypes = {
|
||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
HistoryRow.defaultProps = {
|
||||
customFormats: []
|
||||
};
|
||||
|
||||
export default HistoryRow;
|
||||
|
||||
@@ -49,7 +49,7 @@ class HistoryRowConnector extends Component {
|
||||
|
||||
onMarkAsFailedPress = () => {
|
||||
this.props.markAsFailed({ id: this.props.id });
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
.torrent {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
border-color: var(--torrentColor);
|
||||
background-color: var(--torrentColor);
|
||||
border-color: $torrentColor;
|
||||
background-color: $torrentColor;
|
||||
}
|
||||
|
||||
.usenet {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
border-color: var(--usenetColor);
|
||||
background-color: var(--usenetColor);
|
||||
border-color: $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,7 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
@@ -13,17 +12,16 @@ import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
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 hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
|
||||
import selectAll from 'Utilities/Table/selectAll';
|
||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||
import QueueOptionsConnector from './QueueOptionsConnector';
|
||||
import QueueRowConnector from './QueueRowConnector';
|
||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
|
||||
|
||||
class Queue extends Component {
|
||||
|
||||
@@ -33,8 +31,6 @@ class Queue extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this._shouldBlockRefresh = false;
|
||||
|
||||
this.state = {
|
||||
allSelected: false,
|
||||
allUnselected: false,
|
||||
@@ -46,14 +42,6 @@ class Queue extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
if (this._shouldBlockRefresh) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
items,
|
||||
@@ -76,23 +64,13 @@ class Queue extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextState = {};
|
||||
|
||||
if (prevProps.items !== items) {
|
||||
nextState.items = items;
|
||||
}
|
||||
|
||||
const selectedIds = this.getSelectedIds();
|
||||
const isPendingSelected = _.some(this.props.items, (item) => {
|
||||
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
|
||||
});
|
||||
|
||||
if (isPendingSelected !== this.state.isPendingSelected) {
|
||||
nextState.isPendingSelected = isPendingSelected;
|
||||
}
|
||||
|
||||
if (!_.isEmpty(nextState)) {
|
||||
this.setState(nextState);
|
||||
this.setState({ isPendingSelected });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,45 +79,37 @@ class Queue extends Component {
|
||||
|
||||
getSelectedIds = () => {
|
||||
return getSelectedIds(this.state.selectedState);
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onQueueRowModalOpenOrClose = (isOpen) => {
|
||||
this._shouldBlockRefresh = isOpen;
|
||||
};
|
||||
|
||||
onSelectAllChange = ({ value }) => {
|
||||
this.setState(selectAll(this.state.selectedState, value));
|
||||
};
|
||||
}
|
||||
|
||||
onSelectedChange = ({ id, value, shiftKey = false }) => {
|
||||
this.setState((state) => {
|
||||
return toggleSelected(state, this.props.items, id, value, shiftKey);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
onGrabSelectedPress = () => {
|
||||
this.props.onGrabSelectedPress(this.getSelectedIds());
|
||||
};
|
||||
}
|
||||
|
||||
onRemoveSelectedPress = () => {
|
||||
this.setState({ isConfirmRemoveModalOpen: true }, () => {
|
||||
this._shouldBlockRefresh = true;
|
||||
});
|
||||
};
|
||||
this.setState({ isConfirmRemoveModalOpen: true });
|
||||
}
|
||||
|
||||
onRemoveSelectedConfirmed = (payload) => {
|
||||
this._shouldBlockRefresh = false;
|
||||
this.props.onRemoveSelectedPress({ ids: this.getSelectedIds(), ...payload });
|
||||
this.setState({ isConfirmRemoveModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
onConfirmRemoveModalClose = () => {
|
||||
this._shouldBlockRefresh = false;
|
||||
this.setState({ isConfirmRemoveModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -149,6 +119,7 @@ class Queue extends Component {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
items,
|
||||
isAuthorFetching,
|
||||
isAuthorPopulated,
|
||||
isBooksFetching,
|
||||
@@ -168,8 +139,7 @@ class Queue extends Component {
|
||||
allUnselected,
|
||||
selectedState,
|
||||
isConfirmRemoveModalOpen,
|
||||
isPendingSelected,
|
||||
items
|
||||
isPendingSelected
|
||||
} = this.state;
|
||||
|
||||
const isRefreshing = isFetching || isAuthorFetching || isBooksFetching || isRefreshMonitoredDownloadsExecuting;
|
||||
@@ -180,11 +150,11 @@ class Queue extends Component {
|
||||
const disableSelectedActions = selectedCount === 0;
|
||||
|
||||
return (
|
||||
<PageContent title={translate('Queue')}>
|
||||
<PageContent title="Queue">
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={translate('Refresh')}
|
||||
label="Refresh"
|
||||
iconName={icons.REFRESH}
|
||||
isSpinning={isRefreshing}
|
||||
onPress={onRefreshPress}
|
||||
@@ -193,7 +163,7 @@ class Queue extends Component {
|
||||
<PageToolbarSeparator />
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('GrabSelected')}
|
||||
label="Grab Selected"
|
||||
iconName={icons.DOWNLOAD}
|
||||
isDisabled={disableSelectedActions || !isPendingSelected}
|
||||
isSpinning={isGrabbing}
|
||||
@@ -201,7 +171,7 @@ class Queue extends Component {
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label={translate('RemoveSelected')}
|
||||
label="Remove Selected"
|
||||
iconName={icons.REMOVE}
|
||||
isDisabled={disableSelectedActions}
|
||||
isSpinning={isRemoving}
|
||||
@@ -218,7 +188,7 @@ class Queue extends Component {
|
||||
optionsComponent={QueueOptionsConnector}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
label="Options"
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
@@ -227,29 +197,26 @@ class Queue extends Component {
|
||||
|
||||
<PageContentBody>
|
||||
{
|
||||
isRefreshing && !isAllPopulated ?
|
||||
<LoadingIndicator /> :
|
||||
null
|
||||
isRefreshing && !isAllPopulated &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isRefreshing && hasError ?
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('FailedToLoadQueue')}
|
||||
</Alert> :
|
||||
null
|
||||
!isRefreshing && hasError &&
|
||||
<div>
|
||||
Failed to load Queue
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
isAllPopulated && !hasError && !items.length ?
|
||||
<Alert kind={kinds.INFO}>
|
||||
{translate('QueueIsEmpty')}
|
||||
</Alert> :
|
||||
null
|
||||
isPopulated && !hasError && !items.length &&
|
||||
<div>
|
||||
Queue is empty
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
isAllPopulated && !hasError && !!items.length ?
|
||||
isAllPopulated && !hasError && !!items.length &&
|
||||
<div>
|
||||
<Table
|
||||
columns={columns}
|
||||
@@ -271,7 +238,6 @@ class Queue extends Component {
|
||||
columns={columns}
|
||||
{...item}
|
||||
onSelectedChange={this.onSelectedChange}
|
||||
onQueueRowModalOpenOrClose={this.onQueueRowModalOpenOrClose}
|
||||
/>
|
||||
);
|
||||
})
|
||||
@@ -284,21 +250,13 @@ class Queue extends Component {
|
||||
isFetching={isRefreshing}
|
||||
{...otherProps}
|
||||
/>
|
||||
</div> :
|
||||
null
|
||||
</div>
|
||||
}
|
||||
</PageContentBody>
|
||||
|
||||
<RemoveQueueItemModal
|
||||
<RemoveQueueItemsModal
|
||||
isOpen={isConfirmRemoveModalOpen}
|
||||
selectedCount={selectedCount}
|
||||
canChangeCategory={isConfirmRemoveModalOpen && (
|
||||
selectedIds.every((id) => {
|
||||
const item = items.find((i) => i.id === id);
|
||||
|
||||
return !!(item && item.downloadClientHasPostImportCategory);
|
||||
})
|
||||
)}
|
||||
canIgnore={isConfirmRemoveModalOpen && (
|
||||
selectedIds.every((id) => {
|
||||
const item = items.find((i) => i.id === id);
|
||||
@@ -306,17 +264,6 @@ class Queue extends Component {
|
||||
return !!(item && item.authorId && item.bookId);
|
||||
})
|
||||
)}
|
||||
pending={isConfirmRemoveModalOpen && (
|
||||
selectedIds.every((id) => {
|
||||
const item = items.find((i) => i.id === id);
|
||||
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return item.status === 'delay' || item.status === 'downloadClientUnavailable';
|
||||
})
|
||||
)}
|
||||
onRemovePress={this.onRemoveSelectedConfirmed}
|
||||
onModalClose={this.onConfirmRemoveModalClose}
|
||||
/>
|
||||
@@ -345,8 +292,4 @@ Queue.propTypes = {
|
||||
onRemoveSelectedPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
Queue.defaultProps = {
|
||||
count: 0
|
||||
};
|
||||
|
||||
export default Queue;
|
||||
|
||||
@@ -4,9 +4,12 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import withCurrentPage from 'Components/withCurrentPage';
|
||||
import { clearBooks, fetchBooks } from 'Store/Actions/bookActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as queueActions from 'Store/Actions/queueActions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import selectUniqueIds from 'Utilities/Object/selectUniqueIds';
|
||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||
import Queue from './Queue';
|
||||
|
||||
@@ -34,6 +37,8 @@ function createMapStateToProps() {
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...queueActions,
|
||||
fetchBooks,
|
||||
clearBooks,
|
||||
executeCommand
|
||||
};
|
||||
|
||||
@@ -46,7 +51,6 @@ class QueueConnector extends Component {
|
||||
const {
|
||||
useCurrentPage,
|
||||
fetchQueue,
|
||||
fetchQueueStatus,
|
||||
gotoQueueFirstPage
|
||||
} = this.props;
|
||||
|
||||
@@ -57,11 +61,19 @@ class QueueConnector extends Component {
|
||||
} else {
|
||||
gotoQueueFirstPage();
|
||||
}
|
||||
|
||||
fetchQueueStatus();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (hasDifferentItems(prevProps.items, this.props.items)) {
|
||||
const bookIds = selectUniqueIds(this.props.items, 'bookId');
|
||||
|
||||
if (bookIds.length) {
|
||||
this.props.fetchBooks({ bookIds });
|
||||
} else {
|
||||
this.props.clearBooks();
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.props.includeUnknownAuthorItems !==
|
||||
prevProps.includeUnknownAuthorItems
|
||||
@@ -72,6 +84,8 @@ class QueueConnector extends Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
unregisterPagePopulator(this.repopulate);
|
||||
this.props.clearQueue();
|
||||
this.props.clearBooks();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -79,34 +93,34 @@ class QueueConnector extends Component {
|
||||
|
||||
repopulate = () => {
|
||||
this.props.fetchQueue();
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onFirstPagePress = () => {
|
||||
this.props.gotoQueueFirstPage();
|
||||
};
|
||||
}
|
||||
|
||||
onPreviousPagePress = () => {
|
||||
this.props.gotoQueuePreviousPage();
|
||||
};
|
||||
}
|
||||
|
||||
onNextPagePress = () => {
|
||||
this.props.gotoQueueNextPage();
|
||||
};
|
||||
}
|
||||
|
||||
onLastPagePress = () => {
|
||||
this.props.gotoQueueLastPage();
|
||||
};
|
||||
}
|
||||
|
||||
onPageSelect = (page) => {
|
||||
this.props.gotoQueuePage({ page });
|
||||
};
|
||||
}
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.setQueueSort({ sortKey });
|
||||
};
|
||||
}
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setQueueTableOption(payload);
|
||||
@@ -114,21 +128,21 @@ class QueueConnector extends Component {
|
||||
if (payload.pageSize) {
|
||||
this.props.gotoQueueFirstPage();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onRefreshPress = () => {
|
||||
this.props.executeCommand({
|
||||
name: commandNames.REFRESH_MONITORED_DOWNLOADS
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
onGrabSelectedPress = (ids) => {
|
||||
this.props.grabQueueItems({ ids });
|
||||
};
|
||||
}
|
||||
|
||||
onRemoveSelectedPress = (payload) => {
|
||||
this.props.removeQueueItems(payload);
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -157,7 +171,6 @@ QueueConnector.propTypes = {
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
includeUnknownAuthorItems: PropTypes.bool.isRequired,
|
||||
fetchQueue: PropTypes.func.isRequired,
|
||||
fetchQueueStatus: PropTypes.func.isRequired,
|
||||
gotoQueueFirstPage: PropTypes.func.isRequired,
|
||||
gotoQueuePreviousPage: PropTypes.func.isRequired,
|
||||
gotoQueueNextPage: PropTypes.func.isRequired,
|
||||
@@ -168,6 +181,8 @@ QueueConnector.propTypes = {
|
||||
clearQueue: PropTypes.func.isRequired,
|
||||
grabQueueItems: PropTypes.func.isRequired,
|
||||
removeQueueItems: PropTypes.func.isRequired,
|
||||
fetchBooks: PropTypes.func.isRequired,
|
||||
clearBooks: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function QueueDetails(props) {
|
||||
const {
|
||||
@@ -24,7 +23,7 @@ function QueueDetails(props) {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.PENDING}
|
||||
title={translate('ReleaseWillBeProcessedInterp', [moment(estimatedCompletionTime).fromNow()])}
|
||||
title={`Release will be processed ${moment(estimatedCompletionTime).fromNow()}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -35,7 +34,7 @@ function QueueDetails(props) {
|
||||
<Icon
|
||||
name={icons.DOWNLOAD}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('ImportFailedInterp', [errorMessage])}
|
||||
title={`Import failed: ${errorMessage}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -48,7 +47,7 @@ function QueueDetails(props) {
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DownloadFailedInterp', [errorMessage])}
|
||||
title={`Download failed: ${errorMessage}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -58,7 +57,7 @@ function QueueDetails(props) {
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DownloadFailedCheckDownloadClientForMoreDetails')}
|
||||
title="Download failed: check download client for more details"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -68,7 +67,7 @@ function QueueDetails(props) {
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
kind={kinds.WARNING}
|
||||
title={translate('DownloadWarningCheckDownloadClientForMoreDetails')}
|
||||
title="Download warning: check download client for more details"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -77,7 +76,7 @@ function QueueDetails(props) {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
title={translate('BookIsDownloadingInterp', [progress.toFixed(1), title])}
|
||||
title={`Book is downloading - ${progress.toFixed(1)}% ${title}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
class QueueOptions extends Component {
|
||||
|
||||
@@ -42,7 +41,7 @@ class QueueOptions extends Component {
|
||||
[name]: value
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -55,15 +54,13 @@ class QueueOptions extends Component {
|
||||
return (
|
||||
<Fragment>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('ShowUnknownAuthorItems')}
|
||||
</FormLabel>
|
||||
<FormLabel>Show Unknown Author Items</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includeUnknownAuthorItems"
|
||||
value={includeUnknownAuthorItems}
|
||||
helpText={translate('IncludeUnknownAuthorItemsHelpText')}
|
||||
helpText="Show items without a author in the queue, this could include removed authors, movies or anything else in Readarr's category"
|
||||
onChange={this.onOptionChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
@@ -16,12 +16,6 @@
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.customFormatScore {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 55px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
|
||||
11
frontend/src/Activity/Queue/QueueRow.css.d.ts
vendored
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;
|
||||
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
|
||||
import AuthorNameLink from 'Author/AuthorNameLink';
|
||||
import BookFormats from 'Book/BookFormats';
|
||||
import BookQuality from 'Book/BookQuality';
|
||||
import BookTitleLink from 'Book/BookTitleLink';
|
||||
import Icon from 'Components/Icon';
|
||||
@@ -14,12 +13,8 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QueueStatusCell from './QueueStatusCell';
|
||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||
import TimeleftCell from './TimeleftCell';
|
||||
@@ -44,37 +39,24 @@ class QueueRow extends Component {
|
||||
|
||||
onRemoveQueueItemPress = () => {
|
||||
this.setState({ isRemoveQueueItemModalOpen: true });
|
||||
};
|
||||
|
||||
onRemoveQueueItemModalConfirmed = (blocklist, skipRedownload) => {
|
||||
const {
|
||||
onRemoveQueueItemPress,
|
||||
onQueueRowModalOpenOrClose
|
||||
} = this.props;
|
||||
|
||||
onQueueRowModalOpenOrClose(false);
|
||||
onRemoveQueueItemPress(blocklist, skipRedownload);
|
||||
}
|
||||
|
||||
onRemoveQueueItemModalConfirmed = (blacklist, skipredownload) => {
|
||||
this.props.onRemoveQueueItemPress(blacklist, skipredownload);
|
||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
onRemoveQueueItemModalClose = () => {
|
||||
this.props.onQueueRowModalOpenOrClose(false);
|
||||
|
||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
onInteractiveImportPress = () => {
|
||||
this.props.onQueueRowModalOpenOrClose(true);
|
||||
|
||||
this.setState({ isInteractiveImportModalOpen: true });
|
||||
};
|
||||
}
|
||||
|
||||
onInteractiveImportModalClose = () => {
|
||||
this.props.onQueueRowModalOpenOrClose(false);
|
||||
|
||||
this.setState({ isInteractiveImportModalOpen: false });
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -92,13 +74,10 @@ class QueueRow extends Component {
|
||||
author,
|
||||
book,
|
||||
quality,
|
||||
customFormats,
|
||||
customFormatScore,
|
||||
protocol,
|
||||
indexer,
|
||||
outputPath,
|
||||
downloadClient,
|
||||
downloadClientHasPostImportCategory,
|
||||
downloadForced,
|
||||
estimatedCompletionTime,
|
||||
timeleft,
|
||||
@@ -158,7 +137,7 @@ class QueueRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'authorMetadata.sortName') {
|
||||
if (name === 'authors.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{
|
||||
@@ -216,34 +195,6 @@ class QueueRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<BookFormats
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'customFormatScore') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.customFormatScore}
|
||||
>
|
||||
<Tooltip
|
||||
anchor={formatCustomFormatScore(
|
||||
customFormatScore,
|
||||
customFormats.length
|
||||
)}
|
||||
tooltip={<BookFormats formats={customFormats} />}
|
||||
position={tooltipPositions.BOTTOM}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'protocol') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
@@ -278,12 +229,6 @@ class QueueRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'size') {
|
||||
return (
|
||||
<TableRowCell key={name}>{formatBytes(size)}</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'outputPath') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
@@ -340,7 +285,7 @@ class QueueRow extends Component {
|
||||
kind={kinds.DANGER}
|
||||
/>
|
||||
}
|
||||
title={translate('ManualDownload')}
|
||||
title="Manual Download"
|
||||
body="This release failed parsing checks and was manually downloaded from an interactive search. Import is likely to fail."
|
||||
position={tooltipPositions.LEFT}
|
||||
/>
|
||||
@@ -365,7 +310,7 @@ class QueueRow extends Component {
|
||||
}
|
||||
|
||||
<SpinnerIconButton
|
||||
title={translate('RemoveFromQueue')}
|
||||
title="Remove from queue"
|
||||
name={icons.REMOVE}
|
||||
isSpinning={isRemoving}
|
||||
onPress={this.onRemoveQueueItemPress}
|
||||
@@ -390,9 +335,7 @@ class QueueRow extends Component {
|
||||
<RemoveQueueItemModal
|
||||
isOpen={isRemoveQueueItemModalOpen}
|
||||
sourceTitle={title}
|
||||
canChangeCategory={!!downloadClientHasPostImportCategory}
|
||||
canIgnore={!!author}
|
||||
isPending={isPending}
|
||||
canIgnore={!!(author && book)}
|
||||
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
||||
onModalClose={this.onRemoveQueueItemModalClose}
|
||||
/>
|
||||
@@ -414,13 +357,10 @@ QueueRow.propTypes = {
|
||||
author: PropTypes.object,
|
||||
book: PropTypes.object,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
||||
customFormatScore: PropTypes.number.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
indexer: PropTypes.string,
|
||||
outputPath: PropTypes.string,
|
||||
downloadClient: PropTypes.string,
|
||||
downloadClientHasPostImportCategory: PropTypes.bool,
|
||||
downloadForced: PropTypes.bool.isRequired,
|
||||
estimatedCompletionTime: PropTypes.string,
|
||||
timeleft: PropTypes.string,
|
||||
@@ -436,12 +376,10 @@ QueueRow.propTypes = {
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onSelectedChange: PropTypes.func.isRequired,
|
||||
onGrabPress: PropTypes.func.isRequired,
|
||||
onRemoveQueueItemPress: PropTypes.func.isRequired,
|
||||
onQueueRowModalOpenOrClose: PropTypes.func.isRequired
|
||||
onRemoveQueueItemPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
QueueRow.defaultProps = {
|
||||
customFormats: [],
|
||||
isGrabbing: false,
|
||||
isRemoving: false
|
||||
};
|
||||
|
||||
@@ -41,11 +41,11 @@ class QueueRowConnector extends Component {
|
||||
|
||||
onGrabPress = () => {
|
||||
this.props.grabQueueItem({ id: this.props.id });
|
||||
};
|
||||
}
|
||||
|
||||
onRemoveQueueItemPress = (payload) => {
|
||||
this.props.removeQueueItem({ id: this.props.id, ...payload });
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -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;
|
||||
@@ -4,7 +4,6 @@ import Icon from 'Components/Icon';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './QueueStatusCell.css';
|
||||
|
||||
function getDetailedPopoverBody(statusMessages) {
|
||||
@@ -50,7 +49,11 @@ function QueueStatusCell(props) {
|
||||
// status === 'downloading'
|
||||
let iconName = icons.DOWNLOADING;
|
||||
let iconKind = kinds.DEFAULT;
|
||||
let title = translate('Title');
|
||||
let title = 'Downloading';
|
||||
|
||||
if (hasWarning) {
|
||||
iconKind = kinds.WARNING;
|
||||
}
|
||||
|
||||
if (status === 'paused') {
|
||||
iconName = icons.PAUSED;
|
||||
@@ -68,24 +71,17 @@ function QueueStatusCell(props) {
|
||||
|
||||
if (trackedDownloadState === 'importPending') {
|
||||
title += ' - Waiting to Import';
|
||||
iconKind = kinds.PURPLE;
|
||||
}
|
||||
|
||||
if (trackedDownloadState === 'importing') {
|
||||
title += ' - Importing';
|
||||
iconKind = kinds.PURPLE;
|
||||
}
|
||||
|
||||
if (trackedDownloadState === 'failedPending') {
|
||||
title += ' - Waiting to Process';
|
||||
iconKind = kinds.DANGER;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasWarning) {
|
||||
iconKind = kinds.WARNING;
|
||||
}
|
||||
|
||||
if (status === 'delay') {
|
||||
iconName = icons.PENDING;
|
||||
title = 'Pending';
|
||||
|
||||
@@ -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;
|
||||
164
frontend/src/Activity/Queue/RemoveQueueItemModal.js
Normal file
164
frontend/src/Activity/Queue/RemoveQueueItemModal.js
Normal file
@@ -0,0 +1,164 @@
|
||||
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';
|
||||
|
||||
class RemoveQueueItemModal extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
remove: true,
|
||||
blacklist: false,
|
||||
skipredownload: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
resetState = function() {
|
||||
this.setState({
|
||||
remove: true,
|
||||
blacklist: false,
|
||||
skipredownload: false
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onRemoveChange = ({ value }) => {
|
||||
this.setState({ remove: value });
|
||||
}
|
||||
|
||||
onBlacklistChange = ({ value }) => {
|
||||
this.setState({ blacklist: value });
|
||||
}
|
||||
|
||||
onSkipReDownloadChange = ({ value }) => {
|
||||
this.setState({ skipredownload: value });
|
||||
}
|
||||
|
||||
onRemoveConfirmed = () => {
|
||||
const state = this.state;
|
||||
|
||||
this.resetState();
|
||||
this.props.onRemovePress(state);
|
||||
}
|
||||
|
||||
onModalClose = () => {
|
||||
this.resetState();
|
||||
this.props.onModalClose();
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isOpen,
|
||||
sourceTitle,
|
||||
canIgnore
|
||||
} = this.props;
|
||||
|
||||
const { remove, blacklist, skipredownload } = this.state;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
size={sizes.MEDIUM}
|
||||
onModalClose={this.onModalClose}
|
||||
>
|
||||
<ModalContent
|
||||
onModalClose={this.onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
Remove - {sourceTitle}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div>
|
||||
Are you sure you want to remove '{sourceTitle}' from the queue?
|
||||
</div>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Remove From Download Client</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="remove"
|
||||
value={remove}
|
||||
helpTextWarning="Removing will remove the download and the file(s) from the download client."
|
||||
isDisabled={!canIgnore}
|
||||
onChange={this.onRemoveChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Blacklist Release</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="blacklist"
|
||||
value={blacklist}
|
||||
helpText="Prevents Readarr from automatically grabbing this release again"
|
||||
onChange={this.onBlacklistChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{
|
||||
blacklist &&
|
||||
<FormGroup>
|
||||
<FormLabel>Skip Redownload</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="skipredownload"
|
||||
value={skipredownload}
|
||||
helpText="Prevents Readarr from trying download an alternative release for this item"
|
||||
onChange={this.onSkipReDownloadChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
}
|
||||
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={this.onModalClose}>
|
||||
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,
|
||||
onRemovePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default RemoveQueueItemModal;
|
||||
@@ -1,230 +0,0 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import Button from 'Components/Link/Button';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './RemoveQueueItemModal.css';
|
||||
|
||||
interface RemovePressProps {
|
||||
remove: boolean;
|
||||
changeCategory: boolean;
|
||||
blocklist: boolean;
|
||||
skipRedownload: boolean;
|
||||
}
|
||||
|
||||
interface RemoveQueueItemModalProps {
|
||||
isOpen: boolean;
|
||||
sourceTitle: string;
|
||||
canChangeCategory: boolean;
|
||||
canIgnore: boolean;
|
||||
isPending: boolean;
|
||||
selectedCount?: number;
|
||||
onRemovePress(props: RemovePressProps): void;
|
||||
onModalClose: () => void;
|
||||
}
|
||||
|
||||
type RemovalMethod = 'removeFromClient' | 'changeCategory' | 'ignore';
|
||||
type BlocklistMethod =
|
||||
| 'doNotBlocklist'
|
||||
| 'blocklistAndSearch'
|
||||
| 'blocklistOnly';
|
||||
|
||||
function RemoveQueueItemModal(props: RemoveQueueItemModalProps) {
|
||||
const {
|
||||
isOpen,
|
||||
sourceTitle,
|
||||
canIgnore,
|
||||
canChangeCategory,
|
||||
isPending,
|
||||
selectedCount,
|
||||
onRemovePress,
|
||||
onModalClose,
|
||||
} = props;
|
||||
|
||||
const multipleSelected = selectedCount && selectedCount > 1;
|
||||
|
||||
const [removalMethod, setRemovalMethod] =
|
||||
useState<RemovalMethod>('removeFromClient');
|
||||
const [blocklistMethod, setBlocklistMethod] =
|
||||
useState<BlocklistMethod>('doNotBlocklist');
|
||||
|
||||
const { title, message } = useMemo(() => {
|
||||
if (!selectedCount) {
|
||||
return {
|
||||
title: translate('RemoveQueueItem', { sourceTitle }),
|
||||
message: translate('RemoveQueueItemConfirmation', { sourceTitle }),
|
||||
};
|
||||
}
|
||||
|
||||
if (selectedCount === 1) {
|
||||
return {
|
||||
title: translate('RemoveSelectedItem'),
|
||||
message: translate('RemoveSelectedItemQueueMessageText'),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
title: translate('RemoveSelectedItems'),
|
||||
message: translate('RemoveSelectedItemsQueueMessageText', {
|
||||
selectedCount,
|
||||
}),
|
||||
};
|
||||
}, [sourceTitle, selectedCount]);
|
||||
|
||||
const removalMethodOptions = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
key: 'removeFromClient',
|
||||
value: translate('RemoveFromDownloadClient'),
|
||||
hint: multipleSelected
|
||||
? translate('RemoveMultipleFromDownloadClientHint')
|
||||
: translate('RemoveFromDownloadClientHint'),
|
||||
},
|
||||
{
|
||||
key: 'changeCategory',
|
||||
value: translate('ChangeCategory'),
|
||||
isDisabled: !canChangeCategory,
|
||||
hint: multipleSelected
|
||||
? translate('ChangeCategoryMultipleHint')
|
||||
: translate('ChangeCategoryHint'),
|
||||
},
|
||||
{
|
||||
key: 'ignore',
|
||||
value: multipleSelected
|
||||
? translate('IgnoreDownloads')
|
||||
: translate('IgnoreDownload'),
|
||||
isDisabled: !canIgnore,
|
||||
hint: multipleSelected
|
||||
? translate('IgnoreDownloadsHint')
|
||||
: translate('IgnoreDownloadHint'),
|
||||
},
|
||||
];
|
||||
}, [canChangeCategory, canIgnore, multipleSelected]);
|
||||
|
||||
const blocklistMethodOptions = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
key: 'doNotBlocklist',
|
||||
value: translate('DoNotBlocklist'),
|
||||
hint: translate('DoNotBlocklistHint'),
|
||||
},
|
||||
{
|
||||
key: 'blocklistAndSearch',
|
||||
value: translate('BlocklistAndSearch'),
|
||||
hint: multipleSelected
|
||||
? translate('BlocklistAndSearchMultipleHint')
|
||||
: translate('BlocklistAndSearchHint'),
|
||||
},
|
||||
{
|
||||
key: 'blocklistOnly',
|
||||
value: translate('BlocklistOnly'),
|
||||
hint: multipleSelected
|
||||
? translate('BlocklistMultipleOnlyHint')
|
||||
: translate('BlocklistOnlyHint'),
|
||||
},
|
||||
];
|
||||
}, [multipleSelected]);
|
||||
|
||||
const handleRemovalMethodChange = useCallback(
|
||||
({ value }: { value: RemovalMethod }) => {
|
||||
setRemovalMethod(value);
|
||||
},
|
||||
[setRemovalMethod]
|
||||
);
|
||||
|
||||
const handleBlocklistMethodChange = useCallback(
|
||||
({ value }: { value: BlocklistMethod }) => {
|
||||
setBlocklistMethod(value);
|
||||
},
|
||||
[setBlocklistMethod]
|
||||
);
|
||||
|
||||
const handleConfirmRemove = useCallback(() => {
|
||||
onRemovePress({
|
||||
remove: removalMethod === 'removeFromClient',
|
||||
changeCategory: removalMethod === 'changeCategory',
|
||||
blocklist: blocklistMethod !== 'doNotBlocklist',
|
||||
skipRedownload: blocklistMethod === 'blocklistOnly',
|
||||
});
|
||||
|
||||
setRemovalMethod('removeFromClient');
|
||||
setBlocklistMethod('doNotBlocklist');
|
||||
}, [
|
||||
removalMethod,
|
||||
blocklistMethod,
|
||||
setRemovalMethod,
|
||||
setBlocklistMethod,
|
||||
onRemovePress,
|
||||
]);
|
||||
|
||||
const handleModalClose = useCallback(() => {
|
||||
setRemovalMethod('removeFromClient');
|
||||
setBlocklistMethod('doNotBlocklist');
|
||||
|
||||
onModalClose();
|
||||
}, [setRemovalMethod, setBlocklistMethod, onModalClose]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={handleModalClose}>
|
||||
<ModalContent onModalClose={handleModalClose}>
|
||||
<ModalHeader>{title}</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div className={styles.message}>{message}</div>
|
||||
|
||||
{isPending ? null : (
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('RemoveQueueItemRemovalMethod')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="removalMethod"
|
||||
value={removalMethod}
|
||||
values={removalMethodOptions}
|
||||
isDisabled={!canChangeCategory && !canIgnore}
|
||||
helpTextWarning={translate(
|
||||
'RemoveQueueItemRemovalMethodHelpTextWarning'
|
||||
)}
|
||||
onChange={handleRemovalMethodChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{multipleSelected
|
||||
? translate('BlocklistReleases')
|
||||
: translate('BlocklistRelease')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="blocklistMethod"
|
||||
value={blocklistMethod}
|
||||
values={blocklistMethodOptions}
|
||||
helpText={translate('BlocklistReleaseHelpText')}
|
||||
onChange={handleBlocklistMethodChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={handleModalClose}>{translate('Close')}</Button>
|
||||
|
||||
<Button kind={kinds.DANGER} onPress={handleConfirmRemove}>
|
||||
{translate('Remove')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default RemoveQueueItemModal;
|
||||
167
frontend/src/Activity/Queue/RemoveQueueItemsModal.js
Normal file
167
frontend/src/Activity/Queue/RemoveQueueItemsModal.js
Normal file
@@ -0,0 +1,167 @@
|
||||
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 styles from './RemoveQueueItemsModal.css';
|
||||
|
||||
class RemoveQueueItemsModal extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
remove: true,
|
||||
blacklist: false,
|
||||
skipredownload: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
resetState = function() {
|
||||
this.setState({
|
||||
remove: true,
|
||||
blacklist: false,
|
||||
skipredownload: false
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onRemoveChange = ({ value }) => {
|
||||
this.setState({ remove: value });
|
||||
}
|
||||
|
||||
onBlacklistChange = ({ value }) => {
|
||||
this.setState({ blacklist: value });
|
||||
}
|
||||
|
||||
onSkipReDownloadChange = ({ value }) => {
|
||||
this.setState({ skipredownload: value });
|
||||
}
|
||||
|
||||
onRemoveConfirmed = () => {
|
||||
const state = this.state;
|
||||
|
||||
this.resetState();
|
||||
this.props.onRemovePress(state);
|
||||
}
|
||||
|
||||
onModalClose = () => {
|
||||
this.resetState();
|
||||
this.props.onModalClose();
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isOpen,
|
||||
selectedCount,
|
||||
canIgnore
|
||||
} = this.props;
|
||||
|
||||
const { remove, blacklist, skipredownload } = this.state;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
size={sizes.MEDIUM}
|
||||
onModalClose={this.onModalClose}
|
||||
>
|
||||
<ModalContent
|
||||
onModalClose={this.onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
Remove Selected Item{selectedCount > 1 ? 's' : ''}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div className={styles.message}>
|
||||
Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue?
|
||||
</div>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Remove From Download Client</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="remove"
|
||||
value={remove}
|
||||
helpTextWarning="Removing will remove the download and the file(s) from the download client."
|
||||
isDisabled={!canIgnore}
|
||||
onChange={this.onRemoveChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
Blacklist Release{selectedCount > 1 ? 's' : ''}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="blacklist"
|
||||
value={blacklist}
|
||||
helpText="Prevents Readarr from automatically grabbing these files again"
|
||||
onChange={this.onBlacklistChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{
|
||||
blacklist &&
|
||||
<FormGroup>
|
||||
<FormLabel>Skip Redownload</FormLabel>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="skipredownload"
|
||||
value={skipredownload}
|
||||
helpText="Prevents Readarr from trying download alternative releases for the removed items"
|
||||
onChange={this.onSkipReDownloadChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
}
|
||||
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={this.onModalClose}>
|
||||
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,
|
||||
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;
|
||||
@@ -5,7 +5,6 @@ import formatTime from 'Utilities/Date/formatTime';
|
||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './TimeleftCell.css';
|
||||
|
||||
function TimeleftCell(props) {
|
||||
@@ -20,35 +19,35 @@ function TimeleftCell(props) {
|
||||
timeFormat
|
||||
} = props;
|
||||
|
||||
if (status === 'delay') {
|
||||
if (status === 'Delay') {
|
||||
const date = getRelativeDate(estimatedCompletionTime, shortDateFormat, showRelativeDates);
|
||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||
|
||||
return (
|
||||
<TableRowCell
|
||||
className={styles.timeleft}
|
||||
title={translate('DelayingDownloadUntilInterp', [date, time])}
|
||||
title={`Delaying download until ${date} at ${time}`}
|
||||
>
|
||||
-
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === 'downloadClientUnavailable') {
|
||||
if (status === 'DownloadClientUnavailable') {
|
||||
const date = getRelativeDate(estimatedCompletionTime, shortDateFormat, showRelativeDates);
|
||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||
|
||||
return (
|
||||
<TableRowCell
|
||||
className={styles.timeleft}
|
||||
title={translate('RetryingDownloadInterp', [date, time])}
|
||||
title={`Retrying download ${date} at ${time}`}
|
||||
>
|
||||
-
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (!timeleft || status === 'completed' || status === 'failed') {
|
||||
if (!timeleft) {
|
||||
return (
|
||||
<TableRowCell className={styles.timeleft}>
|
||||
-
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import React from 'react';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function AuthorMonitorNewItemsOptionsPopoverContent() {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('AllBooks')}
|
||||
data={translate('DataNewAllBooks')}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('NewBooks')}
|
||||
data={translate('DataNewBooks')}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('None')}
|
||||
data={translate('DataNewNone')}
|
||||
/>
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthorMonitorNewItemsOptionsPopoverContent;
|
||||
@@ -1,52 +1,45 @@
|
||||
import React from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function AuthorMonitoringOptionsPopoverContent() {
|
||||
return (
|
||||
<>
|
||||
<Alert>
|
||||
{translate('MonitoringOptionsHelpText')}
|
||||
</Alert>
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title={translate('AllBooks')}
|
||||
data={translate('DataAllBooks')}
|
||||
/>
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title="All Books"
|
||||
data="Monitor all books"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('FutureBooks')}
|
||||
data={translate('DataFutureBooks')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title="Future Books"
|
||||
data="Monitor books that have not released yet"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('MissingBooks')}
|
||||
data={translate('DataMissingBooks')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title="Missing Books"
|
||||
data="Monitor books that do not have files or have not released yet"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('ExistingBooks')}
|
||||
data={translate('DataExistingBooks')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title="Existing Books"
|
||||
data="Monitor books that have files or have not released yet"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('FirstBook')}
|
||||
data={translate('DataFirstBook')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title="First Book"
|
||||
data="Monitor the first book. All other books will be ignored"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('LatestBook')}
|
||||
data={translate('DataLatestBook')}
|
||||
/>
|
||||
<DescriptionListItem
|
||||
title="Latest Book"
|
||||
data="Monitor the latest book and future books"
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title={translate('None')}
|
||||
data={translate('DataNone')}
|
||||
/>
|
||||
</DescriptionList>
|
||||
</>
|
||||
<DescriptionListItem
|
||||
title="None"
|
||||
data="No books will be monitored"
|
||||
/>
|
||||
</DescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user