mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-18 21:35:51 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6df45eb6af |
-213
@@ -2,12 +2,6 @@
|
|||||||
# editorconfig.org
|
# editorconfig.org
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
# NOTE: Requires **VS2019 16.3** or later
|
|
||||||
|
|
||||||
# Stylecop.ruleset
|
|
||||||
# Description: Rules for Radarr
|
|
||||||
|
|
||||||
# Code files
|
|
||||||
[*.cs]
|
[*.cs]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
@@ -44,213 +38,6 @@ csharp_style_var_for_built_in_types = true:suggestion
|
|||||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||||
csharp_style_var_elsewhere = true:suggestion
|
csharp_style_var_elsewhere = true:suggestion
|
||||||
|
|
||||||
# Stylecop Rules
|
|
||||||
dotnet_diagnostic.SA0001.severity = none
|
|
||||||
dotnet_diagnostic.SA1005.severity = none
|
|
||||||
dotnet_diagnostic.SA1025.severity = none
|
|
||||||
dotnet_diagnostic.SA1101.severity = none
|
|
||||||
dotnet_diagnostic.SA1116.severity = none
|
|
||||||
dotnet_diagnostic.SA1118.severity = none
|
|
||||||
dotnet_diagnostic.SA1122.severity = none
|
|
||||||
dotnet_diagnostic.SA1201.severity = suggestion
|
|
||||||
dotnet_diagnostic.SA1202.severity = suggestion
|
|
||||||
dotnet_diagnostic.SA1204.severity = suggestion
|
|
||||||
dotnet_diagnostic.SA1300.severity = none
|
|
||||||
dotnet_diagnostic.SA1303.severity = none
|
|
||||||
dotnet_diagnostic.SA1304.severity = none
|
|
||||||
dotnet_diagnostic.SA1306.severity = none
|
|
||||||
dotnet_diagnostic.SA1309.severity = none
|
|
||||||
dotnet_diagnostic.SA1310.severity = none
|
|
||||||
dotnet_diagnostic.SA1401.severity = none
|
|
||||||
dotnet_diagnostic.SA1402.severity = none
|
|
||||||
dotnet_diagnostic.SA1404.severity = suggestion
|
|
||||||
dotnet_diagnostic.SA1405.severity = suggestion
|
|
||||||
dotnet_diagnostic.SA1406.severity = suggestion
|
|
||||||
dotnet_diagnostic.SA1410.severity = suggestion
|
|
||||||
dotnet_diagnostic.SA1411.severity = suggestion
|
|
||||||
dotnet_diagnostic.SA1413.severity = none
|
|
||||||
dotnet_diagnostic.SA1516.severity = none
|
|
||||||
dotnet_diagnostic.SA1600.severity = none
|
|
||||||
dotnet_diagnostic.SA1601.severity = none
|
|
||||||
dotnet_diagnostic.SA1602.severity = none
|
|
||||||
dotnet_diagnostic.SA1604.severity = none
|
|
||||||
dotnet_diagnostic.SA1605.severity = none
|
|
||||||
dotnet_diagnostic.SA1606.severity = none
|
|
||||||
dotnet_diagnostic.SA1607.severity = none
|
|
||||||
dotnet_diagnostic.SA1608.severity = none
|
|
||||||
dotnet_diagnostic.SA1610.severity = none
|
|
||||||
dotnet_diagnostic.SA1611.severity = none
|
|
||||||
dotnet_diagnostic.SA1612.severity = none
|
|
||||||
dotnet_diagnostic.SA1613.severity = none
|
|
||||||
dotnet_diagnostic.SA1614.severity = none
|
|
||||||
dotnet_diagnostic.SA1615.severity = none
|
|
||||||
dotnet_diagnostic.SA1616.severity = none
|
|
||||||
dotnet_diagnostic.SA1617.severity = none
|
|
||||||
dotnet_diagnostic.SA1618.severity = none
|
|
||||||
dotnet_diagnostic.SA1619.severity = none
|
|
||||||
dotnet_diagnostic.SA1620.severity = none
|
|
||||||
dotnet_diagnostic.SA1621.severity = none
|
|
||||||
dotnet_diagnostic.SA1622.severity = none
|
|
||||||
dotnet_diagnostic.SA1623.severity = none
|
|
||||||
dotnet_diagnostic.SA1624.severity = none
|
|
||||||
dotnet_diagnostic.SA1625.severity = none
|
|
||||||
dotnet_diagnostic.SA1626.severity = none
|
|
||||||
dotnet_diagnostic.SA1627.severity = none
|
|
||||||
dotnet_diagnostic.SA1629.severity = none
|
|
||||||
dotnet_diagnostic.SA1633.severity = none
|
|
||||||
dotnet_diagnostic.SA1634.severity = none
|
|
||||||
dotnet_diagnostic.SA1635.severity = none
|
|
||||||
dotnet_diagnostic.SA1636.severity = none
|
|
||||||
dotnet_diagnostic.SA1637.severity = none
|
|
||||||
dotnet_diagnostic.SA1638.severity = none
|
|
||||||
dotnet_diagnostic.SA1640.severity = none
|
|
||||||
dotnet_diagnostic.SA1641.severity = none
|
|
||||||
dotnet_diagnostic.SA1642.severity = none
|
|
||||||
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.CA1003.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1008.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1010.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1012.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1014.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1016.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1017.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1018.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1019.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1021.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1024.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1027.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1028.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1030.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1031.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1032.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1033.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1034.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1036.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1040.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1041.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1043.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1044.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1050.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1051.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1052.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1054.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1055.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1056.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1058.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1060.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1061.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1062.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1063.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1064.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1065.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1066.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1067.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1068.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1069.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1200.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1303.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1304.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1305.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1307.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1308.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1401.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1507.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1707.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1710.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1712.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1714.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1715.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1716.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1717.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1720.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1721.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1724.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.CA1814.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1815.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1816.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1819.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1822.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1823.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA1824.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2000.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2002.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2007.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2008.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2009.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2010.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2011.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2012.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2013.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2100.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2101.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2119.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2153.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2200.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2207.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2208.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2211.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2213.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2214.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2215.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2216.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2219.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2225.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2226.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2227.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2229.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2231.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2234.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2235.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2237.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2241.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2242.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2243.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2244.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2245.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA2246.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA3061.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA3075.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA3076.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA3077.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA3147.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5350.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5351.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5359.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5360.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5363.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5364.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5365.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5366.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5368.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5369.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5370.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5371.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5372.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5373.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5374.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5379.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5384.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5385.severity = suggestion
|
|
||||||
dotnet_diagnostic.CA5397.severity = suggestion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[*.{js,html,js,hbs,less,css}]
|
[*.{js,html,js,hbs,less,css}]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
**Just because you receive an exception in your logs, doesn't mean it's a bug and should be reported here. Often it's something else, such as a permission error. If you are unsure ask on the Discord or Subreddit first.**
|
**Just because you receive an exception in your logs, doesn't mean it's a bug and should be reported here. Often it's something else, such as a permission error. If you are unsure ask on the Discord or Subreddit first.**
|
||||||
|
|
||||||
Visit our [Discord server](https://discord.gg/r5wJPt9) or [Subreddit](https://reddit.com/r/radarr) for support or longer discussions. Support questions posed on here will be closed immediately.
|
Visit our [Discord server](https://discord.gg/NWYch8M) or [Subreddit](https://reddit.com/r/radarr) for support or longer discussions. Support questions posed on here will be closed immediately.
|
||||||
|
|
||||||
Provide a description of the feature request or bug here, the more details the better.
|
Provide a description of the feature request or bug here, the more details the better.
|
||||||
Please also include the following if you are reporting a bug. If you do not include it, the issue will probably be closed as we cannot help you. -->
|
Please also include the following if you are reporting a bug. If you do not include it, the issue will probably be closed as we cannot help you. -->
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Support via Discord
|
- name: Support via Discord
|
||||||
url: https://discord.gg/r5wJPt9
|
url: https://discord.gg/AD3UP37
|
||||||
about: Chat with users and devs on support and setup related topics.
|
about: Chat with users and devs on support and setup related topics.
|
||||||
- name: Support via Reddit
|
- name: Support via Reddit
|
||||||
url: https://reddit.com/r/radarr
|
url: https://reddit.com/r/radarr
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ YES | NO
|
|||||||
|
|
||||||
#### Todos
|
#### Todos
|
||||||
- [ ] Tests
|
- [ ] Tests
|
||||||
- [ ] Translation Keys
|
|
||||||
|
|
||||||
#### Issues Fixed or Closed by this PR
|
#### Issues Fixed or Closed by this PR
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -7,10 +7,7 @@ exemptLabels:
|
|||||||
- feature request
|
- feature request
|
||||||
- parser
|
- parser
|
||||||
- confirmed
|
- confirmed
|
||||||
- sonarr-pull
|
- aphrodite
|
||||||
- lidarr-pull
|
|
||||||
- readarr-pull
|
|
||||||
- v3
|
|
||||||
# Label to use when marking an issue as stale
|
# Label to use when marking an issue as stale
|
||||||
staleLabel: stale
|
staleLabel: stale
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ supportLabel: support
|
|||||||
# to a support page, or set to `false` to disable
|
# to a support page, or set to `false` to disable
|
||||||
supportComment: >
|
supportComment: >
|
||||||
We use the issue tracker exclusively for bug reports and feature requests.
|
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://discord.gg/r5wJPt9) or [Subreddit](https://reddit.com/r/radarr)
|
However, this issue appears to be a support request. Please hop over onto our [Discord](https://discord.gg/ZDmT7qb) or [Subreddit](https://reddit.com/r/radarr)
|
||||||
# Whether to close issues marked as support requests
|
# Whether to close issues marked as support requests
|
||||||
close: true
|
close: true
|
||||||
# Whether to lock issues marked as support requests
|
# Whether to lock issues marked as support requests
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ See the readme for information on setting up your development environment.
|
|||||||
- Rebase from Radarr's develop branch, don't merge
|
- Rebase from Radarr's develop branch, don't merge
|
||||||
- Make meaningful commits, or squash them
|
- 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
|
- 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
|
- Reach out to us on the forums or on IRC if you have any questions
|
||||||
- Add tests (unit/integration)
|
- Add tests (unit/integration)
|
||||||
- Commit with *nix line endings for consistency (We checkout Windows and commit *nix)
|
- 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
|
- One feature/bug fix per pull request to keep things clean and easy to understand
|
||||||
|
|||||||
@@ -13,34 +13,28 @@ The project was inspired by other Usenet/BitTorrent movie downloaders such as Co
|
|||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
[](https://github.com/Radarr/Radarr/wiki/Installation)
|
[](https://github.com/Radarr/Radarr/wiki/Installation)
|
||||||
[](https://github.com/Radarr/Radarr/wiki/Docker)
|
[](https://github.com/Radarr/Radarr/wiki/Docker)
|
||||||
[](https://github.com/Radarr/Radarr/wiki/Setup-Guide)
|
[](https://github.com/Radarr/Radarr/wiki/Setup-Guide)
|
||||||
[](https://github.com/Radarr/Radarr/wiki/FAQ)
|
[](https://github.com/Radarr/Radarr/wiki/FAQ)
|
||||||
|
|
||||||
|
|
||||||
If you are using Docker please ensure your Docker paths are setup correctly using [this guide to facilitate](https://old.reddit.com/r/usenet/wiki/docker) hardlinks and minimize permissions issues.
|
|
||||||
|
|
||||||
* [Install Radarr for your desired OS](https://github.com/Radarr/Radarr/wiki/Installation) *or* use [Docker](https://github.com/Radarr/Radarr/wiki/Docker)
|
* [Install Radarr for your desired OS](https://github.com/Radarr/Radarr/wiki/Installation) *or* use [Docker](https://github.com/Radarr/Radarr/wiki/Docker)
|
||||||
* *For Linux users*, run `radarr` and *optionally* have [Radarr start automatically](https://github.com/Radarr/Radarr/wiki/Autostart-on-Linux)
|
* *For Linux users*, run `radarr` and *optionally* have [Radarr start automatically](https://github.com/Radarr/Radarr/wiki/Autostart-on-Linux)
|
||||||
* Connect to the UI through <http://localhost:7878> or <http://your-ip:7878> in your web browser
|
* Connect to the UI through <http://localhost:7878> or <http://your-ip:7878> in your web browser
|
||||||
* See the [Setup Guide](https://github.com/Radarr/Radarr/wiki/Setup-Guide) for further configuration
|
* See the [Setup Guide](https://github.com/Radarr/Radarr/wiki/Setup-Guide) for further configuration
|
||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
Please note that v0.2 will only have critical bugs resolved as of August 2020. Any additional development or features will be soley in V3.
|
|
||||||
|
|
||||||
Each push to the "develop" branch creates a build on "nightly" release channel (release channel is the "branch" within radarr's settings), once we push a build to Github it will show up on "develop" release channel.
|
| Release Type | Branch: develop (stable) | Branch: nightly (semi-unstable) | Branch: aphrodite (very-unstable) |
|
||||||
|
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| Binary Releases | [](https://github.com/Radarr/Radarr/releases) | [](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts) | |
|
||||||
| Release Channel Type | Branch: develop (stable) (v0.2) | Branch: nightly (semi-unstable) (v3.0) |
|
| Docker | [](https://hub.docker.com/r/linuxserver/radarr) | [](https://hub.docker.com/r/linuxserver/radarr) | [](https://hub.docker.com/r/linuxserver/radarr) |
|
||||||
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
| Docker | [](https://hub.docker.com/r/hotio/radarr) | [](https://hub.docker.com/r/hotio/radarr) | [](https://hub.docker.com/r/hotio/radarr) |
|
||||||
| Binary Releases | [](https://github.com/Radarr/Radarr/releases) | [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=windows&runtime=netcore&arch=x64) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=x64) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm64) [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm) <br> [](https://radarr.servarr.com/v1/update/nightly/updatefile?os=osx&runtime=netcore&arch=x64)
|
|
||||||
| Docker - lsio | [](https://hub.docker.com/r/linuxserver/radarr) | [](https://hub.docker.com/r/linuxserver/radarr) |
|
|
||||||
| Docker - hotio | [](https://hub.docker.com/r/hotio/radarr) | [](https://hub.docker.com/r/hotio/radarr) |
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
[](https://discord.gg/r5wJPt9)
|
[](https://discord.gg/AD3UP37)
|
||||||
[](https://www.reddit.com/r/radarr)
|
[](https://www.reddit.com/r/radarr)
|
||||||
|
[](http://feathub.com/Radarr/Radarr)
|
||||||
[](https://github.com/Radarr/Radarr/issues)
|
[](https://github.com/Radarr/Radarr/issues)
|
||||||
[](https://github.com/Radarr/Radarr/wiki)
|
[](https://github.com/Radarr/Radarr/wiki)
|
||||||
|
|
||||||
@@ -78,14 +72,18 @@ Radarr is currently undergoing rapid development and pull requests are actively
|
|||||||
* New TorrentPotato Indexer
|
* New TorrentPotato Indexer
|
||||||
* Torznab Indexer now supports Movies (Works well with [Jackett](https://github.com/Jackett/Jackett))
|
* Torznab Indexer now supports Movies (Works well with [Jackett](https://github.com/Jackett/Jackett))
|
||||||
* Scanning PreDB to know when a new release is available
|
* Scanning PreDB to know when a new release is available
|
||||||
* Importing movies from various online sources, such as IMDb Watchlists or Trakt (v3) (A complete list can be found [here](https://github.com/Radarr/Radarr/issues/114))
|
* Importing movies from various online sources, such as IMDb Watchlists (A complete list can be found [here](https://github.com/Radarr/Radarr/issues/114))
|
||||||
* Full integration with Kodi, Plex (notification, library update)
|
* Full integration with Kodi, Plex (notification, library update)
|
||||||
* And a new beautiful UI (v3)
|
* And a beautiful UI
|
||||||
* Importing Metadata such as trailers or subtitles
|
* Importing Metadata such as trailers or subtitles
|
||||||
* Adding metadata such as posters and information for Kodi and others to use
|
* Adding metadata such as posters and information for Kodi and others to use
|
||||||
* Advanced customization for profiles, such that Radarr will always download the copy you want
|
* Advanced customization for profiles, such that Radarr will always download the copy you want
|
||||||
|
|
||||||
#### [Feature Requests](https://github.com/Radarr/Radarr/issues/new?assignees=&labels=feature+request&template=feature_request.md&title=)
|
### Planned Features
|
||||||
|
|
||||||
|
See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/11/roadmap-update.html) for an overview of planned features.
|
||||||
|
|
||||||
|
#### [Feature Requests](http://feathub.com/Radarr/Radarr)
|
||||||
|
|
||||||
## Configuring the Development Environment
|
## Configuring the Development Environment
|
||||||
|
|
||||||
|
|||||||
+41
-60
@@ -13,17 +13,17 @@ variables:
|
|||||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||||
sentryOrg: 'servarr'
|
sentryOrg: 'servarr'
|
||||||
sentryUrl: 'https://sentry.servarr.com'
|
sentryUrl: 'https://sentry.servarr.com'
|
||||||
dotnetVersion: '3.1.401'
|
dotnetVersion: '3.1.302'
|
||||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
- develop
|
- develop
|
||||||
- master
|
- aphrodite
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
- develop
|
- develop
|
||||||
|
- aphrodite
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- stage: Setup
|
- stage: Setup
|
||||||
@@ -39,7 +39,7 @@ stages:
|
|||||||
displayName: Set Build Name
|
displayName: Set Build Name
|
||||||
- bash: |
|
- bash: |
|
||||||
if [[ $BUILD_REASON == "PullRequest" ]]; then
|
if [[ $BUILD_REASON == "PullRequest" ]]; then
|
||||||
git diff origin/develop...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
|
git diff origin/aphrodite...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
|
||||||
echo $? > not_backend_update
|
echo $? > not_backend_update
|
||||||
else
|
else
|
||||||
echo 0 > not_backend_update
|
echo 0 > not_backend_update
|
||||||
@@ -68,9 +68,6 @@ stages:
|
|||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
variables:
|
|
||||||
# Disable stylecop here - linting errors get caught by the analyze task
|
|
||||||
EnableAnalyzers: 'false'
|
|
||||||
steps:
|
steps:
|
||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
@@ -139,19 +136,10 @@ stages:
|
|||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
- task: Cache@2
|
|
||||||
inputs:
|
|
||||||
key: 'yarn | "$(osName)" | yarn.lock'
|
|
||||||
restoreKeys: |
|
|
||||||
yarn | "$(osName)"
|
|
||||||
yarn
|
|
||||||
path: $(yarnCacheFolder)
|
|
||||||
displayName: Cache Yarn packages
|
|
||||||
- bash: ./build.sh --frontend
|
- bash: ./build.sh --frontend
|
||||||
displayName: Build Radarr Frontend
|
displayName: Build Radarr Frontend
|
||||||
env:
|
env:
|
||||||
FORCE_COLOR: 0
|
FORCE_COLOR: 0
|
||||||
YARN_CACHE_FOLDER: $(yarnCacheFolder)
|
|
||||||
- publish: $(outputFolder)
|
- publish: $(outputFolder)
|
||||||
artifact: '$(osName)Frontend'
|
artifact: '$(osName)Frontend'
|
||||||
displayName: Publish Frontend
|
displayName: Publish Frontend
|
||||||
@@ -301,22 +289,14 @@ stages:
|
|||||||
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
|
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
|
||||||
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
||||||
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
||||||
if [[ ${BUILD_SOURCEBRANCH} == "refs/heads/develop" ]]; then
|
sentry-cli releases deploys "${RELEASENAME}" new -e aphrodite
|
||||||
sentry-cli releases deploys "${RELEASENAME}" new -e nightly
|
|
||||||
else
|
|
||||||
sentry-cli releases deploys "${RELEASENAME}" new -e production
|
|
||||||
fi
|
|
||||||
if [ $? -gt 0 ]; then
|
if [ $? -gt 0 ]; then
|
||||||
echo "##vso[task.logissue type=warning]Error uploading source maps."
|
echo "##vso[task.logissue type=warning]Error uploading source maps."
|
||||||
fi
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
displayName: Publish Sentry Source Maps
|
displayName: Publish Sentry Source Maps
|
||||||
condition: |
|
continueOnError: true
|
||||||
or
|
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/aphrodite'))
|
||||||
(
|
|
||||||
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
|
|
||||||
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
|
||||||
)
|
|
||||||
env:
|
env:
|
||||||
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
||||||
SENTRY_ORG: $(sentryOrg)
|
SENTRY_ORG: $(sentryOrg)
|
||||||
@@ -386,6 +366,11 @@ stages:
|
|||||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||||
displayName: Enable Windows Test Service
|
displayName: Enable Windows Test Service
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
- bash: |
|
||||||
|
wget https://github.com/acoustid/chromaprint/releases/download/v1.4.3/chromaprint-fpcalc-1.4.3-linux-x86_64.tar.gz
|
||||||
|
sudo tar xf chromaprint-fpcalc-1.4.3-linux-x86_64.tar.gz --strip-components=1 --directory /usr/bin
|
||||||
|
displayName: Install fpcalc
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
|
||||||
- bash: |
|
- bash: |
|
||||||
SYMLINK=6_6_0
|
SYMLINK=6_6_0
|
||||||
MONOPREFIX=/Library/Frameworks/Mono.framework/Versions/$SYMLINK
|
MONOPREFIX=/Library/Frameworks/Mono.framework/Versions/$SYMLINK
|
||||||
@@ -417,6 +402,10 @@ stages:
|
|||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
mono508:
|
||||||
|
testName: 'Mono 5.8'
|
||||||
|
artifactName: LinuxTests
|
||||||
|
containerImage: servarr/testimages:mono-5.8
|
||||||
mono520:
|
mono520:
|
||||||
testName: 'Mono 5.20'
|
testName: 'Mono 5.20'
|
||||||
artifactName: LinuxTests
|
artifactName: LinuxTests
|
||||||
@@ -566,6 +555,11 @@ stages:
|
|||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
mono508:
|
||||||
|
testName: 'Mono 5.8'
|
||||||
|
artifactName: LinuxTests
|
||||||
|
containerImage: servarr/testimages:mono-5.8
|
||||||
|
pattern: 'Radarr.**.linux.tar.gz'
|
||||||
mono520:
|
mono520:
|
||||||
testName: 'Mono 5.20'
|
testName: 'Mono 5.20'
|
||||||
artifactName: LinuxTests
|
artifactName: LinuxTests
|
||||||
@@ -583,7 +577,7 @@ stages:
|
|||||||
pattern: 'Radarr.**.linux.tar.gz'
|
pattern: 'Radarr.**.linux.tar.gz'
|
||||||
alpine:
|
alpine:
|
||||||
testName: 'Musl Net Core'
|
testName: 'Musl Net Core'
|
||||||
artifactName: LinuxMuslCoreTests
|
artifactName: LinuxCoreTests
|
||||||
containerImage: servarr/testimages:alpine
|
containerImage: servarr/testimages:alpine
|
||||||
pattern: 'Radarr.**.linux-musl-core-x64.tar.gz'
|
pattern: 'Radarr.**.linux-musl-core-x64.tar.gz'
|
||||||
pool:
|
pool:
|
||||||
@@ -648,14 +642,14 @@ stages:
|
|||||||
failBuild: true
|
failBuild: true
|
||||||
Mac:
|
Mac:
|
||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
imageName: 'macos-10.14'
|
imageName: 'macos-10.14' # Fails due to firefox not being installed on image
|
||||||
pattern: 'Radarr.**.osx-core-x64.tar.gz'
|
pattern: 'Radarr.**.osx-core-x64.tar.gz'
|
||||||
failBuild: true
|
failBuild: false
|
||||||
Windows:
|
Windows:
|
||||||
osName: 'Windows'
|
osName: 'Windows'
|
||||||
imageName: 'windows-2019'
|
imageName: 'windows-2019'
|
||||||
pattern: 'Radarr.**.windows-core-x64.zip'
|
pattern: 'Radarr.**.windows-core-x64.zip'
|
||||||
failBuild: true
|
failBuild: $(failOnAutomationFailure)
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
@@ -688,21 +682,24 @@ stages:
|
|||||||
mkdir -p ./bin/
|
mkdir -p ./bin/
|
||||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
||||||
displayName: Move Package Contents
|
displayName: Move Package Contents
|
||||||
|
- bash: |
|
||||||
|
if [[ $OSNAME == "Mac" ]]; then
|
||||||
|
url=https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-macos.tar.gz
|
||||||
|
elif [[ $OSNAME == "Linux" ]]; then
|
||||||
|
url=https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz
|
||||||
|
else
|
||||||
|
echo "Unhandled OS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
curl -s -L "$url" | tar -xz
|
||||||
|
chmod +x geckodriver
|
||||||
|
mv geckodriver _tests
|
||||||
|
displayName: Install Gecko Driver
|
||||||
|
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||||
- bash: |
|
- bash: |
|
||||||
chmod a+x ${TESTSFOLDER}/test.sh
|
chmod a+x ${TESTSFOLDER}/test.sh
|
||||||
${TESTSFOLDER}/test.sh ${OSNAME} Automation Test
|
${TESTSFOLDER}/test.sh ${OSNAME} Automation Test
|
||||||
displayName: Run Automation Tests
|
displayName: Run Integration Tests
|
||||||
- task: CopyFiles@2
|
|
||||||
displayName: 'Copy Screenshot to: $(Build.ArtifactStagingDirectory)'
|
|
||||||
inputs:
|
|
||||||
SourceFolder: '$(Build.SourcesDirectory)'
|
|
||||||
Contents: |
|
|
||||||
**/*_test_screenshot.png
|
|
||||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
|
|
||||||
- publish: $(Build.ArtifactStagingDirectory)/screenshots
|
|
||||||
artifact: '$(osName)AutomationScreenshots'
|
|
||||||
displayName: Publish Screenshot Bundle
|
|
||||||
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
|
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
@@ -750,19 +747,10 @@ stages:
|
|||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
- task: Cache@2
|
|
||||||
inputs:
|
|
||||||
key: 'yarn | "$(osName)" | yarn.lock'
|
|
||||||
restoreKeys: |
|
|
||||||
yarn | "$(osName)"
|
|
||||||
yarn
|
|
||||||
path: $(yarnCacheFolder)
|
|
||||||
displayName: Cache Yarn packages
|
|
||||||
- bash: ./build.sh --lint
|
- bash: ./build.sh --lint
|
||||||
displayName: Lint Radarr Frontend
|
displayName: Lint Radarr Frontend
|
||||||
env:
|
env:
|
||||||
FORCE_COLOR: 0
|
FORCE_COLOR: 0
|
||||||
YARN_CACHE_FOLDER: $(yarnCacheFolder)
|
|
||||||
|
|
||||||
- job: Analyze_Frontend
|
- job: Analyze_Frontend
|
||||||
displayName: Frontend
|
displayName: Frontend
|
||||||
@@ -851,15 +839,8 @@ stages:
|
|||||||
- job:
|
- job:
|
||||||
displayName: Discord Notification
|
displayName: Discord Notification
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'windows-2019'
|
vmImage: 'ubuntu-18.04'
|
||||||
steps:
|
steps:
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
continueOnError: true
|
|
||||||
displayName: Download Screenshot Artifact
|
|
||||||
inputs:
|
|
||||||
buildType: 'current'
|
|
||||||
artifactName: 'WindowsAutomationScreenshots'
|
|
||||||
targetPath: $(Build.SourcesDirectory)
|
|
||||||
- checkout: none
|
- checkout: none
|
||||||
- powershell: |
|
- powershell: |
|
||||||
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/Servarr/AzureDiscordNotify/master/DiscordNotify.ps1'))
|
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/Servarr/AzureDiscordNotify/master/DiscordNotify.ps1'))
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ PackageTests()
|
|||||||
# geckodriver.exe isn't copied by dotnet publish
|
# geckodriver.exe isn't copied by dotnet publish
|
||||||
if [ "$runtime" = "win-x64" ];
|
if [ "$runtime" = "win-x64" ];
|
||||||
then
|
then
|
||||||
curl -Lso gecko.zip "https://github.com/mozilla/geckodriver/releases/download/v0.27.0/geckodriver-v0.27.0-win64.zip"
|
curl -Lso gecko.zip "https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-win64.zip"
|
||||||
unzip -o gecko.zip
|
unzip -o gecko.zip
|
||||||
cp geckodriver.exe "$testPackageFolder/$framework/win-x64/publish"
|
cp geckodriver.exe "$testPackageFolder/$framework/win-x64/publish"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
"function-parentheses-newline-inside": "never-multi-line",
|
"function-parentheses-newline-inside": "never-multi-line",
|
||||||
"function-parentheses-space-inside": "never",
|
"function-parentheses-space-inside": "never",
|
||||||
"function-url-quotes": "always",
|
"function-url-quotes": "always",
|
||||||
"function-url-scheme-disallowed-list": [
|
"function-url-scheme-blacklist": [
|
||||||
"data"
|
"data"
|
||||||
],
|
],
|
||||||
"function-whitespace-after": "always",
|
"function-whitespace-after": "always",
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ const errorHandler = require('./helpers/errorHandler');
|
|||||||
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const HtmlWebpackPluginHtmlTags = require('html-webpack-plugin/lib/html-tags');
|
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
|
||||||
const uiFolder = 'UI';
|
const uiFolder = 'UI';
|
||||||
@@ -15,7 +14,7 @@ const frontendFolder = path.join(__dirname, '..');
|
|||||||
const srcFolder = path.join(frontendFolder, 'src');
|
const srcFolder = path.join(frontendFolder, 'src');
|
||||||
const isProduction = process.argv.indexOf('--production') > -1;
|
const isProduction = process.argv.indexOf('--production') > -1;
|
||||||
const isProfiling = isProduction && process.argv.indexOf('--profile') > -1;
|
const isProfiling = isProduction && process.argv.indexOf('--profile') > -1;
|
||||||
const inlineWebWorkers = 'no-fallback';
|
const inlineWebWorkers = true;
|
||||||
|
|
||||||
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
|
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
|
||||||
|
|
||||||
@@ -33,19 +32,14 @@ const cssVarsFiles = [
|
|||||||
].map(require.resolve);
|
].map(require.resolve);
|
||||||
|
|
||||||
// Override the way HtmlWebpackPlugin injects the scripts
|
// Override the way HtmlWebpackPlugin injects the scripts
|
||||||
// TODO: Find a better way to get these paths without
|
|
||||||
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets, assetTags) {
|
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets, assetTags) {
|
||||||
const head = assetTags.headTags.map((v) => {
|
const head = assetTags.head.map((v) => {
|
||||||
const href = v.attributes.href
|
v.attributes = { rel: 'stylesheet', type: 'text/css', href: `/${v.attributes.href.replace('\\', '/')}` };
|
||||||
.replace('\\', '/')
|
return this.createHtmlTag(v);
|
||||||
.replace('%5C', '/');
|
|
||||||
|
|
||||||
v.attributes = { rel: 'stylesheet', type: 'text/css', href: `/${href}` };
|
|
||||||
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
|
|
||||||
});
|
});
|
||||||
const body = assetTags.bodyTags.map((v) => {
|
const body = assetTags.body.map((v) => {
|
||||||
v.attributes = { src: `/${v.attributes.src}` };
|
v.attributes = { src: `/${v.attributes.src}` };
|
||||||
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
|
return this.createHtmlTag(v);
|
||||||
});
|
});
|
||||||
|
|
||||||
return html
|
return html
|
||||||
@@ -131,8 +125,9 @@ const config = {
|
|||||||
use: {
|
use: {
|
||||||
loader: 'worker-loader',
|
loader: 'worker-loader',
|
||||||
options: {
|
options: {
|
||||||
filename: '[name].js',
|
name: '[name].js',
|
||||||
inline: inlineWebWorkers
|
inline: inlineWebWorkers,
|
||||||
|
fallback: !inlineWebWorkers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es6",
|
|
||||||
"checkJs": false,
|
|
||||||
"baseUrl": "src",
|
|
||||||
"jsx": "react",
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"paths": {
|
|
||||||
"*": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"./src/**/*"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||||
@@ -11,84 +10,12 @@ import Table from 'Components/Table/Table';
|
|||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import TablePager from 'Components/Table/TablePager';
|
import TablePager from 'Components/Table/TablePager';
|
||||||
import { align, icons, kinds } from 'Helpers/Props';
|
import { align, icons } from 'Helpers/Props';
|
||||||
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
|
||||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
|
||||||
import translate from 'Utilities/String/translate';
|
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 BlacklistRowConnector from './BlacklistRowConnector';
|
import BlacklistRowConnector from './BlacklistRowConnector';
|
||||||
|
|
||||||
class Blacklist extends Component {
|
class Blacklist extends Component {
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
allSelected: false,
|
|
||||||
allUnselected: false,
|
|
||||||
lastToggled: null,
|
|
||||||
selectedState: {},
|
|
||||||
isConfirmRemoveModalOpen: false,
|
|
||||||
items: props.items
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
const {
|
|
||||||
items
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (hasDifferentItems(prevProps.items, items)) {
|
|
||||||
this.setState((state) => {
|
|
||||||
return {
|
|
||||||
...removeOldSelectedState(state, getRemovedItems(prevProps.items, items)),
|
|
||||||
items
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Control
|
|
||||||
|
|
||||||
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
|
// Render
|
||||||
|
|
||||||
@@ -100,33 +27,15 @@ class Blacklist extends Component {
|
|||||||
items,
|
items,
|
||||||
columns,
|
columns,
|
||||||
totalRecords,
|
totalRecords,
|
||||||
isRemoving,
|
|
||||||
isClearingBlacklistExecuting,
|
isClearingBlacklistExecuting,
|
||||||
onClearBlacklistPress,
|
onClearBlacklistPress,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
|
||||||
allSelected,
|
|
||||||
allUnselected,
|
|
||||||
selectedState,
|
|
||||||
isConfirmRemoveModalOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const selectedIds = this.getSelectedIds();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('Blacklist')}>
|
<PageContent title="Blacklist">
|
||||||
<PageToolbar>
|
<PageToolbar>
|
||||||
<PageToolbarSection>
|
<PageToolbarSection>
|
||||||
<PageToolbarButton
|
|
||||||
label="Remove Selected"
|
|
||||||
iconName={icons.REMOVE}
|
|
||||||
isDisabled={!selectedIds.length}
|
|
||||||
isSpinning={isRemoving}
|
|
||||||
onPress={this.onRemoveSelectedPress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
label={translate('Clear')}
|
label={translate('Clear')}
|
||||||
iconName={icons.CLEAR}
|
iconName={icons.CLEAR}
|
||||||
@@ -156,15 +65,13 @@ class Blacklist extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && !!error &&
|
!isFetching && !!error &&
|
||||||
<div>
|
<div>Unable to load blacklist</div>
|
||||||
{translate('UnableToLoadBlacklist')}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !error && !items.length &&
|
isPopulated && !error && !items.length &&
|
||||||
<div>
|
<div>
|
||||||
{translate('NoHistory')}
|
No history blacklist
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,12 +79,8 @@ class Blacklist extends Component {
|
|||||||
isPopulated && !error && !!items.length &&
|
isPopulated && !error && !!items.length &&
|
||||||
<div>
|
<div>
|
||||||
<Table
|
<Table
|
||||||
selectAll={true}
|
|
||||||
allSelected={allSelected}
|
|
||||||
allUnselected={allUnselected}
|
|
||||||
columns={columns}
|
columns={columns}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
onSelectAllChange={this.onSelectAllChange}
|
|
||||||
>
|
>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{
|
{
|
||||||
@@ -185,10 +88,8 @@ class Blacklist extends Component {
|
|||||||
return (
|
return (
|
||||||
<BlacklistRowConnector
|
<BlacklistRowConnector
|
||||||
key={item.id}
|
key={item.id}
|
||||||
isSelected={selectedState[item.id] || false}
|
|
||||||
columns={columns}
|
columns={columns}
|
||||||
{...item}
|
{...item}
|
||||||
onSelectedChange={this.onSelectedChange}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@@ -204,16 +105,6 @@ class Blacklist extends Component {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</PageContentBody>
|
</PageContentBody>
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={isConfirmRemoveModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
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}
|
|
||||||
/>
|
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -226,9 +117,7 @@ Blacklist.propTypes = {
|
|||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
totalRecords: PropTypes.number,
|
totalRecords: PropTypes.number,
|
||||||
isRemoving: PropTypes.bool.isRequired,
|
|
||||||
isClearingBlacklistExecuting: PropTypes.bool.isRequired,
|
isClearingBlacklistExecuting: PropTypes.bool.isRequired,
|
||||||
onRemoveSelected: PropTypes.func.isRequired,
|
|
||||||
onClearBlacklistPress: PropTypes.func.isRequired
|
onClearBlacklistPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -89,10 +89,6 @@ class BlacklistConnector extends Component {
|
|||||||
this.props.gotoBlacklistPage({ page });
|
this.props.gotoBlacklistPage({ page });
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemoveSelected = (ids) => {
|
|
||||||
this.props.removeBlacklistItems({ ids });
|
|
||||||
}
|
|
||||||
|
|
||||||
onSortPress = (sortKey) => {
|
onSortPress = (sortKey) => {
|
||||||
this.props.setBlacklistSort({ sortKey });
|
this.props.setBlacklistSort({ sortKey });
|
||||||
}
|
}
|
||||||
@@ -128,7 +124,6 @@ class BlacklistConnector extends Component {
|
|||||||
onNextPagePress={this.onNextPagePress}
|
onNextPagePress={this.onNextPagePress}
|
||||||
onLastPagePress={this.onLastPagePress}
|
onLastPagePress={this.onLastPagePress}
|
||||||
onPageSelect={this.onPageSelect}
|
onPageSelect={this.onPageSelect}
|
||||||
onRemoveSelected={this.onRemoveSelected}
|
|
||||||
onSortPress={this.onSortPress}
|
onSortPress={this.onSortPress}
|
||||||
onTableOptionChange={this.onTableOptionChange}
|
onTableOptionChange={this.onTableOptionChange}
|
||||||
onClearBlacklistPress={this.onClearBlacklistPress}
|
onClearBlacklistPress={this.onClearBlacklistPress}
|
||||||
@@ -148,7 +143,6 @@ BlacklistConnector.propTypes = {
|
|||||||
gotoBlacklistNextPage: PropTypes.func.isRequired,
|
gotoBlacklistNextPage: PropTypes.func.isRequired,
|
||||||
gotoBlacklistLastPage: PropTypes.func.isRequired,
|
gotoBlacklistLastPage: PropTypes.func.isRequired,
|
||||||
gotoBlacklistPage: PropTypes.func.isRequired,
|
gotoBlacklistPage: PropTypes.func.isRequired,
|
||||||
removeBlacklistItems: PropTypes.func.isRequired,
|
|
||||||
setBlacklistSort: PropTypes.func.isRequired,
|
setBlacklistSort: PropTypes.func.isRequired,
|
||||||
setBlacklistTableOption: PropTypes.func.isRequired,
|
setBlacklistTableOption: PropTypes.func.isRequired,
|
||||||
clearBlacklist: PropTypes.func.isRequired,
|
clearBlacklist: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import ModalBody from 'Components/Modal/ModalBody';
|
|||||||
import ModalContent from 'Components/Modal/ModalContent';
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
class BlacklistDetailsModal extends Component {
|
class BlacklistDetailsModal extends Component {
|
||||||
|
|
||||||
@@ -40,19 +39,19 @@ class BlacklistDetailsModal extends Component {
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Name')}
|
title="Name"
|
||||||
data={sourceTitle}
|
data={sourceTitle}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Protocol')}
|
title="Protocol"
|
||||||
data={protocol}
|
data={protocol}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
!!message &&
|
!!message &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Indexer')}
|
title="Indexer"
|
||||||
data={indexer}
|
data={indexer}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -60,7 +59,7 @@ class BlacklistDetailsModal extends Component {
|
|||||||
{
|
{
|
||||||
!!message &&
|
!!message &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Message')}
|
title="Message"
|
||||||
data={message}
|
data={message}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -69,7 +68,7 @@ class BlacklistDetailsModal extends Component {
|
|||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button onPress={onModalClose}>
|
<Button onPress={onModalClose}>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ import React, { Component } from 'react';
|
|||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import MovieFormats from 'Movie/MovieFormats';
|
import MovieFormats from 'Movie/MovieFormats';
|
||||||
import MovieLanguage from 'Movie/MovieLanguage';
|
import MovieLanguage from 'Movie/MovieLanguage';
|
||||||
import MovieQuality from 'Movie/MovieQuality';
|
import MovieQuality from 'Movie/MovieQuality';
|
||||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import BlacklistDetailsModal from './BlacklistDetailsModal';
|
import BlacklistDetailsModal from './BlacklistDetailsModal';
|
||||||
import styles from './BlacklistRow.css';
|
import styles from './BlacklistRow.css';
|
||||||
|
|
||||||
@@ -43,7 +41,6 @@ class BlacklistRow extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
id,
|
|
||||||
movie,
|
movie,
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
quality,
|
quality,
|
||||||
@@ -53,9 +50,7 @@ class BlacklistRow extends Component {
|
|||||||
protocol,
|
protocol,
|
||||||
indexer,
|
indexer,
|
||||||
message,
|
message,
|
||||||
isSelected,
|
|
||||||
columns,
|
columns,
|
||||||
onSelectedChange,
|
|
||||||
onRemovePress
|
onRemovePress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -65,12 +60,6 @@ class BlacklistRow extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableSelectCell
|
|
||||||
id={id}
|
|
||||||
isSelected={isSelected}
|
|
||||||
onSelectedChange={onSelectedChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
columns.map((column) => {
|
columns.map((column) => {
|
||||||
const {
|
const {
|
||||||
@@ -166,7 +155,7 @@ class BlacklistRow extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
title={translate('RemoveFromBlacklist')}
|
title="Remove from blacklist"
|
||||||
name={icons.REMOVE}
|
name={icons.REMOVE}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
onPress={onRemovePress}
|
onPress={onRemovePress}
|
||||||
@@ -204,9 +193,7 @@ BlacklistRow.propTypes = {
|
|||||||
protocol: PropTypes.string.isRequired,
|
protocol: PropTypes.string.isRequired,
|
||||||
indexer: PropTypes.string,
|
indexer: PropTypes.string,
|
||||||
message: PropTypes.string,
|
message: PropTypes.string,
|
||||||
isSelected: PropTypes.bool.isRequired,
|
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onSelectedChange: PropTypes.func.isRequired,
|
|
||||||
onRemovePress: PropTypes.func.isRequired
|
onRemovePress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { removeBlacklistItem } from 'Store/Actions/blacklistActions';
|
import { removeFromBlacklist } from 'Store/Actions/blacklistActions';
|
||||||
import createMovieSelector from 'Store/Selectors/createMovieSelector';
|
import createMovieSelector from 'Store/Selectors/createMovieSelector';
|
||||||
import BlacklistRow from './BlacklistRow';
|
import BlacklistRow from './BlacklistRow';
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ function createMapStateToProps() {
|
|||||||
function createMapDispatchToProps(dispatch, props) {
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
return {
|
return {
|
||||||
onRemovePress() {
|
onRemovePress() {
|
||||||
dispatch(removeBlacklistItem({ id: props.id }));
|
dispatch(removeFromBlacklist({ id: props.id }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionList
|
|||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||||
import formatAge from 'Utilities/Number/formatAge';
|
import formatAge from 'Utilities/Number/formatAge';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './HistoryDetails.css';
|
import styles from './HistoryDetails.css';
|
||||||
|
|
||||||
function HistoryDetails(props) {
|
function HistoryDetails(props) {
|
||||||
@@ -36,14 +35,14 @@ function HistoryDetails(props) {
|
|||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Name')}
|
title="Name"
|
||||||
data={sourceTitle}
|
data={sourceTitle}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
!!indexer &&
|
!!indexer &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Indexer')}
|
title="Indexer"
|
||||||
data={indexer}
|
data={indexer}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -52,7 +51,7 @@ function HistoryDetails(props) {
|
|||||||
!!releaseGroup &&
|
!!releaseGroup &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('ReleaseGroup')}
|
title="Release Group"
|
||||||
data={releaseGroup}
|
data={releaseGroup}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -73,7 +72,7 @@ function HistoryDetails(props) {
|
|||||||
{
|
{
|
||||||
!!downloadClient &&
|
!!downloadClient &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('DownloadClient')}
|
title="Download Client"
|
||||||
data={downloadClient}
|
data={downloadClient}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -81,7 +80,7 @@ function HistoryDetails(props) {
|
|||||||
{
|
{
|
||||||
!!downloadId &&
|
!!downloadId &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('GrabID')}
|
title="Grab ID"
|
||||||
data={downloadId}
|
data={downloadId}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -89,7 +88,7 @@ function HistoryDetails(props) {
|
|||||||
{
|
{
|
||||||
!!indexer &&
|
!!indexer &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('AgeWhenGrabbed')}
|
title="Age (when grabbed)"
|
||||||
data={formatAge(age, ageHours, ageMinutes)}
|
data={formatAge(age, ageHours, ageMinutes)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -97,7 +96,7 @@ function HistoryDetails(props) {
|
|||||||
{
|
{
|
||||||
!!publishedDate &&
|
!!publishedDate &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('PublishedDate')}
|
title="Published Date"
|
||||||
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -114,14 +113,14 @@ function HistoryDetails(props) {
|
|||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Name')}
|
title="Name"
|
||||||
data={sourceTitle}
|
data={sourceTitle}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
!!message &&
|
!!message &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Message')}
|
title="Message"
|
||||||
data={message}
|
data={message}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -139,7 +138,7 @@ function HistoryDetails(props) {
|
|||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Name')}
|
title="Name"
|
||||||
data={sourceTitle}
|
data={sourceTitle}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -147,7 +146,7 @@ function HistoryDetails(props) {
|
|||||||
!!droppedPath &&
|
!!droppedPath &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Source')}
|
title="Source"
|
||||||
data={droppedPath}
|
data={droppedPath}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -156,7 +155,7 @@ function HistoryDetails(props) {
|
|||||||
!!importedPath &&
|
!!importedPath &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('ImportedTo')}
|
title="Imported To"
|
||||||
data={importedPath}
|
data={importedPath}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -188,12 +187,12 @@ function HistoryDetails(props) {
|
|||||||
return (
|
return (
|
||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Name')}
|
title="Name"
|
||||||
data={sourceTitle}
|
data={sourceTitle}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Reason')}
|
title="Reason"
|
||||||
data={reasonMessage}
|
data={reasonMessage}
|
||||||
/>
|
/>
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
@@ -211,22 +210,22 @@ function HistoryDetails(props) {
|
|||||||
return (
|
return (
|
||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('SourcePath')}
|
title="Source Path"
|
||||||
data={sourcePath}
|
data={sourcePath}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('SourceRelativePath')}
|
title="Source Relative Path"
|
||||||
data={sourceRelativePath}
|
data={sourceRelativePath}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('DestinationPath')}
|
title="Destination Path"
|
||||||
data={path}
|
data={path}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('DestinationRelativePath')}
|
title="Destination Relative Path"
|
||||||
data={relativePath}
|
data={relativePath}
|
||||||
/>
|
/>
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
@@ -242,14 +241,14 @@ function HistoryDetails(props) {
|
|||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Name')}
|
title="Name"
|
||||||
data={sourceTitle}
|
data={sourceTitle}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
!!message &&
|
!!message &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('Message')}
|
title="Message"
|
||||||
data={message}
|
data={message}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -261,7 +260,7 @@ function HistoryDetails(props) {
|
|||||||
<DescriptionList>
|
<DescriptionList>
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
descriptionClassName={styles.description}
|
descriptionClassName={styles.description}
|
||||||
title={translate('Name')}
|
title="Name"
|
||||||
data={sourceTitle}
|
data={sourceTitle}
|
||||||
/>
|
/>
|
||||||
</DescriptionList>
|
</DescriptionList>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
@@ -7,10 +8,10 @@ function createMapStateToProps() {
|
|||||||
return createSelector(
|
return createSelector(
|
||||||
createUISettingsSelector(),
|
createUISettingsSelector(),
|
||||||
(uiSettings) => {
|
(uiSettings) => {
|
||||||
return {
|
return _.pick(uiSettings, [
|
||||||
shortDateFormat: uiSettings.shortDateFormat,
|
'shortDateFormat',
|
||||||
timeFormat: uiSettings.timeFormat
|
'timeFormat'
|
||||||
};
|
]);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import HistoryDetails from './HistoryDetails';
|
import HistoryDetails from './HistoryDetails';
|
||||||
import styles from './HistoryDetailsModal.css';
|
import styles from './HistoryDetailsModal.css';
|
||||||
|
|
||||||
@@ -80,7 +79,7 @@ function HistoryDetailsModal(props) {
|
|||||||
<Button
|
<Button
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class History extends Component {
|
|||||||
const hasError = error || moviesError;
|
const hasError = error || moviesError;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('History')}>
|
<PageContent title="History">
|
||||||
<PageToolbar>
|
<PageToolbar>
|
||||||
<PageToolbarSection>
|
<PageToolbarSection>
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
@@ -83,9 +83,7 @@ class History extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetchingAny && hasError &&
|
!isFetchingAny && hasError &&
|
||||||
<div>
|
<div>Unable to load history</div>
|
||||||
{translate('UnableToLoadHistory')}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ class Queue extends Component {
|
|||||||
const disableSelectedActions = selectedCount === 0;
|
const disableSelectedActions = selectedCount === 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('Queue')}>
|
<PageContent title="Queue">
|
||||||
<PageToolbar>
|
<PageToolbar>
|
||||||
<PageToolbarSection>
|
<PageToolbarSection>
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
@@ -162,7 +162,7 @@ class Queue extends Component {
|
|||||||
<PageToolbarSeparator />
|
<PageToolbarSeparator />
|
||||||
|
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
label={translate('GrabSelected')}
|
label="Grab Selected"
|
||||||
iconName={icons.DOWNLOAD}
|
iconName={icons.DOWNLOAD}
|
||||||
isDisabled={disableSelectedActions || !isPendingSelected}
|
isDisabled={disableSelectedActions || !isPendingSelected}
|
||||||
isSpinning={isGrabbing}
|
isSpinning={isGrabbing}
|
||||||
@@ -170,7 +170,7 @@ class Queue extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
label={translate('RemoveSelected')}
|
label="Remove Selected"
|
||||||
iconName={icons.REMOVE}
|
iconName={icons.REMOVE}
|
||||||
isDisabled={disableSelectedActions}
|
isDisabled={disableSelectedActions}
|
||||||
isSpinning={isRemoving}
|
isSpinning={isRemoving}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
function QueueDetails(props) {
|
function QueueDetails(props) {
|
||||||
const {
|
const {
|
||||||
@@ -11,20 +10,20 @@ function QueueDetails(props) {
|
|||||||
size,
|
size,
|
||||||
sizeleft,
|
sizeleft,
|
||||||
estimatedCompletionTime,
|
estimatedCompletionTime,
|
||||||
status,
|
status: queueStatus,
|
||||||
trackedDownloadState,
|
|
||||||
trackedDownloadStatus,
|
|
||||||
errorMessage,
|
errorMessage,
|
||||||
progressBar
|
progressBar
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
const status = queueStatus.toLowerCase();
|
||||||
|
|
||||||
|
const progress = (100 - sizeleft / size * 100);
|
||||||
|
|
||||||
if (status === 'pending') {
|
if (status === 'pending') {
|
||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
name={icons.PENDING}
|
name={icons.PENDING}
|
||||||
title={translate('ReleaseWillBeProcessedInterp', [moment(estimatedCompletionTime).fromNow()])}
|
title={`Release will be processed ${moment(estimatedCompletionTime).fromNow()}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -35,40 +34,12 @@ function QueueDetails(props) {
|
|||||||
<Icon
|
<Icon
|
||||||
name={icons.DOWNLOAD}
|
name={icons.DOWNLOAD}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('ImportFailedInterp', [errorMessage])}
|
title={`Import failed: ${errorMessage}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackedDownloadStatus === 'warning') {
|
// TODO: show an icon when download is complete, but not imported yet?
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOAD}
|
|
||||||
kind={kinds.WARNING}
|
|
||||||
title={'Downloaded - Unable to Import: check logs for details'}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importPending') {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOAD}
|
|
||||||
kind={kinds.PURPLE}
|
|
||||||
title={`${translate('Downloaded')} - ${translate('WaitingToImport')}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importing') {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOAD}
|
|
||||||
kind={kinds.PURPLE}
|
|
||||||
title={`${translate('Downloaded')} - ${translate('Importing')}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
@@ -76,7 +47,7 @@ function QueueDetails(props) {
|
|||||||
<Icon
|
<Icon
|
||||||
name={icons.DOWNLOADING}
|
name={icons.DOWNLOADING}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DownloadFailedInterp', [errorMessage])}
|
title={`Download failed: ${errorMessage}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -86,7 +57,7 @@ function QueueDetails(props) {
|
|||||||
<Icon
|
<Icon
|
||||||
name={icons.DOWNLOADING}
|
name={icons.DOWNLOADING}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('DownloadFailedCheckDownloadClientForMoreDetails')}
|
title="Download failed: check download client for more details"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -96,7 +67,7 @@ function QueueDetails(props) {
|
|||||||
<Icon
|
<Icon
|
||||||
name={icons.DOWNLOADING}
|
name={icons.DOWNLOADING}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
title={translate('DownloadWarningCheckDownloadClientForMoreDetails')}
|
title="Download warning: check download client for more details"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -105,7 +76,7 @@ function QueueDetails(props) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
name={icons.DOWNLOADING}
|
name={icons.DOWNLOADING}
|
||||||
title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}
|
title={`Movie is downloading - ${progress.toFixed(1)}% ${title}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -119,8 +90,6 @@ QueueDetails.propTypes = {
|
|||||||
sizeleft: PropTypes.number.isRequired,
|
sizeleft: PropTypes.number.isRequired,
|
||||||
estimatedCompletionTime: PropTypes.string,
|
estimatedCompletionTime: PropTypes.string,
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
trackedDownloadState: PropTypes.string.isRequired,
|
|
||||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
|
||||||
errorMessage: PropTypes.string,
|
errorMessage: PropTypes.string,
|
||||||
progressBar: PropTypes.node.isRequired
|
progressBar: PropTypes.node.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import FormGroup from 'Components/Form/FormGroup';
|
|||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
import { inputTypes } from 'Helpers/Props';
|
import { inputTypes } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
class QueueOptions extends Component {
|
class QueueOptions extends Component {
|
||||||
|
|
||||||
@@ -55,13 +54,13 @@ class QueueOptions extends Component {
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('ShowUnknownMovieItems')}</FormLabel>
|
<FormLabel>Show Unknown Movie Items</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="includeUnknownMovieItems"
|
name="includeUnknownMovieItems"
|
||||||
value={includeUnknownMovieItems}
|
value={includeUnknownMovieItems}
|
||||||
helpText={translate('IncludeUnknownMovieItemsHelpText')}
|
helpText="Show items without a movie in the queue, this could include removed movie, movies or anything else in Radarr's category"
|
||||||
onChange={this.onOptionChange}
|
onChange={this.onOptionChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import MovieLanguage from 'Movie/MovieLanguage';
|
|||||||
import MovieQuality from 'Movie/MovieQuality';
|
import MovieQuality from 'Movie/MovieQuality';
|
||||||
import MovieTitleLink from 'Movie/MovieTitleLink';
|
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import QueueStatusCell from './QueueStatusCell';
|
import QueueStatusCell from './QueueStatusCell';
|
||||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||||
import TimeleftCell from './TimeleftCell';
|
import TimeleftCell from './TimeleftCell';
|
||||||
@@ -295,7 +294,7 @@ class QueueRow extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<SpinnerIconButton
|
<SpinnerIconButton
|
||||||
title={translate('RemoveFromQueue')}
|
title="Remove from queue"
|
||||||
name={icons.REMOVE}
|
name={icons.REMOVE}
|
||||||
isSpinning={isRemoving}
|
isSpinning={isRemoving}
|
||||||
onPress={this.onRemoveQueueItemPress}
|
onPress={this.onRemoveQueueItemPress}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import Icon from 'Components/Icon';
|
|||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './QueueStatusCell.css';
|
import styles from './QueueStatusCell.css';
|
||||||
|
|
||||||
function getDetailedPopoverBody(statusMessages) {
|
function getDetailedPopoverBody(statusMessages) {
|
||||||
@@ -50,34 +49,34 @@ function QueueStatusCell(props) {
|
|||||||
// status === 'downloading'
|
// status === 'downloading'
|
||||||
let iconName = icons.DOWNLOADING;
|
let iconName = icons.DOWNLOADING;
|
||||||
let iconKind = kinds.DEFAULT;
|
let iconKind = kinds.DEFAULT;
|
||||||
let title = translate('Downloading');
|
let title = 'Downloading';
|
||||||
|
|
||||||
if (status === 'paused') {
|
if (status === 'paused') {
|
||||||
iconName = icons.PAUSED;
|
iconName = icons.PAUSED;
|
||||||
title = translate('Paused');
|
title = 'Paused';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'queued') {
|
if (status === 'queued') {
|
||||||
iconName = icons.QUEUED;
|
iconName = icons.QUEUED;
|
||||||
title = translate('Queued');
|
title = 'Queued';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'completed') {
|
if (status === 'completed') {
|
||||||
iconName = icons.DOWNLOADED;
|
iconName = icons.DOWNLOADED;
|
||||||
title = translate('Downloaded');
|
title = 'Downloaded';
|
||||||
|
|
||||||
if (trackedDownloadState === 'importPending') {
|
if (trackedDownloadState === 'importPending') {
|
||||||
title += ` - ${translate('WaitingToImport')}`;
|
title += ' - Waiting to Import';
|
||||||
iconKind = kinds.PURPLE;
|
iconKind = kinds.PURPLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackedDownloadState === 'importing') {
|
if (trackedDownloadState === 'importing') {
|
||||||
title += ` - ${translate('Importing')}`;
|
title += ' - Importing';
|
||||||
iconKind = kinds.PURPLE;
|
iconKind = kinds.PURPLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackedDownloadState === 'failedPending') {
|
if (trackedDownloadState === 'failedPending') {
|
||||||
title += ` - ${translate('WaitingToProcess')}`;
|
title += ' - Waiting to Process';
|
||||||
iconKind = kinds.DANGER;
|
iconKind = kinds.DANGER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,37 +87,36 @@ function QueueStatusCell(props) {
|
|||||||
|
|
||||||
if (status === 'delay') {
|
if (status === 'delay') {
|
||||||
iconName = icons.PENDING;
|
iconName = icons.PENDING;
|
||||||
title = translate('Pending');
|
title = 'Pending';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'DownloadClientUnavailable') {
|
if (status === 'DownloadClientUnavailable') {
|
||||||
iconName = icons.PENDING;
|
iconName = icons.PENDING;
|
||||||
iconKind = kinds.WARNING;
|
iconKind = kinds.WARNING;
|
||||||
title = `${translate('Pending')} - ${translate('DownloadClientUnavailable')}`;
|
title = 'Pending - Download client is unavailable';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'failed') {
|
if (status === 'failed') {
|
||||||
iconName = icons.DOWNLOADING;
|
iconName = icons.DOWNLOADING;
|
||||||
iconKind = kinds.DANGER;
|
iconKind = kinds.DANGER;
|
||||||
title = translate('DownloadFailed');
|
title = 'Download failed';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'warning') {
|
if (status === 'warning') {
|
||||||
iconName = icons.DOWNLOADING;
|
iconName = icons.DOWNLOADING;
|
||||||
iconKind = kinds.WARNING;
|
iconKind = kinds.WARNING;
|
||||||
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
|
title = `Download warning: ${errorMessage || 'check download client for more details'}`;
|
||||||
title = translate('DownloadWarning', [warningMessage]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
if (status === 'completed') {
|
if (status === 'completed') {
|
||||||
iconName = icons.DOWNLOAD;
|
iconName = icons.DOWNLOAD;
|
||||||
iconKind = kinds.DANGER;
|
iconKind = kinds.DANGER;
|
||||||
title = translate('ImportFailed', [sourceTitle]);
|
title = `Import failed: ${sourceTitle}`;
|
||||||
} else {
|
} else {
|
||||||
iconName = icons.DOWNLOADING;
|
iconName = icons.DOWNLOADING;
|
||||||
iconKind = kinds.DANGER;
|
iconKind = kinds.DANGER;
|
||||||
title = translate('DownloadFailed');
|
title = 'Download failed';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,8 +148,8 @@ QueueStatusCell.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
QueueStatusCell.defaultProps = {
|
QueueStatusCell.defaultProps = {
|
||||||
trackedDownloadStatus: translate('Ok'),
|
trackedDownloadStatus: 'Ok',
|
||||||
trackedDownloadState: translate('Downloading')
|
trackedDownloadState: 'Downloading'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default QueueStatusCell;
|
export default QueueStatusCell;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
class RemoveQueueItemModal extends Component {
|
class RemoveQueueItemModal extends Component {
|
||||||
|
|
||||||
@@ -90,25 +89,25 @@ class RemoveQueueItemModal extends Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
<FormLabel>Remove From Download Client</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="remove"
|
name="remove"
|
||||||
value={remove}
|
value={remove}
|
||||||
helpTextWarning={translate('RemoveHelpTextWarning')}
|
helpTextWarning="Removing will remove the download and the file(s) from the download client."
|
||||||
isDisabled={!canIgnore}
|
isDisabled={!canIgnore}
|
||||||
onChange={this.onRemoveChange}
|
onChange={this.onRemoveChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('BlacklistRelease')}</FormLabel>
|
<FormLabel>Blacklist Release</FormLabel>
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="blacklist"
|
name="blacklist"
|
||||||
value={blacklist}
|
value={blacklist}
|
||||||
helpText={translate('BlacklistHelpText')}
|
helpText="Starts a search for this movie again and prevents this release from being grabbed again"
|
||||||
onChange={this.onBlacklistChange}
|
onChange={this.onBlacklistChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -117,7 +116,7 @@ class RemoveQueueItemModal extends Component {
|
|||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button onPress={this.onModalClose}>
|
<Button onPress={this.onModalClose}>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './RemoveQueueItemsModal.css';
|
import styles from './RemoveQueueItemsModal.css';
|
||||||
|
|
||||||
class RemoveQueueItemsModal extends Component {
|
class RemoveQueueItemsModal extends Component {
|
||||||
@@ -91,13 +90,13 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
<FormLabel>Remove From Download Client</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="remove"
|
name="remove"
|
||||||
value={remove}
|
value={remove}
|
||||||
helpTextWarning={translate('RemoveHelpTextWarning')}
|
helpTextWarning="Removing will remove the download and the file(s) from the download client."
|
||||||
isDisabled={!canIgnore}
|
isDisabled={!canIgnore}
|
||||||
onChange={this.onRemoveChange}
|
onChange={this.onRemoveChange}
|
||||||
/>
|
/>
|
||||||
@@ -112,7 +111,7 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="blacklist"
|
name="blacklist"
|
||||||
value={blacklist}
|
value={blacklist}
|
||||||
helpText={translate('BlacklistHelpText')}
|
helpText="Prevents Radarr from automatically grabbing this movie again"
|
||||||
onChange={this.onBlacklistChange}
|
onChange={this.onBlacklistChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -121,7 +120,7 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button onPress={this.onModalClose}>
|
<Button onPress={this.onModalClose}>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import formatTime from 'Utilities/Date/formatTime';
|
|||||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||||
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './TimeleftCell.css';
|
import styles from './TimeleftCell.css';
|
||||||
|
|
||||||
function TimeleftCell(props) {
|
function TimeleftCell(props) {
|
||||||
@@ -27,7 +26,7 @@ function TimeleftCell(props) {
|
|||||||
return (
|
return (
|
||||||
<TableRowCell
|
<TableRowCell
|
||||||
className={styles.timeleft}
|
className={styles.timeleft}
|
||||||
title={translate('DelayingDownloadUntilInterp', [date, time])}
|
title={`Delaying download until ${date} at ${time}`}
|
||||||
>
|
>
|
||||||
-
|
-
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
@@ -41,7 +40,7 @@ function TimeleftCell(props) {
|
|||||||
return (
|
return (
|
||||||
<TableRowCell
|
<TableRowCell
|
||||||
className={styles.timeleft}
|
className={styles.timeleft}
|
||||||
title={translate('RetryingDownloadInterp', [date, time])}
|
title={`Retrying download ${date} at ${time}`}
|
||||||
>
|
>
|
||||||
-
|
-
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class AddNewMovie extends Component {
|
|||||||
const isFetching = this.state.isFetching;
|
const isFetching = this.state.isFetching;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('AddNewMovie')}>
|
<PageContent title="Add New Movie">
|
||||||
<PageContentBody>
|
<PageContentBody>
|
||||||
<div className={styles.searchContainer}>
|
<div className={styles.searchContainer}>
|
||||||
<div className={styles.searchIconContainer}>
|
<div className={styles.searchIconContainer}>
|
||||||
@@ -127,7 +127,7 @@ class AddNewMovie extends Component {
|
|||||||
!isFetching && !!error ?
|
!isFetching && !!error ?
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
<div className={styles.helpText}>
|
<div className={styles.helpText}>
|
||||||
{translate('FailedLoadingSearchResults')}
|
Failed to load search results, please try again.
|
||||||
</div>
|
</div>
|
||||||
<div>{getErrorMessage(error)}</div>
|
<div>{getErrorMessage(error)}</div>
|
||||||
</div> : null
|
</div> : null
|
||||||
@@ -152,15 +152,11 @@ class AddNewMovie extends Component {
|
|||||||
{
|
{
|
||||||
!isFetching && !error && !items.length && !!term &&
|
!isFetching && !error && !items.length && !!term &&
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
<div className={styles.noResults}>
|
<div className={styles.noResults}>Couldn't find any results for '{term}'</div>
|
||||||
{translate('CouldNotFindResults', [term])}
|
<div>You can also search using TMDB ID or IMDB ID of a movie. eg. tmdb:71663</div>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{translate('YouCanAlsoSearch')}
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<Link to="https://github.com/Radarr/Radarr/wiki/FAQ#why-cant-i-add-a-new-movie-when-i-know-the-tmdb-id">
|
<Link to="https://github.com/Radarr/Radarr/wiki/FAQ#why-cant-i-add-a-new-movie-when-i-know-the-tmdb-id">
|
||||||
{translate('CantFindMovie')}
|
Why can't I find my movie?
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -173,9 +169,7 @@ class AddNewMovie extends Component {
|
|||||||
<div className={styles.helpText}>
|
<div className={styles.helpText}>
|
||||||
{translate('AddNewMessage')}
|
{translate('AddNewMessage')}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>{translate('AddNewTmdbIdMessage')}</div>
|
||||||
{translate('AddNewTmdbIdMessage')}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,14 +177,14 @@ class AddNewMovie extends Component {
|
|||||||
!term && !hasExistingMovies ?
|
!term && !hasExistingMovies ?
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
<div className={styles.noMoviesText}>
|
<div className={styles.noMoviesText}>
|
||||||
{translate('HaveNotAddedMovies')}
|
You haven't added any movies yet, do you want to import some or all of your movies first?
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
to="/add/import"
|
to="/add/import"
|
||||||
kind={kinds.PRIMARY}
|
kind={kinds.PRIMARY}
|
||||||
>
|
>
|
||||||
{translate('ImportExistingMovies')}
|
Import Existing Movies
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div> :
|
</div> :
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { clearAddMovie, lookupMovie } from 'Store/Actions/addMovieActions';
|
import { clearAddMovie, lookupMovie } from 'Store/Actions/addMovieActions';
|
||||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||||
import { fetchImportExclusions } from 'Store/Actions/Settings/importExclusions';
|
import { fetchNetImportExclusions } from 'Store/Actions/Settings/netImportExclusions';
|
||||||
import parseUrl from 'Utilities/String/parseUrl';
|
import parseUrl from 'Utilities/String/parseUrl';
|
||||||
import AddNewMovie from './AddNewMovie';
|
import AddNewMovie from './AddNewMovie';
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ const mapDispatchToProps = {
|
|||||||
lookupMovie,
|
lookupMovie,
|
||||||
clearAddMovie,
|
clearAddMovie,
|
||||||
fetchRootFolders,
|
fetchRootFolders,
|
||||||
fetchImportExclusions
|
fetchNetImportExclusions
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddNewMovieConnector extends Component {
|
class AddNewMovieConnector extends Component {
|
||||||
@@ -45,7 +45,7 @@ class AddNewMovieConnector extends Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.fetchRootFolders();
|
this.props.fetchRootFolders();
|
||||||
this.props.fetchImportExclusions();
|
this.props.fetchNetImportExclusions();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@@ -102,7 +102,7 @@ AddNewMovieConnector.propTypes = {
|
|||||||
lookupMovie: PropTypes.func.isRequired,
|
lookupMovie: PropTypes.func.isRequired,
|
||||||
clearAddMovie: PropTypes.func.isRequired,
|
clearAddMovie: PropTypes.func.isRequired,
|
||||||
fetchRootFolders: PropTypes.func.isRequired,
|
fetchRootFolders: PropTypes.func.isRequired,
|
||||||
fetchImportExclusions: PropTypes.func.isRequired
|
fetchNetImportExclusions: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { inputTypes, kinds } from 'Helpers/Props';
|
import { inputTypes, kinds } from 'Helpers/Props';
|
||||||
import MoviePoster from 'Movie/MoviePoster';
|
import MoviePoster from 'Movie/MoviePoster';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './AddNewMovieModalContent.css';
|
import styles from './AddNewMovieModalContent.css';
|
||||||
|
|
||||||
class AddNewMovieModalContent extends Component {
|
class AddNewMovieModalContent extends Component {
|
||||||
@@ -96,7 +95,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
|
|
||||||
<Form>
|
<Form>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('RootFolder')}</FormLabel>
|
<FormLabel>Root Folder</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||||
@@ -109,7 +108,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
movieFolder: folder,
|
movieFolder: folder,
|
||||||
isWindows
|
isWindows
|
||||||
}}
|
}}
|
||||||
helpText={translate('SubfolderWillBeCreatedAutomaticallyInterp', [folder])}
|
helpText={`'${folder}' subfolder will be created automatically`}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...rootFolderPath}
|
{...rootFolderPath}
|
||||||
/>
|
/>
|
||||||
@@ -117,7 +116,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{translate('Monitor')}
|
Monitor
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
@@ -129,7 +128,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('MinimumAvailability')}</FormLabel>
|
<FormLabel>Minimum Availability</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.AVAILABILITY_SELECT}
|
type={inputTypes.AVAILABILITY_SELECT}
|
||||||
@@ -140,7 +139,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('QualityProfile')}</FormLabel>
|
<FormLabel>Quality Profile</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||||
@@ -151,7 +150,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('Tags')}</FormLabel>
|
<FormLabel>Tags</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.TAG}
|
type={inputTypes.TAG}
|
||||||
@@ -168,7 +167,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
<ModalFooter className={styles.modalFooter}>
|
<ModalFooter className={styles.modalFooter}>
|
||||||
<label className={styles.searchForMissingMovieLabelContainer}>
|
<label className={styles.searchForMissingMovieLabelContainer}>
|
||||||
<span className={styles.searchForMissingMovieLabel}>
|
<span className={styles.searchForMissingMovieLabel}>
|
||||||
{translate('StartSearchForMissingMovie')}
|
Start search for missing movie
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<CheckInput
|
<CheckInput
|
||||||
@@ -186,7 +185,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
isSpinning={isAdding}
|
isSpinning={isAdding}
|
||||||
onPress={this.onAddMoviePress}
|
onPress={this.onAddMoviePress}
|
||||||
>
|
>
|
||||||
{translate('AddMovie')}
|
Add {title}
|
||||||
</SpinnerButton>
|
</SpinnerButton>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -34,20 +34,10 @@
|
|||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 0 1 100%;
|
flex: 0 1 100%;
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titleRow {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titleContainer {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
flex: 0 1 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
display: flex;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
}
|
}
|
||||||
@@ -57,12 +47,10 @@
|
|||||||
color: $disabledColor;
|
color: $disabledColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icons {
|
.externalLink {
|
||||||
display: flex;
|
margin-top: 5px;
|
||||||
align-items: center;
|
margin-left: auto;
|
||||||
justify-content: space-between;
|
color: $textColor;
|
||||||
flex: 1 0 auto;
|
|
||||||
height: 55px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.alreadyExistsIcon {
|
.alreadyExistsIcon {
|
||||||
@@ -80,15 +68,3 @@
|
|||||||
.overview {
|
.overview {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.links {
|
|
||||||
margin-left: 8px;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointMedium) {
|
|
||||||
.titleRow {
|
|
||||||
justify-content: space-between;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ import HeartRating from 'Components/HeartRating';
|
|||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
import { icons, kinds, sizes } from 'Helpers/Props';
|
||||||
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks';
|
|
||||||
import MoviePoster from 'Movie/MoviePoster';
|
import MoviePoster from 'Movie/MoviePoster';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import AddNewMovieModal from './AddNewMovieModal';
|
import AddNewMovieModal from './AddNewMovieModal';
|
||||||
import styles from './AddNewMovieSearchResult.css';
|
import styles from './AddNewMovieSearchResult.css';
|
||||||
|
|
||||||
@@ -53,7 +50,6 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
const {
|
const {
|
||||||
tmdbId,
|
tmdbId,
|
||||||
imdbId,
|
imdbId,
|
||||||
youTubeTrailerId,
|
|
||||||
title,
|
title,
|
||||||
titleSlug,
|
titleSlug,
|
||||||
year,
|
year,
|
||||||
@@ -94,43 +90,69 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.titleRow}>
|
<div className={styles.title}>
|
||||||
<div className={styles.titleContainer}>
|
{title}
|
||||||
<div className={styles.title}>
|
|
||||||
{title}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
!title.contains(year) && !!year ?
|
!title.contains(year) && !!year &&
|
||||||
<span className={styles.year}>
|
<span className={styles.year}>({year})</span>
|
||||||
({year})
|
}
|
||||||
</span> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.icons}>
|
{
|
||||||
|
isExistingMovie &&
|
||||||
|
<Icon
|
||||||
|
className={styles.alreadyExistsIcon}
|
||||||
|
name={icons.CHECK_CIRCLE}
|
||||||
|
size={36}
|
||||||
|
title="Already in your library"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isExistingMovie &&
|
isExclusionMovie &&
|
||||||
<Icon
|
<Icon
|
||||||
className={styles.alreadyExistsIcon}
|
className={styles.exclusionIcon}
|
||||||
name={icons.CHECK_CIRCLE}
|
name={icons.DANGER}
|
||||||
size={36}
|
size={36}
|
||||||
title={translate('AlreadyInYourLibrary')}
|
title="Movie is on Net Import Exclusion List"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isExclusionMovie &&
|
isSmallScreen ?
|
||||||
<Icon
|
null :
|
||||||
className={styles.exclusionIcon}
|
<div className={styles.externalLink}>
|
||||||
name={icons.DANGER}
|
<Link
|
||||||
size={36}
|
to={`https://www.themoviedb.org/movie/${tmdbId}`}
|
||||||
title={translate('MovieIsOnImportExclusionList')}
|
onPress={this.onExternalLinkPress}
|
||||||
/>
|
>
|
||||||
}
|
<Label size={sizes.LARGE}>
|
||||||
</div>
|
TMDb
|
||||||
|
</Label>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{
|
||||||
|
imdbId &&
|
||||||
|
<Link
|
||||||
|
to={`https://www.imdb.com/title/${imdbId}`}
|
||||||
|
onPress={this.onExternalLinkPress}
|
||||||
|
>
|
||||||
|
<Label size={sizes.LARGE}>
|
||||||
|
IMDb
|
||||||
|
</Label>
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to={`https://trakt.tv/search/tmdb/${tmdbId}?id_type=movie`}
|
||||||
|
onPress={this.onExternalLinkPress}
|
||||||
|
>
|
||||||
|
<Label size={sizes.LARGE}>
|
||||||
|
Trakt
|
||||||
|
</Label>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -148,33 +170,6 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
</Label>
|
</Label>
|
||||||
}
|
}
|
||||||
|
|
||||||
<Tooltip
|
|
||||||
anchor={
|
|
||||||
<Label
|
|
||||||
size={sizes.LARGE}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
name={icons.EXTERNAL_LINK}
|
|
||||||
size={13}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className={styles.links}>
|
|
||||||
Links
|
|
||||||
</span>
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
tooltip={
|
|
||||||
<MovieDetailsLinks
|
|
||||||
tmdbId={tmdbId}
|
|
||||||
youTubeTrailerId={youTubeTrailerId}
|
|
||||||
imdbId={imdbId}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
canFlip={true}
|
|
||||||
kind={kinds.INVERSE}
|
|
||||||
position={tooltipPositions.BOTTOM}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
status === 'ended' &&
|
status === 'ended' &&
|
||||||
<Label
|
<Label
|
||||||
@@ -186,6 +181,42 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
isSmallScreen ?
|
||||||
|
<div className={styles.externalLink}>
|
||||||
|
<Link
|
||||||
|
to={`https://www.themoviedb.org/movie/${tmdbId}`}
|
||||||
|
onPress={this.onExternalLinkPress}
|
||||||
|
>
|
||||||
|
<Label size={sizes.LARGE}>
|
||||||
|
TMDb
|
||||||
|
</Label>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{
|
||||||
|
imdbId &&
|
||||||
|
<Link
|
||||||
|
to={`https://www.imdb.com/title/${imdbId}`}
|
||||||
|
onPress={this.onExternalLinkPress}
|
||||||
|
>
|
||||||
|
<Label size={sizes.LARGE}>
|
||||||
|
IMDb
|
||||||
|
</Label>
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to={`https://trakt.tv/search/tmdb/${tmdbId}?id_type=movie`}
|
||||||
|
onPress={this.onExternalLinkPress}
|
||||||
|
>
|
||||||
|
<Label size={sizes.LARGE}>
|
||||||
|
Trakt
|
||||||
|
</Label>
|
||||||
|
</Link>
|
||||||
|
</div> :
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
<div className={styles.overview}>
|
<div className={styles.overview}>
|
||||||
{overview}
|
{overview}
|
||||||
</div>
|
</div>
|
||||||
@@ -210,7 +241,6 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
AddNewMovieSearchResult.propTypes = {
|
AddNewMovieSearchResult.propTypes = {
|
||||||
tmdbId: PropTypes.number.isRequired,
|
tmdbId: PropTypes.number.isRequired,
|
||||||
imdbId: PropTypes.string,
|
imdbId: PropTypes.string,
|
||||||
youTubeTrailerId: PropTypes.string,
|
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
titleSlug: PropTypes.string.isRequired,
|
titleSlug: PropTypes.string.isRequired,
|
||||||
year: PropTypes.number.isRequired,
|
year: PropTypes.number.isRequired,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, { Component } from 'react';
|
|||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||||
import selectAll from 'Utilities/Table/selectAll';
|
import selectAll from 'Utilities/Table/selectAll';
|
||||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||||
@@ -81,7 +80,6 @@ class ImportMovie extends Component {
|
|||||||
path,
|
path,
|
||||||
rootFoldersFetching,
|
rootFoldersFetching,
|
||||||
rootFoldersError,
|
rootFoldersError,
|
||||||
rootFoldersPopulated,
|
|
||||||
unmappedFolders
|
unmappedFolders
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -93,7 +91,7 @@ class ImportMovie extends Component {
|
|||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('ImportMovies')}>
|
<PageContent title="Import Movies">
|
||||||
<PageContentBody
|
<PageContentBody
|
||||||
registerScroller={this.setScrollerRef}
|
registerScroller={this.setScrollerRef}
|
||||||
onScroll={this.onScroll}
|
onScroll={this.onScroll}
|
||||||
@@ -104,16 +102,13 @@ class ImportMovie extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!rootFoldersFetching && !!rootFoldersError ?
|
!rootFoldersFetching && !!rootFoldersError ?
|
||||||
<div>
|
<div>Unable to load root folders</div> :
|
||||||
{translate('UnableToLoadRootFolders')}
|
|
||||||
</div> :
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!rootFoldersError &&
|
!rootFoldersError &&
|
||||||
!rootFoldersFetching &&
|
!rootFoldersFetching &&
|
||||||
rootFoldersPopulated &&
|
|
||||||
!unmappedFolders.length ?
|
!unmappedFolders.length ?
|
||||||
<div>
|
<div>
|
||||||
All movies in {path} have been imported
|
All movies in {path} have been imported
|
||||||
@@ -124,7 +119,6 @@ class ImportMovie extends Component {
|
|||||||
{
|
{
|
||||||
!rootFoldersError &&
|
!rootFoldersError &&
|
||||||
!rootFoldersFetching &&
|
!rootFoldersFetching &&
|
||||||
rootFoldersPopulated &&
|
|
||||||
!!unmappedFolders.length &&
|
!!unmappedFolders.length &&
|
||||||
scroller ?
|
scroller ?
|
||||||
<ImportMovieTableConnector
|
<ImportMovieTableConnector
|
||||||
|
|||||||
@@ -31,7 +31,3 @@
|
|||||||
margin: 0 10px 0 12px;
|
margin: 0 10px 0 12px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.importError {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,14 +3,11 @@ import PropTypes from 'prop-types';
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
// import CheckInput from 'Components/Form/CheckInput';
|
// import CheckInput from 'Components/Form/CheckInput';
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import Button from 'Components/Link/Button';
|
import Button from 'Components/Link/Button';
|
||||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
import { inputTypes, kinds } from 'Helpers/Props';
|
||||||
import { icons, inputTypes, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './ImportMovieFooter.css';
|
import styles from './ImportMovieFooter.css';
|
||||||
|
|
||||||
const MIXED = 'mixed';
|
const MIXED = 'mixed';
|
||||||
@@ -96,10 +93,7 @@ class ImportMovieFooter extends Component {
|
|||||||
isMonitorMixed,
|
isMonitorMixed,
|
||||||
isQualityProfileIdMixed,
|
isQualityProfileIdMixed,
|
||||||
isMinimumAvailabilityMixed,
|
isMinimumAvailabilityMixed,
|
||||||
hasUnsearchedItems,
|
|
||||||
importError,
|
|
||||||
onImportPress,
|
onImportPress,
|
||||||
onLookupPress,
|
|
||||||
onCancelLookupPress
|
onCancelLookupPress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -113,7 +107,7 @@ class ImportMovieFooter extends Component {
|
|||||||
<PageContentFooter>
|
<PageContentFooter>
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
{translate('Monitor')}
|
Monitor
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
@@ -128,7 +122,7 @@ class ImportMovieFooter extends Component {
|
|||||||
|
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
{translate('MinimumAvailability')}
|
Minimum Availability
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
@@ -143,7 +137,7 @@ class ImportMovieFooter extends Component {
|
|||||||
|
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
{translate('QualityProfile')}
|
Quality Profile
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
@@ -169,75 +163,31 @@ class ImportMovieFooter extends Component {
|
|||||||
isDisabled={!selectedCount || isLookingUpMovie}
|
isDisabled={!selectedCount || isLookingUpMovie}
|
||||||
onPress={onImportPress}
|
onPress={onImportPress}
|
||||||
>
|
>
|
||||||
{translate('Import')} {selectedCount} {selectedCount > 1 ? translate('Movies') : translate('Movie')}
|
Import {selectedCount} {selectedCount > 1 ? 'Movies' : 'Movie'}
|
||||||
</SpinnerButton>
|
</SpinnerButton>
|
||||||
|
|
||||||
{
|
{
|
||||||
isLookingUpMovie ?
|
isLookingUpMovie &&
|
||||||
<Button
|
<Button
|
||||||
className={styles.loadingButton}
|
className={styles.loadingButton}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
onPress={onCancelLookupPress}
|
onPress={onCancelLookupPress}
|
||||||
>
|
>
|
||||||
{translate('CancelProcessing')}
|
Cancel Processing
|
||||||
</Button> :
|
</Button>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
hasUnsearchedItems ?
|
isLookingUpMovie &&
|
||||||
<Button
|
|
||||||
className={styles.loadingButton}
|
|
||||||
kind={kinds.SUCCESS}
|
|
||||||
onPress={onLookupPress}
|
|
||||||
>
|
|
||||||
{translate('StartProcessing')}
|
|
||||||
</Button> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
isLookingUpMovie ?
|
|
||||||
<LoadingIndicator
|
<LoadingIndicator
|
||||||
className={styles.loading}
|
className={styles.loading}
|
||||||
size={24}
|
size={24}
|
||||||
/> :
|
/>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isLookingUpMovie ?
|
isLookingUpMovie &&
|
||||||
translate('ProcessingFolders') :
|
'Processing Folders'
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
importError ?
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Icon
|
|
||||||
className={styles.importError}
|
|
||||||
name={icons.WARNING}
|
|
||||||
kind={kinds.WARNING}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
title={translate('ImportErrors')}
|
|
||||||
body={
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
importError.responseJSON.map((error, index) => {
|
|
||||||
return (
|
|
||||||
<li key={index}>
|
|
||||||
{error.errorMessage}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
position={tooltipPositions.RIGHT}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -256,11 +206,8 @@ ImportMovieFooter.propTypes = {
|
|||||||
isMonitorMixed: PropTypes.bool.isRequired,
|
isMonitorMixed: PropTypes.bool.isRequired,
|
||||||
isQualityProfileIdMixed: PropTypes.bool.isRequired,
|
isQualityProfileIdMixed: PropTypes.bool.isRequired,
|
||||||
isMinimumAvailabilityMixed: PropTypes.bool.isRequired,
|
isMinimumAvailabilityMixed: PropTypes.bool.isRequired,
|
||||||
hasUnsearchedItems: PropTypes.bool.isRequired,
|
|
||||||
importError: PropTypes.object,
|
|
||||||
onInputChange: PropTypes.func.isRequired,
|
onInputChange: PropTypes.func.isRequired,
|
||||||
onImportPress: PropTypes.func.isRequired,
|
onImportPress: PropTypes.func.isRequired,
|
||||||
onLookupPress: PropTypes.func.isRequired,
|
|
||||||
onCancelLookupPress: PropTypes.func.isRequired
|
onCancelLookupPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { cancelLookupMovie, lookupUnsearchedMovies } from 'Store/Actions/importMovieActions';
|
import { cancelLookupMovie } from 'Store/Actions/importMovieActions';
|
||||||
import ImportMovieFooter from './ImportMovieFooter';
|
import ImportMovieFooter from './ImportMovieFooter';
|
||||||
|
|
||||||
function isMixed(items, selectedIds, defaultValue, key) {
|
function isMixed(items, selectedIds, defaultValue, key) {
|
||||||
@@ -25,14 +25,12 @@ function createMapStateToProps() {
|
|||||||
const {
|
const {
|
||||||
isLookingUpMovie,
|
isLookingUpMovie,
|
||||||
isImporting,
|
isImporting,
|
||||||
items,
|
items
|
||||||
importError
|
|
||||||
} = importMovie;
|
} = importMovie;
|
||||||
|
|
||||||
const isMonitorMixed = isMixed(items, selectedIds, defaultMonitor, 'monitor');
|
const isMonitorMixed = isMixed(items, selectedIds, defaultMonitor, 'monitor');
|
||||||
const isQualityProfileIdMixed = isMixed(items, selectedIds, defaultQualityProfileId, 'qualityProfileId');
|
const isQualityProfileIdMixed = isMixed(items, selectedIds, defaultQualityProfileId, 'qualityProfileId');
|
||||||
const isMinimumAvailabilityMixed = isMixed(items, selectedIds, defaultMinimumAvailability, 'minimumAvailability');
|
const isMinimumAvailabilityMixed = isMixed(items, selectedIds, defaultMinimumAvailability, 'minimumAvailability');
|
||||||
const hasUnsearchedItems = !isLookingUpMovie && items.some((item) => !item.isPopulated);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedCount: selectedIds.length,
|
selectedCount: selectedIds.length,
|
||||||
@@ -43,16 +41,13 @@ function createMapStateToProps() {
|
|||||||
defaultMinimumAvailability,
|
defaultMinimumAvailability,
|
||||||
isMonitorMixed,
|
isMonitorMixed,
|
||||||
isQualityProfileIdMixed,
|
isQualityProfileIdMixed,
|
||||||
isMinimumAvailabilityMixed,
|
isMinimumAvailabilityMixed
|
||||||
importError,
|
|
||||||
hasUnsearchedItems
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
onLookupPress: lookupUnsearchedMovies,
|
|
||||||
onCancelLookupPress: cancelLookupMovie
|
onCancelLookupPress: cancelLookupMovie
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React from 'react';
|
|||||||
import VirtualTableHeader from 'Components/Table/VirtualTableHeader';
|
import VirtualTableHeader from 'Components/Table/VirtualTableHeader';
|
||||||
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
|
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
|
||||||
import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell';
|
import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './ImportMovieHeader.css';
|
import styles from './ImportMovieHeader.css';
|
||||||
|
|
||||||
function ImportMovieHeader(props) {
|
function ImportMovieHeader(props) {
|
||||||
@@ -25,35 +24,35 @@ function ImportMovieHeader(props) {
|
|||||||
className={styles.folder}
|
className={styles.folder}
|
||||||
name="folder"
|
name="folder"
|
||||||
>
|
>
|
||||||
{translate('Folder')}
|
Folder
|
||||||
</VirtualTableHeaderCell>
|
</VirtualTableHeaderCell>
|
||||||
|
|
||||||
<VirtualTableHeaderCell
|
<VirtualTableHeaderCell
|
||||||
className={styles.monitor}
|
className={styles.monitor}
|
||||||
name="monitor"
|
name="monitor"
|
||||||
>
|
>
|
||||||
{translate('Monitor')}
|
Monitor
|
||||||
</VirtualTableHeaderCell>
|
</VirtualTableHeaderCell>
|
||||||
|
|
||||||
<VirtualTableHeaderCell
|
<VirtualTableHeaderCell
|
||||||
className={styles.minimumAvailability}
|
className={styles.minimumAvailability}
|
||||||
name="minimumAvailability"
|
name="minimumAvailability"
|
||||||
>
|
>
|
||||||
{translate('MinAvailability')}
|
Min Availability
|
||||||
</VirtualTableHeaderCell>
|
</VirtualTableHeaderCell>
|
||||||
|
|
||||||
<VirtualTableHeaderCell
|
<VirtualTableHeaderCell
|
||||||
className={styles.qualityProfile}
|
className={styles.qualityProfile}
|
||||||
name="qualityProfileId"
|
name="qualityProfileId"
|
||||||
>
|
>
|
||||||
{translate('QualityProfile')}
|
Quality Profile
|
||||||
</VirtualTableHeaderCell>
|
</VirtualTableHeaderCell>
|
||||||
|
|
||||||
<VirtualTableHeaderCell
|
<VirtualTableHeaderCell
|
||||||
className={styles.movie}
|
className={styles.movie}
|
||||||
name="movie"
|
name="movie"
|
||||||
>
|
>
|
||||||
{translate('Movie')}
|
Movie
|
||||||
</VirtualTableHeaderCell>
|
</VirtualTableHeaderCell>
|
||||||
</VirtualTableHeader>
|
</VirtualTableHeader>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|||||||
import Portal from 'Components/Portal';
|
import Portal from 'Components/Portal';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import ImportMovieSearchResultConnector from './ImportMovieSearchResultConnector';
|
import ImportMovieSearchResultConnector from './ImportMovieSearchResultConnector';
|
||||||
import ImportMovieTitle from './ImportMovieTitle';
|
import ImportMovieTitle from './ImportMovieTitle';
|
||||||
import styles from './ImportMovieSelectMovie.css';
|
import styles from './ImportMovieSelectMovie.css';
|
||||||
@@ -175,7 +174,7 @@ class ImportMovieSelectMovie extends Component {
|
|||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{translate('NoMatchFound')}
|
No match found!
|
||||||
</div> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -190,7 +189,7 @@ class ImportMovieSelectMovie extends Component {
|
|||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{translate('SearchFailedPleaseTryAgainLater')}
|
Search failed, please try again later.
|
||||||
</div> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './ImportMovieTitle.css';
|
import styles from './ImportMovieTitle.css';
|
||||||
|
|
||||||
function ImportMovieTitle(props) {
|
function ImportMovieTitle(props) {
|
||||||
@@ -34,7 +33,7 @@ function ImportMovieTitle(props) {
|
|||||||
<Label
|
<Label
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
>
|
>
|
||||||
{translate('Existing')}
|
Existing
|
||||||
</Label>
|
</Label>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './ImportMovieRootFolderRow.css';
|
import styles from './ImportMovieRootFolderRow.css';
|
||||||
|
|
||||||
function ImportMovieRootFolderRow(props) {
|
function ImportMovieRootFolderRow(props) {
|
||||||
@@ -41,7 +40,7 @@ function ImportMovieRootFolderRow(props) {
|
|||||||
|
|
||||||
<TableRowCell className={styles.actions}>
|
<TableRowCell className={styles.actions}>
|
||||||
<IconButton
|
<IconButton
|
||||||
title={translate('RemoveRootFolder')}
|
title="Remove root folder"
|
||||||
name={icons.REMOVE}
|
name={icons.REMOVE}
|
||||||
onPress={onDeletePress}
|
onPress={onDeletePress}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ import styles from './ImportMovieSelectFolder.css';
|
|||||||
const rootFolderColumns = [
|
const rootFolderColumns = [
|
||||||
{
|
{
|
||||||
name: 'path',
|
name: 'path',
|
||||||
label: translate('Path'),
|
label: 'Path',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'freeSpace',
|
name: 'freeSpace',
|
||||||
label: translate('FreeSpace'),
|
label: 'Free Space',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'unmappedFolders',
|
name: 'unmappedFolders',
|
||||||
label: translate('UnmappedFolders'),
|
label: 'Unmapped Folders',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -77,7 +77,7 @@ class ImportMovieSelectFolder extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('ImportMovies')}>
|
<PageContent title="Import Movies">
|
||||||
<PageContentBody>
|
<PageContentBody>
|
||||||
{
|
{
|
||||||
isFetching && !isPopulated &&
|
isFetching && !isPopulated &&
|
||||||
@@ -86,9 +86,7 @@ class ImportMovieSelectFolder extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && !!error &&
|
!isFetching && !!error &&
|
||||||
<div>
|
<div>Unable to load root folders</div>
|
||||||
{translate('UnableToLoadRootFolders')}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -101,15 +99,19 @@ class ImportMovieSelectFolder extends Component {
|
|||||||
<div className={styles.tips}>
|
<div className={styles.tips}>
|
||||||
{translate('ImportTipsMessage')}
|
{translate('ImportTipsMessage')}
|
||||||
<ul>
|
<ul>
|
||||||
<li className={styles.tip} dangerouslySetInnerHTML={{ __html: translate('ImportIncludeQuality', ['<code>movie.2008.bluray.mkv</code>']) }} />
|
<li className={styles.tip}>
|
||||||
<li className={styles.tip} dangerouslySetInnerHTML={{ __html: translate('ImportRootPath', [`<code>${isWindows ? 'C:\\movies' : '/movies'}</code>`, `<code>${isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}</code>`]) }} />
|
Make sure that your files include the quality in their filenames. eg. <span className={styles.code}>movie.2008.bluray.mkv</span>
|
||||||
|
</li>
|
||||||
|
<li className={styles.tip}>
|
||||||
|
Point Radarr to the folder containing all of your movies, not a specific one. eg. <span className={styles.code}>"{isWindows ? 'C:\\movies' : '/movies'}"</span> and not <span className={styles.code}>"{isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}"</span>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
items.length > 0 ?
|
items.length > 0 ?
|
||||||
<div className={styles.recentFolders}>
|
<div className={styles.recentFolders}>
|
||||||
<FieldSet legend={translate('RecentFolders')}>
|
<FieldSet legend="Recent Folders">
|
||||||
<Table
|
<Table
|
||||||
columns={rootFolderColumns}
|
columns={rootFolderColumns}
|
||||||
>
|
>
|
||||||
@@ -154,7 +156,7 @@ class ImportMovieSelectFolder extends Component {
|
|||||||
className={styles.importButtonIcon}
|
className={styles.importButtonIcon}
|
||||||
name={icons.DRIVE}
|
name={icons.DRIVE}
|
||||||
/>
|
/>
|
||||||
{translate('StartImport')}
|
Start Import
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ import MovieIndexConnector from 'Movie/Index/MovieIndexConnector';
|
|||||||
import CustomFormatSettingsConnector from 'Settings/CustomFormats/CustomFormatSettingsConnector';
|
import CustomFormatSettingsConnector from 'Settings/CustomFormats/CustomFormatSettingsConnector';
|
||||||
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
|
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
|
||||||
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
|
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
|
||||||
import ImportListSettingsConnector from 'Settings/ImportLists/ImportListSettingsConnector';
|
|
||||||
import IndexerSettingsConnector from 'Settings/Indexers/IndexerSettingsConnector';
|
import IndexerSettingsConnector from 'Settings/Indexers/IndexerSettingsConnector';
|
||||||
import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementConnector';
|
import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementConnector';
|
||||||
import MetadataSettings from 'Settings/Metadata/MetadataSettings';
|
import MetadataSettings from 'Settings/Metadata/MetadataSettings';
|
||||||
|
import NetImportSettingsConnector from 'Settings/NetImport/NetImportSettingsConnector';
|
||||||
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
|
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
|
||||||
import Profiles from 'Settings/Profiles/Profiles';
|
import Profiles from 'Settings/Profiles/Profiles';
|
||||||
import Quality from 'Settings/Quality/Quality';
|
import Quality from 'Settings/Quality/Quality';
|
||||||
@@ -156,8 +156,8 @@ function AppRoutes(props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/settings/importlists"
|
path="/settings/netimports"
|
||||||
component={ImportListSettingsConnector}
|
component={NetImportSettingsConnector}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import UpdateChanges from 'System/Updates/UpdateChanges';
|
import UpdateChanges from 'System/Updates/UpdateChanges';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './AppUpdatedModalContent.css';
|
import styles from './AppUpdatedModalContent.css';
|
||||||
|
|
||||||
function AppUpdatedModalContent(props) {
|
function AppUpdatedModalContent(props) {
|
||||||
@@ -50,12 +49,12 @@ function AppUpdatedModalContent(props) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UpdateChanges
|
<UpdateChanges
|
||||||
title={translate('New')}
|
title="New"
|
||||||
changes={update.changes.new}
|
changes={update.changes.new}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UpdateChanges
|
<UpdateChanges
|
||||||
title={translate('Fixed')}
|
title="Fixed"
|
||||||
changes={update.changes.fixed}
|
changes={update.changes.fixed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './ConnectionLostModal.css';
|
import styles from './ConnectionLostModal.css';
|
||||||
|
|
||||||
function ConnectionLostModal(props) {
|
function ConnectionLostModal(props) {
|
||||||
@@ -23,16 +22,16 @@ function ConnectionLostModal(props) {
|
|||||||
>
|
>
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
{translate('ConnectionLost')}
|
Connnection Lost
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<div>
|
||||||
{translate('ConnectionLostMessage')}
|
Radarr has lost it's connection to the backend and will need to be reloaded to restore functionality.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.automatic}>
|
<div className={styles.automatic}>
|
||||||
{translate('ConnectionLostAutomaticMessage')}
|
Radarr will try to connect automatically, or you can click reload below.
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
@@ -40,7 +39,7 @@ function ConnectionLostModal(props) {
|
|||||||
kind={kinds.PRIMARY}
|
kind={kinds.PRIMARY}
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
{translate('Reload')}
|
Reload
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -6,38 +6,9 @@ import styles from './Agenda.css';
|
|||||||
|
|
||||||
function Agenda(props) {
|
function Agenda(props) {
|
||||||
const {
|
const {
|
||||||
items,
|
items
|
||||||
start,
|
|
||||||
end
|
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const startDateParsed = Date.parse(start);
|
|
||||||
const endDateParsed = Date.parse(end);
|
|
||||||
|
|
||||||
items.forEach((item) => {
|
|
||||||
const cinemaDateParsed = Date.parse(item.inCinemas);
|
|
||||||
const digitalDateParsed = Date.parse(item.digitalRelease);
|
|
||||||
const physicalDateParsed = Date.parse(item.physicalRelease);
|
|
||||||
const dates = [];
|
|
||||||
|
|
||||||
if (cinemaDateParsed > 0 && cinemaDateParsed >= startDateParsed && cinemaDateParsed <= endDateParsed) {
|
|
||||||
dates.push(cinemaDateParsed);
|
|
||||||
}
|
|
||||||
if (digitalDateParsed > 0 && digitalDateParsed >= startDateParsed && digitalDateParsed <= endDateParsed) {
|
|
||||||
dates.push(digitalDateParsed);
|
|
||||||
}
|
|
||||||
if (physicalDateParsed > 0 && physicalDateParsed >= startDateParsed && physicalDateParsed <= endDateParsed) {
|
|
||||||
dates.push(physicalDateParsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.sortDate = Math.min(...dates);
|
|
||||||
item.cinemaDateParsed = cinemaDateParsed;
|
|
||||||
item.digitalDateParsed = digitalDateParsed;
|
|
||||||
item.physicalDateParsed = physicalDateParsed;
|
|
||||||
});
|
|
||||||
|
|
||||||
items.sort((a, b) => ((a.sortDate > b.sortDate) ? 1 : -1));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.agenda}>
|
<div className={styles.agenda}>
|
||||||
{
|
{
|
||||||
@@ -61,9 +32,7 @@ function Agenda(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Agenda.propTypes = {
|
Agenda.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||||
start: PropTypes.string.isRequired,
|
|
||||||
end: PropTypes.string.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Agenda;
|
export default Agenda;
|
||||||
|
|||||||
@@ -10,10 +10,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
|
||||||
composes: link from '~Calendar/Events/CalendarEvent.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.eventWrapper {
|
.eventWrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 0 1px;
|
flex: 1 0 1px;
|
||||||
@@ -34,8 +30,7 @@
|
|||||||
border: none !important;
|
border: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.movieTitle,
|
.movieTitle {
|
||||||
.genres {
|
|
||||||
@add-mixin truncate;
|
@add-mixin truncate;
|
||||||
|
|
||||||
flex: 0 1 300px;
|
flex: 0 1 300px;
|
||||||
@@ -66,10 +61,6 @@
|
|||||||
composes: missing from '~Calendar/Events/CalendarEvent.css';
|
composes: missing from '~Calendar/Events/CalendarEvent.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
.unreleased {
|
|
||||||
composes: unreleased from '~Calendar/Events/CalendarEvent.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointSmall) {
|
@media only screen and (max-width: $breakpointSmall) {
|
||||||
.event {
|
.event {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -90,7 +81,3 @@
|
|||||||
flex: 0 0 100%;
|
flex: 0 0 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dateIcon {
|
|
||||||
width: 25px;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import getStatusStyle from 'Calendar/getStatusStyle';
|
|||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import MovieTitleLink from 'Movie/MovieTitleLink';
|
||||||
import styles from './AgendaEvent.css';
|
import styles from './AgendaEvent.css';
|
||||||
|
|
||||||
class AgendaEvent extends Component {
|
class AgendaEvent extends Component {
|
||||||
@@ -41,69 +41,35 @@ class AgendaEvent extends Component {
|
|||||||
movieFile,
|
movieFile,
|
||||||
title,
|
title,
|
||||||
titleSlug,
|
titleSlug,
|
||||||
genres,
|
|
||||||
isAvailable,
|
isAvailable,
|
||||||
inCinemas,
|
inCinemas,
|
||||||
digitalRelease,
|
|
||||||
physicalRelease,
|
|
||||||
monitored,
|
monitored,
|
||||||
hasFile,
|
hasFile,
|
||||||
grabbed,
|
grabbed,
|
||||||
queueItem,
|
queueItem,
|
||||||
showDate,
|
showDate,
|
||||||
showMovieInformation,
|
|
||||||
showCutoffUnmetIcon,
|
showCutoffUnmetIcon,
|
||||||
longDateFormat,
|
longDateFormat,
|
||||||
colorImpairedMode,
|
colorImpairedMode
|
||||||
cinemaDateParsed,
|
|
||||||
digitalDateParsed,
|
|
||||||
physicalDateParsed,
|
|
||||||
sortDate
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let startTime = null;
|
const startTime = moment(inCinemas);
|
||||||
let releaseIcon = null;
|
|
||||||
|
|
||||||
if (physicalDateParsed === sortDate) {
|
|
||||||
startTime = physicalRelease;
|
|
||||||
releaseIcon = icons.DISC;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (digitalDateParsed === sortDate) {
|
|
||||||
startTime = digitalRelease;
|
|
||||||
releaseIcon = icons.MOVIE_FILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cinemaDateParsed === sortDate) {
|
|
||||||
startTime = inCinemas;
|
|
||||||
releaseIcon = icons.IN_CINEMAS;
|
|
||||||
}
|
|
||||||
|
|
||||||
startTime = moment(startTime);
|
|
||||||
const downloading = !!(queueItem || grabbed);
|
const downloading = !!(queueItem || grabbed);
|
||||||
const isMonitored = monitored;
|
const isMonitored = monitored;
|
||||||
const statusStyle = getStatusStyle(hasFile, downloading, isAvailable, isMonitored);
|
const statusStyle = getStatusStyle(hasFile, downloading, isAvailable, isMonitored);
|
||||||
const joinedGenres = genres.slice(0, 2).join(', ');
|
|
||||||
const link = `/movie/${titleSlug}`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
className={classNames(
|
className={styles.event}
|
||||||
styles.event,
|
component="div"
|
||||||
styles.link
|
onPress={this.onPress}
|
||||||
)}
|
|
||||||
to={link}
|
|
||||||
>
|
>
|
||||||
<div className={styles.dateIcon}>
|
|
||||||
<Icon
|
|
||||||
name={releaseIcon}
|
|
||||||
kind={kinds.DEFAULT}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.date}>
|
<div className={styles.date}>
|
||||||
{(showDate) ? startTime.format(longDateFormat) : null}
|
{
|
||||||
|
showDate &&
|
||||||
|
startTime.format(longDateFormat)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -114,16 +80,12 @@ class AgendaEvent extends Component {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={styles.movieTitle}>
|
<div className={styles.movieTitle}>
|
||||||
{title}
|
<MovieTitleLink
|
||||||
|
titleSlug={titleSlug}
|
||||||
|
title={title}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
|
||||||
showMovieInformation &&
|
|
||||||
<div className={styles.genres}>
|
|
||||||
{joinedGenres}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
!!queueItem &&
|
!!queueItem &&
|
||||||
<span className={styles.statusIcon}>
|
<span className={styles.statusIcon}>
|
||||||
@@ -138,7 +100,7 @@ class AgendaEvent extends Component {
|
|||||||
<Icon
|
<Icon
|
||||||
className={styles.statusIcon}
|
className={styles.statusIcon}
|
||||||
name={icons.DOWNLOADING}
|
name={icons.DOWNLOADING}
|
||||||
title={translate('MovieIsDownloading')}
|
title="Movie is downloading"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +112,7 @@ class AgendaEvent extends Component {
|
|||||||
className={styles.statusIcon}
|
className={styles.statusIcon}
|
||||||
name={icons.MOVIE_FILE}
|
name={icons.MOVIE_FILE}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
title={translate('QualityCutoffHasNotBeenMet')}
|
title="Quality cutoff has not been met"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -165,29 +127,17 @@ AgendaEvent.propTypes = {
|
|||||||
movieFile: PropTypes.object,
|
movieFile: PropTypes.object,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
titleSlug: PropTypes.string.isRequired,
|
titleSlug: PropTypes.string.isRequired,
|
||||||
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
isAvailable: PropTypes.bool.isRequired,
|
isAvailable: PropTypes.bool.isRequired,
|
||||||
inCinemas: PropTypes.string,
|
inCinemas: PropTypes.string,
|
||||||
digitalRelease: PropTypes.string,
|
|
||||||
physicalRelease: PropTypes.string,
|
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
hasFile: PropTypes.bool.isRequired,
|
hasFile: PropTypes.bool.isRequired,
|
||||||
grabbed: PropTypes.bool,
|
grabbed: PropTypes.bool,
|
||||||
queueItem: PropTypes.object,
|
queueItem: PropTypes.object,
|
||||||
showDate: PropTypes.bool.isRequired,
|
showDate: PropTypes.bool.isRequired,
|
||||||
showMovieInformation: PropTypes.bool.isRequired,
|
|
||||||
showCutoffUnmetIcon: PropTypes.bool.isRequired,
|
showCutoffUnmetIcon: PropTypes.bool.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired,
|
timeFormat: PropTypes.string.isRequired,
|
||||||
longDateFormat: PropTypes.string.isRequired,
|
longDateFormat: PropTypes.string.isRequired,
|
||||||
colorImpairedMode: PropTypes.bool.isRequired,
|
colorImpairedMode: PropTypes.bool.isRequired
|
||||||
cinemaDateParsed: PropTypes.number,
|
|
||||||
digitalDateParsed: PropTypes.number,
|
|
||||||
physicalDateParsed: PropTypes.number,
|
|
||||||
sortDate: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
AgendaEvent.defaultProps = {
|
|
||||||
genres: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AgendaEvent;
|
export default AgendaEvent;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import AgendaConnector from './Agenda/AgendaConnector';
|
import AgendaConnector from './Agenda/AgendaConnector';
|
||||||
import * as calendarViews from './calendarViews';
|
import * as calendarViews from './calendarViews';
|
||||||
import CalendarDaysConnector from './Day/CalendarDaysConnector';
|
import CalendarDaysConnector from './Day/CalendarDaysConnector';
|
||||||
@@ -31,9 +30,7 @@ class Calendar extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && !!error &&
|
!isFetching && !!error &&
|
||||||
<div>
|
<div>Unable to load the calendar</div>
|
||||||
{translate('UnableToLoadTheCalendar')}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -76,15 +76,16 @@ class CalendarConnector extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (hasDifferentItems(prevProps.items, items)) {
|
if (hasDifferentItems(prevProps.items, items)) {
|
||||||
|
const movieIds = selectUniqueIds(items, 'id');
|
||||||
const movieFileIds = selectUniqueIds(items, 'movieFileId');
|
const movieFileIds = selectUniqueIds(items, 'movieFileId');
|
||||||
|
|
||||||
|
if (items.length) {
|
||||||
|
this.props.fetchQueueDetails({ movieIds });
|
||||||
|
}
|
||||||
|
|
||||||
if (movieFileIds.length) {
|
if (movieFileIds.length) {
|
||||||
this.props.fetchMovieFiles({ movieFileIds });
|
this.props.fetchMovieFiles({ movieFileIds });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.length) {
|
|
||||||
this.props.fetchQueueDetails();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevProps.time !== time) {
|
if (prevProps.time !== time) {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class CalendarPage extends Component {
|
|||||||
const isMeasured = this.state.width > 0;
|
const isMeasured = this.state.width > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('Calendar')}>
|
<PageContent title="Calendar">
|
||||||
<PageToolbar>
|
<PageToolbar>
|
||||||
<PageToolbarSection>
|
<PageToolbarSection>
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
@@ -110,7 +110,7 @@ class CalendarPage extends Component {
|
|||||||
<PageToolbarSeparator />
|
<PageToolbarSeparator />
|
||||||
|
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
label={translate('RSSSync')}
|
label={translate('RssSync')}
|
||||||
iconName={icons.RSS}
|
iconName={icons.RSS}
|
||||||
isSpinning={isRssSyncExecuting}
|
isSpinning={isRssSyncExecuting}
|
||||||
onPress={onRssSyncPress}
|
onPress={onRssSyncPress}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import getStatusStyle from 'Calendar/getStatusStyle';
|
|||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import CalendarEventQueueDetails from './CalendarEventQueueDetails';
|
import CalendarEventQueueDetails from './CalendarEventQueueDetails';
|
||||||
import styles from './CalendarEvent.css';
|
import styles from './CalendarEvent.css';
|
||||||
|
|
||||||
@@ -86,7 +85,7 @@ class CalendarEvent extends Component {
|
|||||||
<Icon
|
<Icon
|
||||||
className={styles.statusIcon}
|
className={styles.statusIcon}
|
||||||
name={icons.DOWNLOADING}
|
name={icons.DOWNLOADING}
|
||||||
title={translate('MovieIsDownloading')}
|
title="movie is downloading"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ class CalendarEvent extends Component {
|
|||||||
className={styles.statusIcon}
|
className={styles.statusIcon}
|
||||||
name={icons.MOVIE_FILE}
|
name={icons.MOVIE_FILE}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
title={translate('QualityCutoffHasNotBeenMet')}
|
title="Quality cutoff has not been met"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React from 'react';
|
|||||||
import QueueDetails from 'Activity/Queue/QueueDetails';
|
import QueueDetails from 'Activity/Queue/QueueDetails';
|
||||||
import CircularProgressBar from 'Components/CircularProgressBar';
|
import CircularProgressBar from 'Components/CircularProgressBar';
|
||||||
import colors from 'Styles/Variables/colors';
|
import colors from 'Styles/Variables/colors';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
function CalendarEventQueueDetails(props) {
|
function CalendarEventQueueDetails(props) {
|
||||||
const {
|
const {
|
||||||
@@ -12,12 +11,10 @@ function CalendarEventQueueDetails(props) {
|
|||||||
sizeleft,
|
sizeleft,
|
||||||
estimatedCompletionTime,
|
estimatedCompletionTime,
|
||||||
status,
|
status,
|
||||||
trackedDownloadState,
|
|
||||||
trackedDownloadStatus,
|
|
||||||
errorMessage
|
errorMessage
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
const progress = (100 - sizeleft / size * 100);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueueDetails
|
<QueueDetails
|
||||||
@@ -26,11 +23,9 @@ function CalendarEventQueueDetails(props) {
|
|||||||
sizeleft={sizeleft}
|
sizeleft={sizeleft}
|
||||||
estimatedCompletionTime={estimatedCompletionTime}
|
estimatedCompletionTime={estimatedCompletionTime}
|
||||||
status={status}
|
status={status}
|
||||||
trackedDownloadState={trackedDownloadState}
|
|
||||||
trackedDownloadStatus={trackedDownloadStatus}
|
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
progressBar={
|
progressBar={
|
||||||
<div title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}>
|
<div title={`Movie is downloading - ${progress.toFixed(1)}% ${title}`}>
|
||||||
<CircularProgressBar
|
<CircularProgressBar
|
||||||
progress={progress}
|
progress={progress}
|
||||||
size={20}
|
size={20}
|
||||||
@@ -49,8 +44,6 @@ CalendarEventQueueDetails.propTypes = {
|
|||||||
sizeleft: PropTypes.number.isRequired,
|
sizeleft: PropTypes.number.isRequired,
|
||||||
estimatedCompletionTime: PropTypes.string,
|
estimatedCompletionTime: PropTypes.string,
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
trackedDownloadState: PropTypes.string.isRequired,
|
|
||||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
|
||||||
errorMessage: PropTypes.string
|
errorMessage: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function getTitle(time, start, end, view, longDateFormat) {
|
|||||||
} else if (view === 'month') {
|
} else if (view === 'month') {
|
||||||
return timeMoment.format('MMMM YYYY');
|
return timeMoment.format('MMMM YYYY');
|
||||||
} else if (view === 'agenda') {
|
} else if (view === 'agenda') {
|
||||||
return `Agenda: ${startMoment.format('MMM D')} - ${endMoment.format('MMM D')}`;
|
return 'Agenda';
|
||||||
}
|
}
|
||||||
|
|
||||||
let startFormat = 'MMM D YYYY';
|
let startFormat = 'MMM D YYYY';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
@@ -13,16 +14,19 @@ function createMapStateToProps() {
|
|||||||
createDimensionsSelector(),
|
createDimensionsSelector(),
|
||||||
createUISettingsSelector(),
|
createUISettingsSelector(),
|
||||||
(calendar, dimensions, uiSettings) => {
|
(calendar, dimensions, uiSettings) => {
|
||||||
return {
|
const result = _.pick(calendar, [
|
||||||
isFetching: calendar.isFetching,
|
'isFetching',
|
||||||
view: calendar.view,
|
'view',
|
||||||
time: calendar.time,
|
'time',
|
||||||
start: calendar.start,
|
'start',
|
||||||
end: calendar.end,
|
'end'
|
||||||
isSmallScreen: dimensions.isSmallScreen,
|
]);
|
||||||
collapseViewButtons: dimensions.isLargeScreen,
|
|
||||||
longDateFormat: uiSettings.longDateFormat
|
result.isSmallScreen = dimensions.isSmallScreen;
|
||||||
};
|
result.collapseViewButtons = dimensions.isLargeScreen;
|
||||||
|
result.longDateFormat = uiSettings.longDateFormat;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { inputTypes } from 'Helpers/Props';
|
import { inputTypes } from 'Helpers/Props';
|
||||||
import { firstDayOfWeekOptions, timeFormatOptions, weekColumnOptions } from 'Settings/UI/UISettings';
|
import { firstDayOfWeekOptions, timeFormatOptions, weekColumnOptions } from 'Settings/UI/UISettings';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
class CalendarOptionsModalContent extends Component {
|
class CalendarOptionsModalContent extends Component {
|
||||||
|
|
||||||
@@ -111,38 +110,38 @@ class CalendarOptionsModalContent extends Component {
|
|||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<FieldSet legend={translate('Local')}>
|
<FieldSet legend="Local">
|
||||||
<Form>
|
<Form>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('ShowMovieInformation')}</FormLabel>
|
<FormLabel>Show Movie Information</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="showMovieInformation"
|
name="showMovieInformation"
|
||||||
value={showMovieInformation}
|
value={showMovieInformation}
|
||||||
helpText={translate('ShowMovieInformationHelpText')}
|
helpText="Show movie genres and certification"
|
||||||
onChange={this.onOptionInputChange}
|
onChange={this.onOptionInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('IconForCutoffUnmet')}</FormLabel>
|
<FormLabel>Icon for Cutoff Unmet</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="showCutoffUnmetIcon"
|
name="showCutoffUnmetIcon"
|
||||||
value={showCutoffUnmetIcon}
|
value={showCutoffUnmetIcon}
|
||||||
helpText={translate('ShowCutoffUnmetIconHelpText')}
|
helpText="Show icon for files when the cutoff hasn't been met"
|
||||||
onChange={this.onOptionInputChange}
|
onChange={this.onOptionInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Form>
|
</Form>
|
||||||
</FieldSet>
|
</FieldSet>
|
||||||
|
|
||||||
<FieldSet legend={translate('Global')}>
|
<FieldSet legend="Global">
|
||||||
<Form>
|
<Form>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('FirstDayOfWeek')}</FormLabel>
|
<FormLabel>First Day of Week</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.SELECT}
|
type={inputTypes.SELECT}
|
||||||
@@ -154,7 +153,7 @@ class CalendarOptionsModalContent extends Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('WeekColumnHeader')}</FormLabel>
|
<FormLabel>Week Column Header</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.SELECT}
|
type={inputTypes.SELECT}
|
||||||
@@ -162,12 +161,12 @@ class CalendarOptionsModalContent extends Component {
|
|||||||
values={weekColumnOptions}
|
values={weekColumnOptions}
|
||||||
value={calendarWeekColumnHeader}
|
value={calendarWeekColumnHeader}
|
||||||
onChange={this.onGlobalInputChange}
|
onChange={this.onGlobalInputChange}
|
||||||
helpText={translate('HelpText')}
|
helpText="Shown above each column when week is the active view"
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('TimeFormat')}</FormLabel>
|
<FormLabel>Time Format</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.SELECT}
|
type={inputTypes.SELECT}
|
||||||
@@ -177,13 +176,13 @@ class CalendarOptionsModalContent extends Component {
|
|||||||
onChange={this.onGlobalInputChange}
|
onChange={this.onGlobalInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup><FormGroup>
|
</FormGroup><FormGroup>
|
||||||
<FormLabel>{translate('EnableColorImpairedMode')}</FormLabel>
|
<FormLabel>Enable Color-Impaired Mode</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="enableColorImpairedMode"
|
name="enableColorImpairedMode"
|
||||||
value={enableColorImpairedMode}
|
value={enableColorImpairedMode}
|
||||||
helpText={translate('EnableColorImpairedModeHelpText')}
|
helpText="Altered style to allow color-impaired users to better distinguish color coded information"
|
||||||
onChange={this.onGlobalInputChange}
|
onChange={this.onGlobalInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -194,7 +193,7 @@ class CalendarOptionsModalContent extends Component {
|
|||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button onPress={onModalClose}>
|
<Button onPress={onModalClose}>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { icons, inputTypes, kinds, sizes } from 'Helpers/Props';
|
import { icons, inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
function getUrls(state) {
|
function getUrls(state) {
|
||||||
const {
|
const {
|
||||||
@@ -115,37 +114,37 @@ class CalendarLinkModalContent extends Component {
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
<Form>
|
<Form>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('IncludeUnmonitored')}</FormLabel>
|
<FormLabel>Include Unmonitored</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="unmonitored"
|
name="unmonitored"
|
||||||
value={unmonitored}
|
value={unmonitored}
|
||||||
helpText={translate('UnmonitoredHelpText')}
|
helpText="Include unmonitored movies in the iCal feed"
|
||||||
onChange={this.onInputChange}
|
onChange={this.onInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('ShowAsAllDayEvents')}</FormLabel>
|
<FormLabel>Show as All-Day Events</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="asAllDay"
|
name="asAllDay"
|
||||||
value={asAllDay}
|
value={asAllDay}
|
||||||
helpText={translate('AsAllDayHelpText')}
|
helpText="Events will appear as all-day events in your calendar"
|
||||||
onChange={this.onInputChange}
|
onChange={this.onInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('Tags')}</FormLabel>
|
<FormLabel>Tags</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.TAG}
|
type={inputTypes.TAG}
|
||||||
name="tags"
|
name="tags"
|
||||||
value={tags}
|
value={tags}
|
||||||
helpText={translate('TagsHelpText')}
|
helpText="Feed will only contain movies with at least one matching tag"
|
||||||
onChange={this.onInputChange}
|
onChange={this.onInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -153,14 +152,14 @@ class CalendarLinkModalContent extends Component {
|
|||||||
<FormGroup
|
<FormGroup
|
||||||
size={sizes.LARGE}
|
size={sizes.LARGE}
|
||||||
>
|
>
|
||||||
<FormLabel>{translate('ICalFeed')}</FormLabel>
|
<FormLabel>iCal Feed</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.TEXT}
|
type={inputTypes.TEXT}
|
||||||
name="iCalHttpUrl"
|
name="iCalHttpUrl"
|
||||||
value={iCalHttpUrl}
|
value={iCalHttpUrl}
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
helpText={translate('ICalHttpUrlHelpText')}
|
helpText="Copy this URL to your client(s) or click to subscribe if your browser supports webcal"
|
||||||
buttons={[
|
buttons={[
|
||||||
<ClipboardButton
|
<ClipboardButton
|
||||||
key="copy"
|
key="copy"
|
||||||
@@ -187,7 +186,7 @@ class CalendarLinkModalContent extends Component {
|
|||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button onPress={onModalClose}>
|
<Button onPress={onModalClose}>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -16,4 +16,3 @@ export const RENAME_MOVIE = 'RenameMovie';
|
|||||||
export const RESET_API_KEY = 'ResetApiKey';
|
export const RESET_API_KEY = 'ResetApiKey';
|
||||||
export const RSS_SYNC = 'RssSync';
|
export const RSS_SYNC = 'RssSync';
|
||||||
export const MOVIE_SEARCH = 'MoviesSearch';
|
export const MOVIE_SEARCH = 'MoviesSearch';
|
||||||
export const IMPORT_LIST_SYNC = 'ImportListSync';
|
|
||||||
|
|||||||
@@ -14,19 +14,18 @@ import Scroller from 'Components/Scroller/Scroller';
|
|||||||
import Table from 'Components/Table/Table';
|
import Table from 'Components/Table/Table';
|
||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import { kinds, scrollDirections } from 'Helpers/Props';
|
import { kinds, scrollDirections } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import FileBrowserRow from './FileBrowserRow';
|
import FileBrowserRow from './FileBrowserRow';
|
||||||
import styles from './FileBrowserModalContent.css';
|
import styles from './FileBrowserModalContent.css';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'type',
|
name: 'type',
|
||||||
label: translate('Type'),
|
label: 'Type',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: translate('Name'),
|
label: 'Name',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -135,7 +134,7 @@ class FileBrowserModalContent extends Component {
|
|||||||
|
|
||||||
<PathInput
|
<PathInput
|
||||||
className={styles.pathInput}
|
className={styles.pathInput}
|
||||||
placeholder={translate('StartTypingOrSelectAPathBelow')}
|
placeholder="Start typing or select a path below"
|
||||||
hasFileBrowser={false}
|
hasFileBrowser={false}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
value={this.state.currentPath}
|
value={this.state.currentPath}
|
||||||
@@ -149,9 +148,7 @@ class FileBrowserModalContent extends Component {
|
|||||||
>
|
>
|
||||||
{
|
{
|
||||||
!!error &&
|
!!error &&
|
||||||
<div>
|
<div>Error loading contents</div>
|
||||||
{translate('ErrorLoadingContents')}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -226,13 +223,13 @@ class FileBrowserModalContent extends Component {
|
|||||||
<Button
|
<Button
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
{translate('Cancel')}
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onPress={this.onOkPress}
|
onPress={this.onOkPress}
|
||||||
>
|
>
|
||||||
{translate('Ok')}
|
Ok
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { Component } from 'react';
|
|||||||
import NumberInput from 'Components/Form/NumberInput';
|
import NumberInput from 'Components/Form/NumberInput';
|
||||||
import SelectInput from 'Components/Form/SelectInput';
|
import SelectInput from 'Components/Form/SelectInput';
|
||||||
import TextInput from 'Components/Form/TextInput';
|
import TextInput from 'Components/Form/TextInput';
|
||||||
import { IN_LAST, IN_NEXT, NOT_IN_LAST, NOT_IN_NEXT } from 'Helpers/Props/filterTypes';
|
import { IN_LAST, IN_NEXT } from 'Helpers/Props/filterTypes';
|
||||||
import isString from 'Utilities/String/isString';
|
import isString from 'Utilities/String/isString';
|
||||||
import { NAME } from './FilterBuilderRowValue';
|
import { NAME } from './FilterBuilderRowValue';
|
||||||
import styles from './DateFilterBuilderRowValue.css';
|
import styles from './DateFilterBuilderRowValue.css';
|
||||||
@@ -18,12 +18,7 @@ const timeOptions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function isInFilter(filterType) {
|
function isInFilter(filterType) {
|
||||||
return (
|
return filterType === IN_LAST || filterType === IN_NEXT;
|
||||||
filterType === IN_LAST ||
|
|
||||||
filterType === NOT_IN_LAST ||
|
|
||||||
filterType === IN_NEXT ||
|
|
||||||
filterType === NOT_IN_NEXT
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DateFilterBuilderRowValue extends Component {
|
class DateFilterBuilderRowValue extends Component {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { inputTypes } from 'Helpers/Props';
|
import { inputTypes } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import FilterBuilderRow from './FilterBuilderRow';
|
import FilterBuilderRow from './FilterBuilderRow';
|
||||||
import styles from './FilterBuilderModalContent.css';
|
import styles from './FilterBuilderModalContent.css';
|
||||||
|
|
||||||
@@ -193,7 +192,7 @@ class FilterBuilderModalContent extends Component {
|
|||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button onPress={onCancelPress}>
|
<Button onPress={onCancelPress}>
|
||||||
{translate('Cancel')}
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<SpinnerErrorButton
|
<SpinnerErrorButton
|
||||||
@@ -201,7 +200,7 @@ class FilterBuilderModalContent extends Component {
|
|||||||
error={saveError}
|
error={saveError}
|
||||||
onPress={this.onSaveFilterPress}
|
onPress={this.onSaveFilterPress}
|
||||||
>
|
>
|
||||||
{translate('Save')}
|
Save
|
||||||
</SpinnerErrorButton>
|
</SpinnerErrorButton>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { filterBuilderTypes, filterBuilderValueTypes, icons } from 'Helpers/Prop
|
|||||||
import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
|
import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
|
||||||
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
|
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
|
||||||
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
|
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
|
||||||
import ImportListFilterBuilderRowValueConnector from './ImportListFilterBuilderRowValueConnector';
|
|
||||||
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
|
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
|
||||||
import MovieStatusFilterBuilderRowValue from './MovieStatusFilterBuilderRowValue';
|
import MovieStatusFilterBuilderRowValue from './MovieStatusFilterBuilderRowValue';
|
||||||
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
|
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
|
||||||
@@ -75,9 +74,6 @@ function getRowValueConnector(selectedFilterBuilderProp) {
|
|||||||
case filterBuilderValueTypes.TAG:
|
case filterBuilderValueTypes.TAG:
|
||||||
return TagFilterBuilderRowValueConnector;
|
return TagFilterBuilderRowValueConnector;
|
||||||
|
|
||||||
case filterBuilderValueTypes.IMPORTLIST:
|
|
||||||
return ImportListFilterBuilderRowValueConnector;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FilterBuilderRowValueConnector;
|
return FilterBuilderRowValueConnector;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createImportListSelector from 'Store/Selectors/createImportListSelector';
|
|
||||||
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createImportListSelector(),
|
|
||||||
(importLists) => {
|
|
||||||
return {
|
|
||||||
tagList: importLists.map((importList) => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name
|
|
||||||
} = importList;
|
|
||||||
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name
|
|
||||||
};
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(FilterBuilderRowValue);
|
|
||||||
@@ -3,7 +3,6 @@ import React, { Component } from 'react';
|
|||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './CustomFilter.css';
|
import styles from './CustomFilter.css';
|
||||||
|
|
||||||
class CustomFilter extends Component {
|
class CustomFilter extends Component {
|
||||||
@@ -90,7 +89,7 @@ class CustomFilter extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<SpinnerIconButton
|
<SpinnerIconButton
|
||||||
title={translate('RemoveFilter')}
|
title="Remove filter"
|
||||||
name={icons.REMOVE}
|
name={icons.REMOVE}
|
||||||
isSpinning={this.state.isDeleting}
|
isSpinning={this.state.isDeleting}
|
||||||
onPress={this.onRemovePress}
|
onPress={this.onRemovePress}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ function CustomFiltersModalContent(props) {
|
|||||||
<Button
|
<Button
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -8,28 +8,16 @@ import DeviceInput from './DeviceInput';
|
|||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { value }) => value,
|
(state, { value }) => value,
|
||||||
(state, { name }) => name,
|
|
||||||
(state) => state.providerOptions,
|
(state) => state.providerOptions,
|
||||||
(value, name, devices) => {
|
(value, devices) => {
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
items
|
|
||||||
} = devices;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isFetching,
|
...devices,
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
items: items[name] || [],
|
|
||||||
selectedDevices: value.map((valueDevice) => {
|
selectedDevices: value.map((valueDevice) => {
|
||||||
const sectionItems = items[name] || [];
|
|
||||||
|
|
||||||
// Disable equality ESLint rule so we don't need to worry about
|
// Disable equality ESLint rule so we don't need to worry about
|
||||||
// a type mismatch between the value items and the device ID.
|
// a type mismatch between the value items and the device ID.
|
||||||
// eslint-disable-next-line eqeqeq
|
// eslint-disable-next-line eqeqeq
|
||||||
const device = sectionItems.find((d) => d.id == valueDevice);
|
const device = devices.items.find((d) => d.id == valueDevice);
|
||||||
|
|
||||||
if (device) {
|
if (device) {
|
||||||
return {
|
return {
|
||||||
@@ -73,14 +61,11 @@ class DeviceInputConnector extends Component {
|
|||||||
const {
|
const {
|
||||||
provider,
|
provider,
|
||||||
providerData,
|
providerData,
|
||||||
dispatchFetchOptions,
|
dispatchFetchOptions
|
||||||
requestAction,
|
|
||||||
name
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
dispatchFetchOptions({
|
dispatchFetchOptions({
|
||||||
action: requestAction,
|
action: 'getDevices',
|
||||||
itemSection: name,
|
|
||||||
provider,
|
provider,
|
||||||
providerData
|
providerData
|
||||||
});
|
});
|
||||||
@@ -109,7 +94,6 @@ class DeviceInputConnector extends Component {
|
|||||||
DeviceInputConnector.propTypes = {
|
DeviceInputConnector.propTypes = {
|
||||||
provider: PropTypes.string.isRequired,
|
provider: PropTypes.string.isRequired,
|
||||||
providerData: PropTypes.object.isRequired,
|
providerData: PropTypes.object.isRequired,
|
||||||
requestAction: PropTypes.string.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
dispatchFetchOptions: PropTypes.func.isRequired,
|
dispatchFetchOptions: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -58,30 +58,11 @@ function getSelectedIndex(props) {
|
|||||||
values
|
values
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return values.findIndex((v) => {
|
|
||||||
return value.size && v.key === value[0];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.findIndex((v) => {
|
return values.findIndex((v) => {
|
||||||
return v.key === value;
|
return v.key === value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSelectedItem(index, props) {
|
|
||||||
const {
|
|
||||||
value,
|
|
||||||
values
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return value.includes(values[index].key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return values[index].key === value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getKey(selectedIndex, values) {
|
function getKey(selectedIndex, values) {
|
||||||
return values[selectedIndex].key;
|
return values[selectedIndex].key;
|
||||||
}
|
}
|
||||||
@@ -111,7 +92,7 @@ class EnhancedSelectInput extends Component {
|
|||||||
this._scheduleUpdate();
|
this._scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(this.props.value) && prevProps.value !== this.props.value) {
|
if (prevProps.value !== this.props.value) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedIndex: getSelectedIndex(this.props)
|
selectedIndex: getSelectedIndex(this.props)
|
||||||
});
|
});
|
||||||
@@ -153,7 +134,7 @@ class EnhancedSelectInput extends Component {
|
|||||||
const button = document.getElementById(this._buttonId);
|
const button = document.getElementById(this._buttonId);
|
||||||
const options = document.getElementById(this._optionsId);
|
const options = document.getElementById(this._optionsId);
|
||||||
|
|
||||||
if (!button || !event.target.isConnected || this.state.isMobile) {
|
if (!button || this.state.isMobile) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +177,7 @@ class EnhancedSelectInput extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
selectedIndex == null || selectedIndex === -1 ||
|
selectedIndex == null ||
|
||||||
getSelectedOption(selectedIndex, values).isDisabled
|
getSelectedOption(selectedIndex, values).isDisabled
|
||||||
) {
|
) {
|
||||||
if (keyCode === keyCodes.UP_ARROW) {
|
if (keyCode === keyCodes.UP_ARROW) {
|
||||||
@@ -254,27 +235,12 @@ class EnhancedSelectInput extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSelect = (value) => {
|
onSelect = (value) => {
|
||||||
if (Array.isArray(this.props.value)) {
|
this.setState({ isOpen: false });
|
||||||
let newValue = null;
|
|
||||||
const index = this.props.value.indexOf(value);
|
|
||||||
if (index === -1) {
|
|
||||||
newValue = this.props.values.map((v) => v.key).filter((v) => (v === value) || this.props.value.includes(v));
|
|
||||||
} else {
|
|
||||||
newValue = [...this.props.value];
|
|
||||||
newValue.splice(index, 1);
|
|
||||||
}
|
|
||||||
this.props.onChange({
|
|
||||||
name: this.props.name,
|
|
||||||
value: newValue
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({ isOpen: false });
|
|
||||||
|
|
||||||
this.props.onChange({
|
this.props.onChange({
|
||||||
name: this.props.name,
|
name: this.props.name,
|
||||||
value
|
value
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMeasure = ({ width }) => {
|
onMeasure = ({ width }) => {
|
||||||
@@ -292,7 +258,6 @@ class EnhancedSelectInput extends Component {
|
|||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
disabledClassName,
|
disabledClassName,
|
||||||
value,
|
|
||||||
values,
|
values,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
hasError,
|
hasError,
|
||||||
@@ -310,7 +275,6 @@ class EnhancedSelectInput extends Component {
|
|||||||
isMobile
|
isMobile
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const isMultiSelect = Array.isArray(value);
|
|
||||||
const selectedOption = getSelectedOption(selectedIndex, values);
|
const selectedOption = getSelectedOption(selectedIndex, values);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -339,12 +303,9 @@ class EnhancedSelectInput extends Component {
|
|||||||
onPress={this.onPress}
|
onPress={this.onPress}
|
||||||
>
|
>
|
||||||
<SelectedValueComponent
|
<SelectedValueComponent
|
||||||
value={value}
|
|
||||||
values={values}
|
|
||||||
{...selectedValueOptions}
|
{...selectedValueOptions}
|
||||||
{...selectedOption}
|
{...selectedOption}
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
isMultiSelect={isMultiSelect}
|
|
||||||
>
|
>
|
||||||
{selectedOption ? selectedOption.value : null}
|
{selectedOption ? selectedOption.value : null}
|
||||||
</SelectedValueComponent>
|
</SelectedValueComponent>
|
||||||
@@ -398,17 +359,11 @@ class EnhancedSelectInput extends Component {
|
|||||||
>
|
>
|
||||||
{
|
{
|
||||||
values.map((v, index) => {
|
values.map((v, index) => {
|
||||||
const hasParent = v.parentKey !== undefined;
|
|
||||||
const depth = hasParent ? 1 : 0;
|
|
||||||
const parentSelected = hasParent && value.includes(v.parentKey);
|
|
||||||
return (
|
return (
|
||||||
<OptionComponent
|
<OptionComponent
|
||||||
key={v.key}
|
key={v.key}
|
||||||
id={v.key}
|
id={v.key}
|
||||||
depth={depth}
|
isSelected={index === selectedIndex}
|
||||||
isSelected={isSelectedItem(index, this.props)}
|
|
||||||
isDisabled={parentSelected}
|
|
||||||
isMultiSelect={isMultiSelect}
|
|
||||||
{...valueOptions}
|
{...valueOptions}
|
||||||
{...v}
|
{...v}
|
||||||
isMobile={false}
|
isMobile={false}
|
||||||
@@ -446,17 +401,11 @@ class EnhancedSelectInput extends Component {
|
|||||||
<Scroller className={styles.optionsModalScroller}>
|
<Scroller className={styles.optionsModalScroller}>
|
||||||
{
|
{
|
||||||
values.map((v, index) => {
|
values.map((v, index) => {
|
||||||
const hasParent = v.parentKey !== undefined;
|
|
||||||
const depth = hasParent ? 1 : 0;
|
|
||||||
const parentSelected = hasParent && value.includes(v.parentKey);
|
|
||||||
return (
|
return (
|
||||||
<OptionComponent
|
<OptionComponent
|
||||||
key={v.key}
|
key={v.key}
|
||||||
id={v.key}
|
id={v.key}
|
||||||
depth={depth}
|
isSelected={index === selectedIndex}
|
||||||
isSelected={isSelectedItem(index, this.props)}
|
|
||||||
isMultiSelect={isMultiSelect}
|
|
||||||
isDisabled={parentSelected}
|
|
||||||
{...valueOptions}
|
{...valueOptions}
|
||||||
{...v}
|
{...v}
|
||||||
isMobile={true}
|
isMobile={true}
|
||||||
@@ -480,9 +429,9 @@ EnhancedSelectInput.propTypes = {
|
|||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabledClassName: PropTypes.string,
|
disabledClassName: PropTypes.string,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.number)]).isRequired,
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
isDisabled: PropTypes.bool,
|
||||||
hasError: PropTypes.bool,
|
hasError: PropTypes.bool,
|
||||||
hasWarning: PropTypes.bool,
|
hasWarning: PropTypes.bool,
|
||||||
valueOptions: PropTypes.object.isRequired,
|
valueOptions: PropTypes.object.isRequired,
|
||||||
|
|||||||
@@ -11,18 +11,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.optionCheck {
|
|
||||||
composes: container from '~./CheckInput.css';
|
|
||||||
|
|
||||||
flex: 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.optionCheckInput {
|
|
||||||
composes: input from '~./CheckInput.css';
|
|
||||||
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.isSelected {
|
.isSelected {
|
||||||
background-color: #e2e2e2;
|
background-color: #e2e2e2;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import React, { Component } from 'react';
|
|||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import CheckInput from './CheckInput';
|
|
||||||
import styles from './EnhancedSelectInputOption.css';
|
import styles from './EnhancedSelectInputOption.css';
|
||||||
|
|
||||||
class EnhancedSelectInputOption extends Component {
|
class EnhancedSelectInputOption extends Component {
|
||||||
@@ -21,21 +20,15 @@ class EnhancedSelectInputOption extends Component {
|
|||||||
onSelect(id);
|
onSelect(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCheckPress = () => {
|
|
||||||
// CheckInput requires a handler. Swallow the change event because onPress will already handle it via event propagation.
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
id,
|
|
||||||
isSelected,
|
isSelected,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isHidden,
|
isHidden,
|
||||||
isMultiSelect,
|
|
||||||
isMobile,
|
isMobile,
|
||||||
children
|
children
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@@ -44,8 +37,8 @@ class EnhancedSelectInputOption extends Component {
|
|||||||
<Link
|
<Link
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
isSelected && !isMultiSelect && styles.isSelected,
|
isSelected && styles.isSelected,
|
||||||
isDisabled && !isMultiSelect && styles.isDisabled,
|
isDisabled && styles.isDisabled,
|
||||||
isHidden && styles.isHidden,
|
isHidden && styles.isHidden,
|
||||||
isMobile && styles.isMobile
|
isMobile && styles.isMobile
|
||||||
)}
|
)}
|
||||||
@@ -53,19 +46,6 @@ class EnhancedSelectInputOption extends Component {
|
|||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
onPress={this.onPress}
|
onPress={this.onPress}
|
||||||
>
|
>
|
||||||
|
|
||||||
{
|
|
||||||
isMultiSelect &&
|
|
||||||
<CheckInput
|
|
||||||
className={styles.optionCheckInput}
|
|
||||||
containerClassName={styles.optionCheck}
|
|
||||||
name={`select-${id}`}
|
|
||||||
value={isSelected}
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
onChange={this.onCheckPress}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -87,7 +67,6 @@ EnhancedSelectInputOption.propTypes = {
|
|||||||
isSelected: PropTypes.bool.isRequired,
|
isSelected: PropTypes.bool.isRequired,
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
isDisabled: PropTypes.bool.isRequired,
|
||||||
isHidden: PropTypes.bool.isRequired,
|
isHidden: PropTypes.bool.isRequired,
|
||||||
isMultiSelect: PropTypes.bool.isRequired,
|
|
||||||
isMobile: PropTypes.bool.isRequired,
|
isMobile: PropTypes.bool.isRequired,
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
onSelect: PropTypes.func.isRequired
|
onSelect: PropTypes.func.isRequired
|
||||||
@@ -96,8 +75,7 @@ EnhancedSelectInputOption.propTypes = {
|
|||||||
EnhancedSelectInputOption.defaultProps = {
|
EnhancedSelectInputOption.defaultProps = {
|
||||||
className: styles.option,
|
className: styles.option,
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
isHidden: false,
|
isHidden: false
|
||||||
isMultiSelect: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EnhancedSelectInputOption;
|
export default EnhancedSelectInputOption;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import CheckInput from './CheckInput';
|
|||||||
import DeviceInputConnector from './DeviceInputConnector';
|
import DeviceInputConnector from './DeviceInputConnector';
|
||||||
import EnhancedSelectInput from './EnhancedSelectInput';
|
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||||
import FormInputHelpText from './FormInputHelpText';
|
import FormInputHelpText from './FormInputHelpText';
|
||||||
import IndexerFlagsSelectInputConnector from './IndexerFlagsSelectInputConnector';
|
|
||||||
import KeyValueListInput from './KeyValueListInput';
|
import KeyValueListInput from './KeyValueListInput';
|
||||||
import MovieMonitoredSelectInput from './MovieMonitoredSelectInput';
|
import MovieMonitoredSelectInput from './MovieMonitoredSelectInput';
|
||||||
import NumberInput from './NumberInput';
|
import NumberInput from './NumberInput';
|
||||||
@@ -21,7 +20,6 @@ import QualityProfileSelectInputConnector from './QualityProfileSelectInputConne
|
|||||||
import RootFolderSelectInputConnector from './RootFolderSelectInputConnector';
|
import RootFolderSelectInputConnector from './RootFolderSelectInputConnector';
|
||||||
import TagInputConnector from './TagInputConnector';
|
import TagInputConnector from './TagInputConnector';
|
||||||
import TagSelectInputConnector from './TagSelectInputConnector';
|
import TagSelectInputConnector from './TagSelectInputConnector';
|
||||||
import TextArea from './TextArea';
|
|
||||||
import TextInput from './TextInput';
|
import TextInput from './TextInput';
|
||||||
import TextTagInputConnector from './TextTagInputConnector';
|
import TextTagInputConnector from './TextTagInputConnector';
|
||||||
import styles from './FormInputGroup.css';
|
import styles from './FormInputGroup.css';
|
||||||
@@ -67,18 +65,12 @@ function getComponent(type) {
|
|||||||
case inputTypes.ROOT_FOLDER_SELECT:
|
case inputTypes.ROOT_FOLDER_SELECT:
|
||||||
return RootFolderSelectInputConnector;
|
return RootFolderSelectInputConnector;
|
||||||
|
|
||||||
case inputTypes.INDEXER_FLAGS_SELECT:
|
|
||||||
return IndexerFlagsSelectInputConnector;
|
|
||||||
|
|
||||||
case inputTypes.SELECT:
|
case inputTypes.SELECT:
|
||||||
return EnhancedSelectInput;
|
return EnhancedSelectInput;
|
||||||
|
|
||||||
case inputTypes.TAG:
|
case inputTypes.TAG:
|
||||||
return TagInputConnector;
|
return TagInputConnector;
|
||||||
|
|
||||||
case inputTypes.TEXT_AREA:
|
|
||||||
return TextArea;
|
|
||||||
|
|
||||||
case inputTypes.TEXT_TAG:
|
case inputTypes.TEXT_TAG:
|
||||||
return TextTagInputConnector;
|
return TextTagInputConnector;
|
||||||
|
|
||||||
@@ -161,7 +153,7 @@ function FormInputGroup(props) {
|
|||||||
<Icon
|
<Icon
|
||||||
name={icons.UNSAVED_SETTING}
|
name={icons.UNSAVED_SETTING}
|
||||||
className={styles.pendingChangesIcon}
|
className={styles.pendingChangesIcon}
|
||||||
title={translate('ChangeHasNotBeenSavedYet')}
|
title="Change has not been saved yet"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div> */}
|
</div> */}
|
||||||
@@ -215,7 +207,7 @@ function FormInputGroup(props) {
|
|||||||
key={index}
|
key={index}
|
||||||
text={error.message}
|
text={error.message}
|
||||||
link={error.link}
|
link={error.link}
|
||||||
tooltip={error.detailedMessage}
|
linkTooltip={error.detailedMessage}
|
||||||
isError={true}
|
isError={true}
|
||||||
isCheckInput={checkInput}
|
isCheckInput={checkInput}
|
||||||
/>
|
/>
|
||||||
@@ -230,7 +222,7 @@ function FormInputGroup(props) {
|
|||||||
key={index}
|
key={index}
|
||||||
text={warning.message}
|
text={warning.message}
|
||||||
link={warning.link}
|
link={warning.link}
|
||||||
tooltip={warning.detailedMessage}
|
linkTooltip={warning.detailedMessage}
|
||||||
isWarning={true}
|
isWarning={true}
|
||||||
isCheckInput={checkInput}
|
isCheckInput={checkInput}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -37,7 +37,3 @@
|
|||||||
|
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function FormInputHelpText(props) {
|
|||||||
className,
|
className,
|
||||||
text,
|
text,
|
||||||
link,
|
link,
|
||||||
tooltip,
|
linkTooltip,
|
||||||
isError,
|
isError,
|
||||||
isWarning,
|
isWarning,
|
||||||
isCheckInput
|
isCheckInput
|
||||||
@@ -28,27 +28,16 @@ function FormInputHelpText(props) {
|
|||||||
{text}
|
{text}
|
||||||
|
|
||||||
{
|
{
|
||||||
link ?
|
!!link &&
|
||||||
<Link
|
<Link
|
||||||
className={styles.link}
|
className={styles.link}
|
||||||
to={link}
|
to={link}
|
||||||
title={tooltip}
|
title={linkTooltip}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name={icons.EXTERNAL_LINK}
|
name={icons.EXTERNAL_LINK}
|
||||||
/>
|
/>
|
||||||
</Link> :
|
</Link>
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!link && tooltip ?
|
|
||||||
<Icon
|
|
||||||
containerClassName={styles.details}
|
|
||||||
name={icons.INFO}
|
|
||||||
title={tooltip}
|
|
||||||
/> :
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -58,7 +47,7 @@ FormInputHelpText.propTypes = {
|
|||||||
className: PropTypes.string.isRequired,
|
className: PropTypes.string.isRequired,
|
||||||
text: PropTypes.string.isRequired,
|
text: PropTypes.string.isRequired,
|
||||||
link: PropTypes.string,
|
link: PropTypes.string,
|
||||||
tooltip: PropTypes.string,
|
linkTooltip: PropTypes.string,
|
||||||
isError: PropTypes.bool,
|
isError: PropTypes.bool,
|
||||||
isWarning: PropTypes.bool,
|
isWarning: PropTypes.bool,
|
||||||
isCheckInput: PropTypes.bool
|
isCheckInput: PropTypes.bool
|
||||||
|
|||||||
@@ -6,23 +6,14 @@ import styles from './HintedSelectInputOption.css';
|
|||||||
|
|
||||||
function HintedSelectInputOption(props) {
|
function HintedSelectInputOption(props) {
|
||||||
const {
|
const {
|
||||||
id,
|
|
||||||
value,
|
value,
|
||||||
hint,
|
hint,
|
||||||
isSelected,
|
|
||||||
isDisabled,
|
|
||||||
isMultiSelect,
|
|
||||||
isMobile,
|
isMobile,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EnhancedSelectInputOption
|
<EnhancedSelectInputOption
|
||||||
id={id}
|
|
||||||
isSelected={isSelected}
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
isHidden={isDisabled}
|
|
||||||
isMultiSelect={isMultiSelect}
|
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
@@ -45,19 +36,9 @@ function HintedSelectInputOption(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HintedSelectInputOption.propTypes = {
|
HintedSelectInputOption.propTypes = {
|
||||||
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
hint: PropTypes.node,
|
hint: PropTypes.node,
|
||||||
isSelected: PropTypes.bool.isRequired,
|
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
|
||||||
isMultiSelect: PropTypes.bool.isRequired,
|
|
||||||
isMobile: PropTypes.bool.isRequired
|
isMobile: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
HintedSelectInputOption.defaultProps = {
|
|
||||||
isDisabled: false,
|
|
||||||
isHidden: false,
|
|
||||||
isMultiSelect: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HintedSelectInputOption;
|
export default HintedSelectInputOption;
|
||||||
|
|||||||
@@ -1,43 +1,23 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Label from 'Components/Label';
|
|
||||||
import EnhancedSelectInputSelectedValue from './EnhancedSelectInputSelectedValue';
|
import EnhancedSelectInputSelectedValue from './EnhancedSelectInputSelectedValue';
|
||||||
import styles from './HintedSelectInputSelectedValue.css';
|
import styles from './HintedSelectInputSelectedValue.css';
|
||||||
|
|
||||||
function HintedSelectInputSelectedValue(props) {
|
function HintedSelectInputSelectedValue(props) {
|
||||||
const {
|
const {
|
||||||
value,
|
value,
|
||||||
values,
|
|
||||||
hint,
|
hint,
|
||||||
isMultiSelect,
|
|
||||||
includeHint,
|
includeHint,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const valuesMap = isMultiSelect && _.keyBy(values, 'key');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EnhancedSelectInputSelectedValue
|
<EnhancedSelectInputSelectedValue
|
||||||
className={styles.selectedValue}
|
className={styles.selectedValue}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
<div className={styles.valueText}>
|
<div className={styles.valueText}>
|
||||||
{
|
{value}
|
||||||
isMultiSelect &&
|
|
||||||
value.map((key, index) => {
|
|
||||||
const v = valuesMap[key];
|
|
||||||
return (
|
|
||||||
<Label key={key}>
|
|
||||||
{v ? v.value : key}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isMultiSelect && value
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -51,15 +31,12 @@ function HintedSelectInputSelectedValue(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HintedSelectInputSelectedValue.propTypes = {
|
HintedSelectInputSelectedValue.propTypes = {
|
||||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]).isRequired,
|
value: PropTypes.string,
|
||||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
hint: PropTypes.string,
|
hint: PropTypes.string,
|
||||||
isMultiSelect: PropTypes.bool.isRequired,
|
|
||||||
includeHint: PropTypes.bool.isRequired
|
includeHint: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
HintedSelectInputSelectedValue.defaultProps = {
|
HintedSelectInputSelectedValue.defaultProps = {
|
||||||
isMultiSelect: false,
|
|
||||||
includeHint: true
|
includeHint: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import EnhancedSelectInput from './EnhancedSelectInput';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state, { indexerFlags }) => indexerFlags,
|
|
||||||
(state) => state.settings.indexerFlags,
|
|
||||||
(selectedFlags, indexerFlags) => {
|
|
||||||
const value = [];
|
|
||||||
|
|
||||||
indexerFlags.items.forEach((item) => {
|
|
||||||
// eslint-disable-next-line no-bitwise
|
|
||||||
if ((selectedFlags & item.id) === item.id) {
|
|
||||||
value.push(item.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const values = indexerFlags.items.map(({ id, name }) => {
|
|
||||||
return {
|
|
||||||
key: id,
|
|
||||||
value: name
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
values
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class IndexerFlagsSelectInputConnector extends Component {
|
|
||||||
|
|
||||||
onChange = ({ name, value }) => {
|
|
||||||
let indexerFlags = 0;
|
|
||||||
|
|
||||||
value.forEach((flagId) => {
|
|
||||||
indexerFlags += flagId;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onChange({ name, value: indexerFlags });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EnhancedSelectInput
|
|
||||||
{...this.props}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexerFlagsSelectInputConnector.propTypes = {
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
indexerFlags: PropTypes.number.isRequired,
|
|
||||||
value: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(IndexerFlagsSelectInputConnector);
|
|
||||||
@@ -3,17 +3,10 @@ import React from 'react';
|
|||||||
import TextInput from './TextInput';
|
import TextInput from './TextInput';
|
||||||
import styles from './PasswordInput.css';
|
import styles from './PasswordInput.css';
|
||||||
|
|
||||||
// Prevent a user from copying (or cutting) the password from the input
|
|
||||||
function onCopy(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.nativeEvent.stopImmediatePropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
function PasswordInput(props) {
|
function PasswordInput(props) {
|
||||||
return (
|
return (
|
||||||
<TextInput
|
<TextInput
|
||||||
{...props}
|
{...props}
|
||||||
onCopy={onCopy}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
|||||||
import FormLabel from 'Components/Form/FormLabel';
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
import { inputTypes } from 'Helpers/Props';
|
import { inputTypes } from 'Helpers/Props';
|
||||||
|
|
||||||
function getType(type, value) {
|
function getType(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'captcha':
|
case 'captcha':
|
||||||
return inputTypes.CAPTCHA;
|
return inputTypes.CAPTCHA;
|
||||||
@@ -45,8 +45,7 @@ function getSelectValues(selectOptions) {
|
|||||||
return _.reduce(selectOptions, (result, option) => {
|
return _.reduce(selectOptions, (result, option) => {
|
||||||
result.push({
|
result.push({
|
||||||
key: option.value,
|
key: option.value,
|
||||||
value: option.name,
|
value: option.name
|
||||||
hint: option.hint
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -63,7 +62,6 @@ function ProviderFieldFormGroup(props) {
|
|||||||
value,
|
value,
|
||||||
type,
|
type,
|
||||||
advanced,
|
advanced,
|
||||||
requestAction,
|
|
||||||
hidden,
|
hidden,
|
||||||
pending,
|
pending,
|
||||||
errors,
|
errors,
|
||||||
@@ -88,7 +86,7 @@ function ProviderFieldFormGroup(props) {
|
|||||||
<FormLabel>{label}</FormLabel>
|
<FormLabel>{label}</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={getType(type, value)}
|
type={getType(type)}
|
||||||
name={name}
|
name={name}
|
||||||
label={label}
|
label={label}
|
||||||
helpText={helpText}
|
helpText={helpText}
|
||||||
@@ -100,7 +98,6 @@ function ProviderFieldFormGroup(props) {
|
|||||||
pending={pending}
|
pending={pending}
|
||||||
includeFiles={type === 'filePath' ? true : undefined}
|
includeFiles={type === 'filePath' ? true : undefined}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
requestAction={requestAction}
|
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -121,7 +118,6 @@ ProviderFieldFormGroup.propTypes = {
|
|||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
advanced: PropTypes.bool.isRequired,
|
advanced: PropTypes.bool.isRequired,
|
||||||
requestAction: PropTypes.string,
|
|
||||||
hidden: PropTypes.string,
|
hidden: PropTypes.string,
|
||||||
pending: PropTypes.bool.isRequired,
|
pending: PropTypes.bool.isRequired,
|
||||||
errors: PropTypes.arrayOf(PropTypes.object).isRequired,
|
errors: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
|||||||
@@ -15,12 +15,10 @@
|
|||||||
|
|
||||||
.value {
|
.value {
|
||||||
display: flex;
|
display: flex;
|
||||||
max-width: 500px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.movieFolder {
|
.movieFolder {
|
||||||
@add-mixin truncate;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
color: $disabledColor;
|
color: $disabledColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
.input {
|
|
||||||
composes: input from '~Components/Form/Input.css';
|
|
||||||
|
|
||||||
flex-grow: 1;
|
|
||||||
min-height: 200px;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.readOnly {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hasError {
|
|
||||||
composes: hasError from '~Components/Form/Input.css';
|
|
||||||
}
|
|
||||||
|
|
||||||
.hasWarning {
|
|
||||||
composes: hasWarning from '~Components/Form/Input.css';
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import styles from './TextArea.css';
|
|
||||||
|
|
||||||
class TextArea extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this._input = null;
|
|
||||||
this._selectionStart = null;
|
|
||||||
this._selectionEnd = null;
|
|
||||||
this._selectionTimeout = null;
|
|
||||||
this._isMouseTarget = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
window.addEventListener('mouseup', this.onDocumentMouseUp);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
window.removeEventListener('mouseup', this.onDocumentMouseUp);
|
|
||||||
|
|
||||||
if (this._selectionTimeout) {
|
|
||||||
this._selectionTimeout = clearTimeout(this._selectionTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Control
|
|
||||||
|
|
||||||
setInputRef = (ref) => {
|
|
||||||
this._input = ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectionChange() {
|
|
||||||
if (this._selectionTimeout) {
|
|
||||||
this._selectionTimeout = clearTimeout(this._selectionTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._selectionTimeout = setTimeout(() => {
|
|
||||||
const selectionStart = this._input.selectionStart;
|
|
||||||
const selectionEnd = this._input.selectionEnd;
|
|
||||||
|
|
||||||
const selectionChanged = (
|
|
||||||
this._selectionStart !== selectionStart ||
|
|
||||||
this._selectionEnd !== selectionEnd
|
|
||||||
);
|
|
||||||
|
|
||||||
this._selectionStart = selectionStart;
|
|
||||||
this._selectionEnd = selectionEnd;
|
|
||||||
|
|
||||||
if (this.props.onSelectionChange && selectionChanged) {
|
|
||||||
this.props.onSelectionChange(selectionStart, selectionEnd);
|
|
||||||
}
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onChange = (event) => {
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
onChange
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
name,
|
|
||||||
value: event.target.value
|
|
||||||
};
|
|
||||||
|
|
||||||
onChange(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
onFocus = (event) => {
|
|
||||||
if (this.props.onFocus) {
|
|
||||||
this.props.onFocus(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectionChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyUp = () => {
|
|
||||||
this.selectionChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMouseDown = () => {
|
|
||||||
this._isMouseTarget = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMouseUp = () => {
|
|
||||||
this.selectionChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
onDocumentMouseUp = () => {
|
|
||||||
if (this._isMouseTarget) {
|
|
||||||
this.selectionChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isMouseTarget = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
className,
|
|
||||||
readOnly,
|
|
||||||
autoFocus,
|
|
||||||
placeholder,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
hasError,
|
|
||||||
hasWarning,
|
|
||||||
onBlur
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<textarea
|
|
||||||
ref={this.setInputRef}
|
|
||||||
readOnly={readOnly}
|
|
||||||
autoFocus={autoFocus}
|
|
||||||
placeholder={placeholder}
|
|
||||||
className={classNames(
|
|
||||||
className,
|
|
||||||
readOnly && styles.readOnly,
|
|
||||||
hasError && styles.hasError,
|
|
||||||
hasWarning && styles.hasWarning
|
|
||||||
)}
|
|
||||||
name={name}
|
|
||||||
value={value}
|
|
||||||
onChange={this.onChange}
|
|
||||||
onFocus={this.onFocus}
|
|
||||||
onBlur={onBlur}
|
|
||||||
onKeyUp={this.onKeyUp}
|
|
||||||
onMouseDown={this.onMouseDown}
|
|
||||||
onMouseUp={this.onMouseUp}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea.propTypes = {
|
|
||||||
className: PropTypes.string.isRequired,
|
|
||||||
readOnly: PropTypes.bool,
|
|
||||||
autoFocus: PropTypes.bool,
|
|
||||||
placeholder: PropTypes.string,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array]).isRequired,
|
|
||||||
hasError: PropTypes.bool,
|
|
||||||
hasWarning: PropTypes.bool,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
onFocus: PropTypes.func,
|
|
||||||
onBlur: PropTypes.func,
|
|
||||||
onSelectionChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
TextArea.defaultProps = {
|
|
||||||
className: styles.input,
|
|
||||||
type: 'text',
|
|
||||||
readOnly: false,
|
|
||||||
autoFocus: false,
|
|
||||||
value: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TextArea;
|
|
||||||
@@ -130,8 +130,7 @@ class TextInput extends Component {
|
|||||||
step,
|
step,
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
onBlur,
|
onBlur
|
||||||
onCopy
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -156,8 +155,6 @@ class TextInput extends Component {
|
|||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onFocus={this.onFocus}
|
onFocus={this.onFocus}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onCopy={onCopy}
|
|
||||||
onCut={onCopy}
|
|
||||||
onKeyUp={this.onKeyUp}
|
onKeyUp={this.onKeyUp}
|
||||||
onMouseDown={this.onMouseDown}
|
onMouseDown={this.onMouseDown}
|
||||||
onMouseUp={this.onMouseUp}
|
onMouseUp={this.onMouseUp}
|
||||||
@@ -183,7 +180,6 @@ TextInput.propTypes = {
|
|||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
onFocus: PropTypes.func,
|
onFocus: PropTypes.func,
|
||||||
onBlur: PropTypes.func,
|
onBlur: PropTypes.func,
|
||||||
onCopy: PropTypes.func,
|
|
||||||
onSelectionChange: PropTypes.func
|
onSelectionChange: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,14 @@ import Icon from 'Components/Icon';
|
|||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import styles from './HeartRating.css';
|
import styles from './HeartRating.css';
|
||||||
|
|
||||||
function HeartRating({ rating, iconSize, hideHeart }) {
|
function HeartRating({ rating, iconSize }) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{
|
<Icon
|
||||||
!hideHeart &&
|
className={styles.heart}
|
||||||
<Icon
|
name={icons.HEART}
|
||||||
className={styles.heart}
|
size={iconSize}
|
||||||
name={icons.HEART}
|
/>
|
||||||
size={iconSize}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{rating * 10}%
|
{rating * 10}%
|
||||||
</span>
|
</span>
|
||||||
@@ -23,8 +20,7 @@ function HeartRating({ rating, iconSize, hideHeart }) {
|
|||||||
|
|
||||||
HeartRating.propTypes = {
|
HeartRating.propTypes = {
|
||||||
rating: PropTypes.number.isRequired,
|
rating: PropTypes.number.isRequired,
|
||||||
iconSize: PropTypes.number.isRequired,
|
iconSize: PropTypes.number.isRequired
|
||||||
hideHeart: PropTypes.bool
|
|
||||||
};
|
};
|
||||||
|
|
||||||
HeartRating.defaultProps = {
|
HeartRating.defaultProps = {
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
.lists {
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { kinds, sizes } from 'Helpers/Props';
|
|
||||||
import Label from './Label';
|
|
||||||
import styles from './ImportListList.css';
|
|
||||||
|
|
||||||
function ImportListList({ lists, importListList }) {
|
|
||||||
return (
|
|
||||||
<div className={styles.lists}>
|
|
||||||
{
|
|
||||||
lists.map((t) => {
|
|
||||||
const list = _.find(importListList, { id: t });
|
|
||||||
|
|
||||||
if (!list) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
key={list.id}
|
|
||||||
kind={kinds.INFO}
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
>
|
|
||||||
{list.name}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportListList.propTypes = {
|
|
||||||
lists: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
importListList: PropTypes.arrayOf(PropTypes.object).isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
ImportListList.defaultProps = {
|
|
||||||
lists: []
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ImportListList;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createImportListSelector from 'Store/Selectors/createImportListSelector';
|
|
||||||
import ImportListList from './ImportListList';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createImportListSelector(),
|
|
||||||
(importListList) => {
|
|
||||||
return {
|
|
||||||
importListList
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(ImportListList);
|
|
||||||
@@ -87,15 +87,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.queue {
|
|
||||||
border-color: $queueColor;
|
|
||||||
background-color: $queueColor;
|
|
||||||
|
|
||||||
&.outline {
|
|
||||||
color: $queueColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sizes **/
|
/** Sizes **/
|
||||||
|
|
||||||
.small {
|
.small {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ class ClipboardButton extends Component {
|
|||||||
|
|
||||||
this._id = getUniqueElememtId();
|
this._id = getUniqueElememtId();
|
||||||
this._successTimeout = null;
|
this._successTimeout = null;
|
||||||
this._testResultTimeout = null;
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showSuccess: false,
|
showSuccess: false,
|
||||||
@@ -27,8 +26,7 @@ class ClipboardButton extends Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this._clipboard = new Clipboard(`#${this._id}`, {
|
this._clipboard = new Clipboard(`#${this._id}`, {
|
||||||
text: () => this.props.value,
|
text: () => this.props.value
|
||||||
container: document.getElementById(this._id)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this._clipboard.on('success', this.onSuccess);
|
this._clipboard.on('success', this.onSuccess);
|
||||||
@@ -49,10 +47,6 @@ class ClipboardButton extends Component {
|
|||||||
if (this._clipboard) {
|
if (this._clipboard) {
|
||||||
this._clipboard.destroy();
|
this._clipboard.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._testResultTimeout) {
|
|
||||||
clearTimeout(this._testResultTimeout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -86,7 +80,6 @@ class ClipboardButton extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
value,
|
value,
|
||||||
className,
|
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -102,7 +95,7 @@ class ClipboardButton extends Component {
|
|||||||
return (
|
return (
|
||||||
<FormInputButton
|
<FormInputButton
|
||||||
id={this._id}
|
id={this._id}
|
||||||
className={className}
|
className={styles.button}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
<span className={showStateIcon ? styles.showStateIcon : undefined}>
|
<span className={showStateIcon ? styles.showStateIcon : undefined}>
|
||||||
@@ -128,12 +121,7 @@ class ClipboardButton extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClipboardButton.propTypes = {
|
ClipboardButton.propTypes = {
|
||||||
className: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.string.isRequired
|
value: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
ClipboardButton.defaultProps = {
|
|
||||||
className: styles.button
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ClipboardButton;
|
export default ClipboardButton;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styles from './LoadingIndicator.css';
|
import styles from './LoadingIndicator.css';
|
||||||
@@ -14,7 +13,7 @@ function LoadingIndicator({ className, rippleClassName, size }) {
|
|||||||
style={{ height }}
|
style={{ height }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(styles.rippleContainer, 'followingBalls')}
|
className={styles.rippleContainer}
|
||||||
style={{ width, height }}
|
style={{ width, height }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -2,24 +2,8 @@ import React from 'react';
|
|||||||
import styles from './LoadingMessage.css';
|
import styles from './LoadingMessage.css';
|
||||||
|
|
||||||
const messages = [
|
const messages = [
|
||||||
'Downloading more RAM',
|
'Welcome to Radarr Aphrodite Preview. Enjoy'
|
||||||
'Now in Technicolor',
|
// TODO Add some messages here
|
||||||
'Previously on Radarr...',
|
|
||||||
'Bleep Bloop.',
|
|
||||||
'Locating the required gigapixels to render...',
|
|
||||||
'Spinning up the hamster wheel...',
|
|
||||||
'At least you\'re not on hold',
|
|
||||||
'Hum something loud while others stare',
|
|
||||||
'Loading humorous message... Please Wait',
|
|
||||||
'I could\'ve been faster in Python',
|
|
||||||
'Don\'t forget to rewind your tracks',
|
|
||||||
'Congratulations! you are the 1000th visitor.',
|
|
||||||
'HELP! I\'m being held hostage and forced to write these stupid lines!',
|
|
||||||
'RE-calibrating the internet...',
|
|
||||||
'I\'ll be here all week',
|
|
||||||
'Don\'t forget to tip your waitress',
|
|
||||||
'Apply directly to the forehead',
|
|
||||||
'Loading Battlestation'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let message = null;
|
let message = null;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ class Marquee extends Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
text: PropTypes.string,
|
text: PropTypes.string,
|
||||||
title: PropTypes.string,
|
|
||||||
hoverToStop: PropTypes.bool,
|
hoverToStop: PropTypes.bool,
|
||||||
loop: PropTypes.bool,
|
loop: PropTypes.bool,
|
||||||
className: PropTypes.string
|
className: PropTypes.string
|
||||||
@@ -17,7 +16,6 @@ class Marquee extends Component {
|
|||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
text: '',
|
text: '',
|
||||||
title: '',
|
|
||||||
hoverToStop: true,
|
hoverToStop: true,
|
||||||
loop: false
|
loop: false
|
||||||
};
|
};
|
||||||
@@ -146,7 +144,7 @@ class Marquee extends Component {
|
|||||||
this.text = el;
|
this.text = el;
|
||||||
}}
|
}}
|
||||||
style={style}
|
style={style}
|
||||||
title={(this.props.title && (this.props.text !== this.props.title)) ? `Original Title: ${this.props.title}` : this.props.text}
|
title={this.props.text}
|
||||||
>
|
>
|
||||||
{this.props.text}
|
{this.props.text}
|
||||||
</span>
|
</span>
|
||||||
@@ -169,7 +167,7 @@ class Marquee extends Component {
|
|||||||
this.text = el;
|
this.text = el;
|
||||||
}}
|
}}
|
||||||
style={style}
|
style={style}
|
||||||
title={(this.props.title && (this.props.text !== this.props.title)) ? `Original Title: ${this.props.title}` : this.props.text}
|
title={this.props.text}
|
||||||
>
|
>
|
||||||
{this.props.text}
|
{this.props.text}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import ModalBody from 'Components/Modal/ModalBody';
|
|||||||
import ModalContent from 'Components/Modal/ModalContent';
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './ModalError.css';
|
import styles from './ModalError.css';
|
||||||
|
|
||||||
function ModalError(props) {
|
function ModalError(props) {
|
||||||
@@ -34,7 +33,7 @@ function ModalError(props) {
|
|||||||
<Button
|
<Button
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>);
|
</ModalContent>);
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './NotFound.css';
|
import styles from './NotFound.css';
|
||||||
|
|
||||||
function NotFound({ message }) {
|
function NotFound({ message }) {
|
||||||
return (
|
return (
|
||||||
<PageContent title={translate('MIA')}>
|
<PageContent title="MIA">
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
{message}
|
{message}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import ModalBody from 'Components/Modal/ModalBody';
|
|||||||
import ModalContent from 'Components/Modal/ModalContent';
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './KeyboardShortcutsModalContent.css';
|
import styles from './KeyboardShortcutsModalContent.css';
|
||||||
|
|
||||||
function getShortcuts() {
|
function getShortcuts() {
|
||||||
@@ -20,26 +19,18 @@ function getShortcuts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getShortcutKey(combo, isOsx) {
|
function getShortcutKey(combo, isOsx) {
|
||||||
const comboMatch = combo.match(/(.+?)\+(.*)/);
|
const comboMatch = combo.match(/(.+?)\+(.)/);
|
||||||
|
|
||||||
if (!comboMatch) {
|
if (!comboMatch) {
|
||||||
return combo;
|
return combo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modifier = comboMatch[1];
|
const modifier = comboMatch[1];
|
||||||
let key = comboMatch[2];
|
const key = comboMatch[2];
|
||||||
let osModifier = modifier;
|
let osModifier = modifier;
|
||||||
|
|
||||||
if (modifier === 'mod') {
|
if (modifier === 'mod') {
|
||||||
osModifier = isOsx ? 'cmd' : 'Ctrl';
|
osModifier = isOsx ? 'cmd' : 'ctrl';
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'home') {
|
|
||||||
key = isOsx ? '↑' : 'Home';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'end') {
|
|
||||||
key = isOsx ? '↓' : 'End';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${osModifier} + ${key}`;
|
return `${osModifier} + ${key}`;
|
||||||
@@ -84,7 +75,7 @@ function KeyboardShortcutsModalContent(props) {
|
|||||||
<Button
|
<Button
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
{translate('Close')}
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
position: absolute;
|
margin-top: 18px;
|
||||||
display: inline-block;
|
margin-bottom: 18px;
|
||||||
margin-left: 5px;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ripple {
|
.ripple {
|
||||||
composes: ripple from '~Components/Loading/LoadingIndicator.css';
|
composes: ripple from '~Components/Loading/LoadingIndicator.css';
|
||||||
|
|
||||||
border: 1px solid $toolbarColor;
|
border: 2px solid $toolbarColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import FuseWorker from './fuse.worker';
|
|||||||
import MovieSearchResult from './MovieSearchResult';
|
import MovieSearchResult from './MovieSearchResult';
|
||||||
import styles from './MovieSearchInput.css';
|
import styles from './MovieSearchInput.css';
|
||||||
|
|
||||||
|
const LOADING_TYPE = 'suggestionsLoading';
|
||||||
const ADD_NEW_TYPE = 'addNew';
|
const ADD_NEW_TYPE = 'addNew';
|
||||||
|
const workerInstance = new FuseWorker();
|
||||||
|
|
||||||
class MovieSearchInput extends Component {
|
class MovieSearchInput extends Component {
|
||||||
|
|
||||||
@@ -22,7 +24,6 @@ class MovieSearchInput extends Component {
|
|||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this._autosuggest = null;
|
this._autosuggest = null;
|
||||||
this._worker = null;
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
value: '',
|
value: '',
|
||||||
@@ -32,23 +33,7 @@ class MovieSearchInput extends Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.bindShortcut(shortcuts.MOVIE_SEARCH_INPUT.key, this.focusInput);
|
this.props.bindShortcut(shortcuts.MOVIE_SEARCH_INPUT.key, this.focusInput);
|
||||||
}
|
workerInstance.addEventListener('message', this.onSuggestionsReceived, false);
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this._worker) {
|
|
||||||
this._worker.removeEventListener('message', this.onSuggestionsReceived, false);
|
|
||||||
this._worker.terminate();
|
|
||||||
this._worker = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getWorker() {
|
|
||||||
if (!this._worker) {
|
|
||||||
this._worker = new FuseWorker();
|
|
||||||
this._worker.addEventListener('message', this.onSuggestionsReceived, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._worker;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -71,15 +56,6 @@ class MovieSearchInput extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.sectionTitle}>
|
<div className={styles.sectionTitle}>
|
||||||
{section.title}
|
{section.title}
|
||||||
|
|
||||||
{
|
|
||||||
section.loading &&
|
|
||||||
<LoadingIndicator
|
|
||||||
className={styles.loading}
|
|
||||||
rippleClassName={styles.ripple}
|
|
||||||
size={20}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -97,6 +73,16 @@ class MovieSearchInput extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.type === LOADING_TYPE) {
|
||||||
|
return (
|
||||||
|
<LoadingIndicator
|
||||||
|
className={styles.loading}
|
||||||
|
rippleClassName={styles.ripple}
|
||||||
|
size={30}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MovieSearchResult
|
<MovieSearchResult
|
||||||
{...item.item}
|
{...item.item}
|
||||||
@@ -113,8 +99,7 @@ class MovieSearchInput extends Component {
|
|||||||
reset() {
|
reset() {
|
||||||
this.setState({
|
this.setState({
|
||||||
value: '',
|
value: '',
|
||||||
suggestions: [],
|
suggestions: []
|
||||||
loading: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,15 +115,6 @@ class MovieSearchInput extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown = (event) => {
|
onKeyDown = (event) => {
|
||||||
if (event.shiftKey || event.altKey || event.ctrlKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key !== 'Tab' && event.key !== 'Enter') {
|
if (event.key !== 'Tab' && event.key !== 'Enter') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -179,74 +155,35 @@ class MovieSearchInput extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSuggestionsFetchRequested = ({ value }) => {
|
onSuggestionsFetchRequested = ({ value }) => {
|
||||||
if (!this.state.loading) {
|
this.setState({
|
||||||
this.setState({
|
suggestions: [
|
||||||
loading: true
|
{
|
||||||
});
|
type: LOADING_TYPE,
|
||||||
}
|
title: value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
this.requestSuggestions(value);
|
this.requestSuggestions(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
requestSuggestions = _.debounce((value) => {
|
requestSuggestions = _.debounce((value) => {
|
||||||
if (!this.state.loading) {
|
const payload = {
|
||||||
return;
|
value,
|
||||||
}
|
movies: this.props.movies
|
||||||
|
};
|
||||||
|
|
||||||
const requestLoading = this.state.requestLoading;
|
workerInstance.postMessage(payload);
|
||||||
|
|
||||||
this.setState({
|
|
||||||
requestValue: value,
|
|
||||||
requestLoading: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!requestLoading) {
|
|
||||||
const payload = {
|
|
||||||
value,
|
|
||||||
movies: this.props.movies
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getWorker().postMessage(payload);
|
|
||||||
}
|
|
||||||
}, 250);
|
}, 250);
|
||||||
|
|
||||||
onSuggestionsReceived = (message) => {
|
onSuggestionsReceived = (message) => {
|
||||||
const {
|
this.setState({
|
||||||
value,
|
suggestions: message.data
|
||||||
suggestions
|
});
|
||||||
} = message.data;
|
|
||||||
|
|
||||||
if (!this.state.loading) {
|
|
||||||
this.setState({
|
|
||||||
requestValue: null,
|
|
||||||
requestLoading: false
|
|
||||||
});
|
|
||||||
} else if (value === this.state.requestValue) {
|
|
||||||
this.setState({
|
|
||||||
suggestions,
|
|
||||||
requestValue: null,
|
|
||||||
requestLoading: false,
|
|
||||||
loading: false
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
suggestions,
|
|
||||||
requestLoading: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
value: this.state.requestValue,
|
|
||||||
movies: this.props.movies
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getWorker().postMessage(payload);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuggestionsClearRequested = () => {
|
onSuggestionsClearRequested = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
suggestions: [],
|
suggestions: []
|
||||||
loading: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,22 +201,20 @@ class MovieSearchInput extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
value,
|
value,
|
||||||
loading,
|
|
||||||
suggestions
|
suggestions
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const suggestionGroups = [];
|
const suggestionGroups = [];
|
||||||
|
|
||||||
if (suggestions.length || loading) {
|
if (suggestions.length) {
|
||||||
suggestionGroups.push({
|
suggestionGroups.push({
|
||||||
title: translate('ExistingMovies'),
|
title: 'Existing Movie',
|
||||||
loading,
|
|
||||||
suggestions
|
suggestions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestionGroups.push({
|
suggestionGroups.push({
|
||||||
title: translate('AddNewMovie'),
|
title: 'Add New Movie',
|
||||||
suggestions: [
|
suggestions: [
|
||||||
{
|
{
|
||||||
type: ADD_NEW_TYPE,
|
type: ADD_NEW_TYPE,
|
||||||
|
|||||||
@@ -21,9 +21,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logoFull {
|
.logoFull {
|
||||||
margin-left: 15px;
|
width: 144px;
|
||||||
width: 120px;
|
height: 48px;
|
||||||
height: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
|||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
|
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
|
||||||
import MovieSearchInputConnector from './MovieSearchInputConnector';
|
import MovieSearchInputConnector from './MovieSearchInputConnector';
|
||||||
import PageHeaderActionsMenuConnector from './PageHeaderActionsMenuConnector';
|
import PageHeaderActionsMenuConnector from './PageHeaderActionsMenuConnector';
|
||||||
@@ -80,7 +79,7 @@ class PageHeader extends Component {
|
|||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
className={styles.translate}
|
className={styles.translate}
|
||||||
title={translate('SuggestTranslationChange')}
|
title="Suggest translation change"
|
||||||
name={icons.TRANSLATE}
|
name={icons.TRANSLATE}
|
||||||
to="https://translate.servarr.com/projects/radarr/radarr/"
|
to="https://translate.servarr.com/projects/radarr/radarr/"
|
||||||
size={24}
|
size={24}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import MenuContent from 'Components/Menu/MenuContent';
|
|||||||
import MenuItem from 'Components/Menu/MenuItem';
|
import MenuItem from 'Components/Menu/MenuItem';
|
||||||
import MenuItemSeparator from 'Components/Menu/MenuItemSeparator';
|
import MenuItemSeparator from 'Components/Menu/MenuItemSeparator';
|
||||||
import { align, icons, kinds } from 'Helpers/Props';
|
import { align, icons, kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './PageHeaderActionsMenu.css';
|
import styles from './PageHeaderActionsMenu.css';
|
||||||
|
|
||||||
function PageHeaderActionsMenu(props) {
|
function PageHeaderActionsMenu(props) {
|
||||||
@@ -33,7 +32,7 @@ function PageHeaderActionsMenu(props) {
|
|||||||
className={styles.itemIcon}
|
className={styles.itemIcon}
|
||||||
name={icons.KEYBOARD}
|
name={icons.KEYBOARD}
|
||||||
/>
|
/>
|
||||||
{translate('KeyboardShortcuts')}
|
Keyboard Shortcuts
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItemSeparator />
|
<MenuItemSeparator />
|
||||||
@@ -43,7 +42,7 @@ function PageHeaderActionsMenu(props) {
|
|||||||
className={styles.itemIcon}
|
className={styles.itemIcon}
|
||||||
name={icons.RESTART}
|
name={icons.RESTART}
|
||||||
/>
|
/>
|
||||||
{translate('Restart')}
|
Restart
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem onPress={onShutdownPress}>
|
<MenuItem onPress={onShutdownPress}>
|
||||||
@@ -52,7 +51,7 @@ function PageHeaderActionsMenu(props) {
|
|||||||
name={icons.SHUTDOWN}
|
name={icons.SHUTDOWN}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
/>
|
/>
|
||||||
{translate('Shutdown')}
|
Shutdown
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user