mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-18 21:35:51 -04:00
Compare commits
764 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bca1a71a2 | |||
| 4f009bb81d | |||
| bdc7733faf | |||
| 7f4be53db0 | |||
| 27d998d6f2 | |||
| 5eb593f453 | |||
| 4652db0583 | |||
| 35d43480bf | |||
| 3e7c136a7f | |||
| 05f9f6b413 | |||
| 67f6eb544a | |||
| 3c11e934a8 | |||
| 627a39b8fc | |||
| 95c7b96dff | |||
| dadd59fc3a | |||
| e67d3d3666 | |||
| f4718243ed | |||
| fcec787eb6 | |||
| 5fe8f65d64 | |||
| c2a21cd238 | |||
| a31ca4e80b | |||
| db14ac4605 | |||
| 5f229b78be | |||
| 543f2e7ddc | |||
| d6b7ab6260 | |||
| d7ab9292fb | |||
| 4300d8d8c6 | |||
| 446b2ffff9 | |||
| 695720b552 | |||
| c47934c5ca | |||
| 9938737cd7 | |||
| 58326f05e0 | |||
| 04ad5ec9c0 | |||
| 2c008384dd | |||
| a9b605c872 | |||
| cd9b469823 | |||
| df4bfa501c | |||
| 194a1e5154 | |||
| e53b2bb83c | |||
| 10772c09ef | |||
| f9ed15409a | |||
| f75ab93458 | |||
| 7755a8bd3b | |||
| 017c7998be | |||
| f40ddfef10 | |||
| 80049909eb | |||
| fc22264f89 | |||
| aba2e10b5c | |||
| 4b6874d551 | |||
| 58934a30ce | |||
| 33a960f325 | |||
| 8560ff43fe | |||
| c88a47b275 | |||
| 67fe9101d9 | |||
| af99c78352 | |||
| df3253f55c | |||
| b9abc1be11 | |||
| 5c0ee04271 | |||
| 5e2cd3798b | |||
| 83041b1d37 | |||
| 9f6c48191b | |||
| 5696fa2efe | |||
| d38311b717 | |||
| aa522066ee | |||
| d2ba70c4d7 | |||
| ca2e62492d | |||
| 02bcb4d865 | |||
| 36962f176f | |||
| 0a2afe692f | |||
| 5140ee8f2e | |||
| e47ceae0c5 | |||
| 906f8c1049 | |||
| 27e871656e | |||
| 1ff147c541 | |||
| 7028bfb082 | |||
| efdb9c20d4 | |||
| 9bf872c9fa | |||
| feaeda9186 | |||
| e9cf9d05f5 | |||
| a1211f54de | |||
| c7b5e6204c | |||
| 788404ee27 | |||
| 8ef4429b9e | |||
| 7c5fc1e4b0 | |||
| c66f7abea5 | |||
| ce6f52552a | |||
| 7807e2e13a | |||
| d5aa73fe8f | |||
| cb5bf86d8e | |||
| 8d17f0b1db | |||
| 2e62aad279 | |||
| f2c9fcde7c | |||
| 2716bd6ae1 | |||
| b67f6b98ae | |||
| 7de2c8d41f | |||
| 5dabec5cc8 | |||
| fc61687e82 | |||
| 398fc4dca2 | |||
| f883cab6db | |||
| 0a18898e36 | |||
| 19d533b50a | |||
| 4ce98d689d | |||
| ef9c1bf0a4 | |||
| bec8312d61 | |||
| ac5736c05b | |||
| 35abd99b3f | |||
| 0fb8aabb41 | |||
| cbc14a1a2c | |||
| 627ab64fd0 | |||
| 7fdfd0cfb7 | |||
| 4052e7160c | |||
| f5d3defa52 | |||
| 4cb87309b2 | |||
| c6ec9c316d | |||
| fc26f78558 | |||
| 4eaad6ca4b | |||
| 7abbe2c8c0 | |||
| 40839424be | |||
| 3234cff09c | |||
| d715bb4437 | |||
| df88d9a8bf | |||
| a3f4695442 | |||
| 321ce87d4d | |||
| 30e9a7a1b9 | |||
| f26b00444f | |||
| e088a7258e | |||
| f705692fb7 | |||
| 92feeb49a6 | |||
| 0d7d647291 | |||
| 2ddaef5c50 | |||
| 0aaf0f702b | |||
| 97e9d64397 | |||
| f1583dcb13 | |||
| dc4dbf4610 | |||
| bb21798c42 | |||
| 07b2890f30 | |||
| d9534a857f | |||
| 2b17b5310e | |||
| b330a15431 | |||
| ad79250783 | |||
| 845b96508f | |||
| cff95eb251 | |||
| d8542f4689 | |||
| 99642f15fe | |||
| 5d1c600062 | |||
| 5cee22f207 | |||
| bb3871e9ea | |||
| 434c095c54 | |||
| 4808fb1429 | |||
| 7dd6805512 | |||
| c31070ab91 | |||
| e6974eb99e | |||
| a72e3d78d7 | |||
| 7c707babff | |||
| 3b254fd54f | |||
| d9e9df6c26 | |||
| c6b969cd61 | |||
| 5ccee34107 | |||
| b8724c384a | |||
| acd82e36c5 | |||
| afd2b7a29e | |||
| 74a26b02db | |||
| 356926a445 | |||
| 19128e70e7 | |||
| aa75bc241d | |||
| 88780f33a4 | |||
| 1ed30713f5 | |||
| 3b3dbfec60 | |||
| e011614570 | |||
| 2a1b2187ba | |||
| ea3a6171fa | |||
| c9477d4b5a | |||
| cc466b9e5b | |||
| e9ece8a319 | |||
| 22a5304ad8 | |||
| bcb16d484a | |||
| 50ebd283dd | |||
| 53b9332675 | |||
| bfc969c45c | |||
| 31770cd889 | |||
| 6267497fc0 | |||
| efeb216383 | |||
| 8eb0f33718 | |||
| 176bf7ce32 | |||
| 4340b6b922 | |||
| ec2c7ec5af | |||
| f3fb67a62e | |||
| ec8d1c4ae6 | |||
| ea3e523f8d | |||
| e50550bb33 | |||
| e0957bfd20 | |||
| 582322adfb | |||
| 7a3964c42a | |||
| b2f6c4dcdc | |||
| 45f8700689 | |||
| 7c5fe561e0 | |||
| 5145a4a211 | |||
| 2aa2164763 | |||
| 05bde70b14 | |||
| d8c138694d | |||
| ad8504fa89 | |||
| 2f26a955d1 | |||
| 3d7808745b | |||
| 1d7082593d | |||
| 82420d6956 | |||
| 77b84be8d1 | |||
| 24330beca9 | |||
| dd50f1c564 | |||
| 4f63015a3e | |||
| 568185c908 | |||
| b56525de27 | |||
| 4673736b67 | |||
| ffae7d6b31 | |||
| 42146fc366 | |||
| de6a465370 | |||
| bef12f10a8 | |||
| 8064a43dee | |||
| 3c2e127601 | |||
| 6ea370870d | |||
| f6f8934ce3 | |||
| 3c59b6aac0 | |||
| 2e0d603f4a | |||
| e8972f2273 | |||
| 578ce25166 | |||
| fc12770495 | |||
| 5d5e66f0d7 | |||
| fe2af13fae | |||
| 10205da1c3 | |||
| 914d3764dc | |||
| 59f8a4d2e2 | |||
| 024000275d | |||
| a3723b5ad7 | |||
| e9e034d193 | |||
| 52dbcfe852 | |||
| f488fe75e0 | |||
| 47e85e6388 | |||
| 3264de3a96 | |||
| 1c04aad92e | |||
| 3b2cf55788 | |||
| b66ce95139 | |||
| f6753bf2c2 | |||
| 2a981c3bbc | |||
| 82004b6535 | |||
| eb36a1c134 | |||
| 02109437e4 | |||
| 38872ffc0a | |||
| 5e940330b4 | |||
| 53b3b58477 | |||
| d6101259ad | |||
| cf94178623 | |||
| 999f6cbcd3 | |||
| f0191525a1 | |||
| 596803cf0b | |||
| e90465c4ae | |||
| 0c21467411 | |||
| d9d1d8ecad | |||
| d9faeb11c2 | |||
| 7b13ec0dbd | |||
| 6ffc57c106 | |||
| ee1bcb20dc | |||
| 19d8604022 | |||
| 5926ca5d8a | |||
| 24de3fdee6 | |||
| be40a0d738 | |||
| 2b97352661 | |||
| 88bc44f7ba | |||
| 95527218af | |||
| 8a062d1cba | |||
| 4acd885f19 | |||
| a19c292779 | |||
| aa9880b8dd | |||
| abe8a06c11 | |||
| 7ea749e93c | |||
| f85fc5e578 | |||
| 20e3a357a5 | |||
| 60ac9d3ad7 | |||
| e5fc423f29 | |||
| 3f5ff23210 | |||
| a9792973ee | |||
| 90289de22c | |||
| a68011988e | |||
| 2fd030bd5c | |||
| 33cd9f555e | |||
| 94df76b6e6 | |||
| 065846464d | |||
| 108609d064 | |||
| 0fcad533eb | |||
| c7b5a42bea | |||
| fa6b7ad287 | |||
| b51ce06e04 | |||
| 63cf10c29a | |||
| 6b5e743583 | |||
| 743946b929 | |||
| cf89533b37 | |||
| 7031a8fe44 | |||
| d8c962a911 | |||
| 6702c7d21b | |||
| e64dd799e6 | |||
| 47c2a15b70 | |||
| 4bf726cc66 | |||
| dd61480d60 | |||
| b0753ab153 | |||
| 281afe7d9d | |||
| bd0fe16b52 | |||
| 901723b8e3 | |||
| 3a146ea667 | |||
| 88b9a47c79 | |||
| fe5445ba31 | |||
| 39f08b6283 | |||
| 8831fd7b50 | |||
| 70e4324a7c | |||
| 35a53c5627 | |||
| 9b85f328a6 | |||
| 012fe53acc | |||
| 566c1405c2 | |||
| 83c637e8cb | |||
| fd612f9258 | |||
| 621edba82d | |||
| f03dfda2f0 | |||
| e623efefd3 | |||
| 7742a81782 | |||
| 8588d2e2a3 | |||
| 86c5f06c5b | |||
| 6d7fb3de25 | |||
| a0d2af54e8 | |||
| ad3ddd11cf | |||
| 1372abecc0 | |||
| 3ddbd30604 | |||
| eff2e11652 | |||
| aa95781d5d | |||
| 6e46720d7b | |||
| 7e70166b62 | |||
| 444305aa65 | |||
| 8fd1f75e21 | |||
| 625cdab04a | |||
| 325a9c34e3 | |||
| a7e4078c53 | |||
| d2bfddc182 | |||
| a6777015f7 | |||
| f8b211c561 | |||
| fce86db9c5 | |||
| 2ff07a1368 | |||
| 208b17e9f5 | |||
| e0ce7b3294 | |||
| 7af75da694 | |||
| 86213d91a2 | |||
| eed4a4ec68 | |||
| 24d4390550 | |||
| 4983cb9745 | |||
| e71e386cb1 | |||
| d03a37ed60 | |||
| 5ea88f6bcd | |||
| d74415a3d7 | |||
| f4deefd21e | |||
| 6296356e42 | |||
| a777880a91 | |||
| eb311f33ef | |||
| 34ba436204 | |||
| 45bb108127 | |||
| 0443cc34c6 | |||
| 6ad6bf270f | |||
| 2237624333 | |||
| 39d11b4669 | |||
| 70faa123ee | |||
| f4869e6bc9 | |||
| 2a3fc81b8a | |||
| a2182e2fca | |||
| fe23c985a2 | |||
| 4ffd0d2ddb | |||
| efd3aad6b0 | |||
| 11b4967629 | |||
| b6e46ad8fd | |||
| c28b62da2a | |||
| 600c7e3391 | |||
| 7107aeaddc | |||
| 913766f9af | |||
| 9e7ea6f55b | |||
| c2c4186965 | |||
| 1a755bd3c0 | |||
| e5f66da087 | |||
| 2a93686360 | |||
| f33f004aa9 | |||
| 30293bc7cc | |||
| a1db7a8f1e | |||
| 539fcb91c9 | |||
| 970848acc3 | |||
| 2b7a383886 | |||
| 4dc67c027b | |||
| a12395b468 | |||
| 19dceb35fa | |||
| b56043504b | |||
| f2efdd8b6d | |||
| 13b14f4d38 | |||
| ecc2996669 | |||
| 1df31f9059 | |||
| 79d4e0da80 | |||
| 0dcb07f170 | |||
| e8040a0819 | |||
| 553a8f2a0a | |||
| c892b827af | |||
| 1b36b512d7 | |||
| 560dc1ffed | |||
| 14d43e6d6b | |||
| bc1e131e49 | |||
| 61509b53d1 | |||
| b7f680ac98 | |||
| c9526b7dfd | |||
| 9f6bca0810 | |||
| 114549ca7b | |||
| 481863ca1f | |||
| 13e55f7c37 | |||
| 397442f32e | |||
| 71ae27fc0c | |||
| 286468e6ab | |||
| aafd82ba73 | |||
| cff5c03af3 | |||
| c394fcc085 | |||
| a6f237bd70 | |||
| 013915be62 | |||
| 9bec7d2ad0 | |||
| 808797f403 | |||
| 230a3df3f3 | |||
| 26f2b6a3d9 | |||
| e039dd81df | |||
| cd6dd33359 | |||
| 8803ef4dbd | |||
| 5f0b2c91a1 | |||
| 1684fbb119 | |||
| 326bcdc18c | |||
| ac0845eedf | |||
| aa00959010 | |||
| 986f8d43c0 | |||
| 07f5312d43 | |||
| 7754589353 | |||
| 8428de7a00 | |||
| d210082dad | |||
| 4f9df6a114 | |||
| a5726cd65e | |||
| 3bfb08c3ff | |||
| ceeea9f7c6 | |||
| 83b1e037d5 | |||
| 9cc5343547 | |||
| e3cc4f4cb1 | |||
| f0bb614360 | |||
| 9241f4a40d | |||
| 8e6157074d | |||
| 188b02e5c7 | |||
| acb8e1d3c8 | |||
| ab31cccfb2 | |||
| 42afaa17ee | |||
| c6123a076f | |||
| 0f0aaeef4b | |||
| 2983e27dcd | |||
| a18036eed4 | |||
| 2013e1f580 | |||
| 1e396ead5e | |||
| 5ea3ad45cb | |||
| 26a04c9fd1 | |||
| 7f29af6d4d | |||
| 92f3a18e17 | |||
| 7836246b05 | |||
| e2b2061ee1 | |||
| 8d651e6f7a | |||
| 25c0ed1cd0 | |||
| 638db3d8d7 | |||
| 3a5d18f52d | |||
| c8d0a6a4ea | |||
| 553b8b1945 | |||
| e263d066da | |||
| cdaf88efa1 | |||
| ea801665e4 | |||
| a81b6f41af | |||
| 177d77db69 | |||
| 8dafa29f8c | |||
| 6b3bdb4f88 | |||
| 56bb319934 | |||
| 6de847a361 | |||
| 94591dea93 | |||
| 5cb20b0dac | |||
| 69954c50cb | |||
| e5d72bb995 | |||
| ae7c78ba48 | |||
| 84498d9e92 | |||
| d12b75e223 | |||
| b27367088e | |||
| 570bc87ab7 | |||
| 1ae4c53e27 | |||
| cd84045161 | |||
| 667fdc64e3 | |||
| e424264504 | |||
| 7716c31ebd | |||
| 6d729416d9 | |||
| 1668978fc6 | |||
| 874840c921 | |||
| defd08addf | |||
| c072e0a2ca | |||
| d9ce781dfa | |||
| a438d43c57 | |||
| 81f0fd38ac | |||
| bbc9de464c | |||
| 0738e35583 | |||
| 6e2f13f76b | |||
| 2fb36f7c6c | |||
| 5ead82903c | |||
| b4c03aae6d | |||
| 2a8a2ce3b0 | |||
| c75a9dfe34 | |||
| 7410a2db5d | |||
| 74c89e1d44 | |||
| f120b84c43 | |||
| 88ec106ec6 | |||
| 99b75a3089 | |||
| 50592d5bb6 | |||
| 9fce6b1026 | |||
| 5f32627bfb | |||
| 56b658cc96 | |||
| c22ae12c1f | |||
| 4bac44e893 | |||
| 1c58e26183 | |||
| 4d965955d1 | |||
| 23b9d657ce | |||
| c046c3c229 | |||
| f734ba2323 | |||
| 44fa36373c | |||
| e6afcb68f3 | |||
| c8c900251a | |||
| 915e7ce589 | |||
| 5bab42b712 | |||
| f499e846d9 | |||
| 80a744eff5 | |||
| 82dfaf0ea5 | |||
| 8f78953191 | |||
| 9261efa843 | |||
| b9a8166f9f | |||
| 8cccde48c1 | |||
| 96384521c5 | |||
| f90211b0d3 | |||
| d1bebd3e5b | |||
| a32bc21907 | |||
| 4f7dc94b94 | |||
| 5ca1a1d086 | |||
| 7d763b8ba0 | |||
| 23c197bd3b | |||
| 8c892a732e | |||
| 140547e42a | |||
| 3ab9af0663 | |||
| a71874db8d | |||
| 226ee34fec | |||
| eb5add1198 | |||
| 3759625386 | |||
| b85f3f0e35 | |||
| 286b083da4 | |||
| 912273b5dd | |||
| 73d1486392 | |||
| 063c6fb85c | |||
| c95573a7da | |||
| a6cf524fe7 | |||
| bb264e9a8a | |||
| ca0cabe985 | |||
| 48c3aea9bb | |||
| 4122b82601 | |||
| 3abf67aaf0 | |||
| 2b860aab36 | |||
| 85bfcd72ff | |||
| df15c636a4 | |||
| 1136de29e5 | |||
| ffcb34f9f0 | |||
| 877b6c0fcf | |||
| 854c1a6866 | |||
| cd8ed08376 | |||
| b64c938188 | |||
| bd930f3b8d | |||
| 0880cd6ffc | |||
| c2a96ff485 | |||
| 21e92e7fcf | |||
| 96f0bb2f90 | |||
| c0e1b97f29 | |||
| fd4fb88ce1 | |||
| 003686d68f | |||
| 3e0e4ba1fd | |||
| 45ecfb05f4 | |||
| ab620d373c | |||
| 9b3faefc8e | |||
| dd577f9db0 | |||
| 5c0939d9df | |||
| bdb818b1b6 | |||
| 4550ef13a4 | |||
| 98f0769c5d | |||
| 6f854ddd56 | |||
| 98ae094733 | |||
| 15e4dc18b9 | |||
| d5a7479b2e | |||
| e1bff99a93 | |||
| 9d31aed9da | |||
| 9fbc1df74c | |||
| 2d9c77ec5e | |||
| ede7d37cae | |||
| b580b4f829 | |||
| 4f281669fc | |||
| 9fbedc32ed | |||
| 745007209e | |||
| 3e1cfe0033 | |||
| 94b94041a8 | |||
| 791a32b939 | |||
| af395f554e | |||
| 39640a710f | |||
| 6557f72600 | |||
| 14b127e60d | |||
| e6e6205799 | |||
| 083c4750fb | |||
| 099d0b6e67 | |||
| 17615e9ace | |||
| 9179b9e0eb | |||
| 3389cc0cf8 | |||
| 21c297024f | |||
| c4324c8e47 | |||
| ac8fa1ee93 | |||
| 93cff9c6ef | |||
| 3d0e993a53 | |||
| 87330c8ab9 | |||
| 785ad5d62e | |||
| 0ddf19d384 | |||
| 30c9446165 | |||
| e104a9e261 | |||
| 4c19fa0d05 | |||
| 7cd29c48bb | |||
| 1aa117331f | |||
| 89baa3ff6d | |||
| c4b729697d | |||
| 9052ef11a4 | |||
| 3d5d884505 | |||
| 23a8b68869 | |||
| 46ff3b7538 | |||
| 444b8e3436 | |||
| 413669dbaa | |||
| 564a7554fc | |||
| c16ca46f11 | |||
| a960716cec | |||
| f55e44c591 | |||
| 17f8eb3aae | |||
| 082a0679d2 | |||
| 5182ac2a13 | |||
| 0e1cb335a9 | |||
| 078cdc0cb0 | |||
| 0366029269 | |||
| 6d011cdc48 | |||
| 9f1833dd9e | |||
| 882b1d6427 | |||
| d73f1fe954 | |||
| b707dcc20f | |||
| c1e1df3c22 | |||
| 2297b9cc14 | |||
| 9225e04f80 | |||
| 7496ae0b69 | |||
| 3443738e21 | |||
| f632386b5c | |||
| 4f26081f9d | |||
| 8c5709aec8 | |||
| 6f046293b8 | |||
| c9519dc4b6 | |||
| 1c1b8902c2 | |||
| 053bce7a0f | |||
| b3476c4b28 | |||
| 1b372c41ca | |||
| 624a7ad00a | |||
| 5079db5dd8 | |||
| a8f517b64f | |||
| 2ca31afa8a | |||
| 5eb5e5d5ab | |||
| a152ab2b56 | |||
| 6bfd9f0e18 | |||
| bb418f7191 | |||
| e88b4b10e9 | |||
| f72a56f88a | |||
| c55a832934 | |||
| ff62ac22e8 | |||
| 699d2be938 | |||
| 86118b4537 | |||
| 1a7ead0e51 | |||
| dbd241bbc5 | |||
| 1ee270114b | |||
| f90133d588 | |||
| bb721272ca | |||
| e3ac7d6fc5 | |||
| 28e2834c5d | |||
| 9c77399379 | |||
| 73ce77f1ca | |||
| 984d0101bf | |||
| f9242af246 | |||
| 0b5a40a2b7 | |||
| 0221df15e2 | |||
| c6294b71ae | |||
| 2fbd829b93 | |||
| 5ed4a386dd | |||
| f9733eea7a | |||
| 59f9ff7a22 | |||
| 460c86911c | |||
| a04f375eb1 | |||
| 7792c159b4 | |||
| 6beb8c8b05 | |||
| 6d0019ad00 | |||
| ef8ba37d0e | |||
| 5e1b197702 | |||
| 4d43c3eb2b | |||
| 4c1de51b52 | |||
| bdb61e263e | |||
| 616ae471d1 | |||
| ce11d4c7bf | |||
| a7ba19ddd8 | |||
| 784913a85b | |||
| 67ebf49b21 | |||
| 0e5f43e139 | |||
| d5addfa12f | |||
| 96852c9f96 | |||
| 7311a1e837 | |||
| a591227052 | |||
| 897bfa04a6 | |||
| 3e795d290b | |||
| b7719662a7 | |||
| f1b7f5b355 | |||
| 164a7f0f8c | |||
| cfa0e9aa19 | |||
| d9ff8e0d4a | |||
| b888b044d6 | |||
| 9be8d438e7 | |||
| 642783d3de | |||
| cf08f483b2 | |||
| f6c630ee9e | |||
| f5a46b14ac | |||
| 3a8ca21a17 | |||
| 70a9a68ed3 | |||
| dc96857e66 | |||
| bb5f5eeee6 | |||
| d7938b6282 | |||
| c21e733d57 | |||
| 75866b135c | |||
| 5aaf8dcf0e | |||
| 1dcff1b742 | |||
| adedbc1bed | |||
| 4fa1955008 | |||
| bab9b2c040 | |||
| 216f9b37b2 | |||
| 025f064ecf | |||
| b8df38e929 | |||
| 72f0dadbea | |||
| 3e22658e27 | |||
| d00f3abbf0 | |||
| c270a53eab | |||
| b474961b65 | |||
| cfb44c7db7 | |||
| 45011198e2 | |||
| a03d136aa4 | |||
| e2a45caada | |||
| e3db49896b | |||
| 920b7d8e29 | |||
| 0df442caf2 | |||
| d835358f75 | |||
| 45d329423c | |||
| f177ebfb4a | |||
| 6e135b1e36 | |||
| 31ca413fc7 | |||
| 959d63b4eb | |||
| 09ed2ab889 | |||
| 1374a12d4c |
+12
-1
@@ -110,13 +110,13 @@ dotnet_diagnostic.SA1643.severity = none
|
|||||||
dotnet_diagnostic.SA1648.severity = none
|
dotnet_diagnostic.SA1648.severity = none
|
||||||
dotnet_diagnostic.SA1649.severity = none
|
dotnet_diagnostic.SA1649.severity = none
|
||||||
dotnet_diagnostic.SA1651.severity = none
|
dotnet_diagnostic.SA1651.severity = none
|
||||||
dotnet_diagnostic.SX1101.severity = warning
|
|
||||||
dotnet_diagnostic.SX1309.severity = warning
|
dotnet_diagnostic.SX1309.severity = warning
|
||||||
|
|
||||||
# Microsoft Analyzers that fail and need to be sorted thru
|
# Microsoft Analyzers that fail and need to be sorted thru
|
||||||
dotnet_diagnostic.ASP0000.severity = suggestion
|
dotnet_diagnostic.ASP0000.severity = suggestion
|
||||||
dotnet_diagnostic.CA1000.severity = suggestion
|
dotnet_diagnostic.CA1000.severity = suggestion
|
||||||
dotnet_diagnostic.CA1001.severity = suggestion
|
dotnet_diagnostic.CA1001.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1002.severity = suggestion
|
||||||
dotnet_diagnostic.CA1003.severity = suggestion
|
dotnet_diagnostic.CA1003.severity = suggestion
|
||||||
dotnet_diagnostic.CA1008.severity = suggestion
|
dotnet_diagnostic.CA1008.severity = suggestion
|
||||||
dotnet_diagnostic.CA1010.severity = suggestion
|
dotnet_diagnostic.CA1010.severity = suggestion
|
||||||
@@ -163,10 +163,16 @@ dotnet_diagnostic.CA1304.severity = suggestion
|
|||||||
dotnet_diagnostic.CA1305.severity = suggestion
|
dotnet_diagnostic.CA1305.severity = suggestion
|
||||||
dotnet_diagnostic.CA1307.severity = suggestion
|
dotnet_diagnostic.CA1307.severity = suggestion
|
||||||
dotnet_diagnostic.CA1308.severity = suggestion
|
dotnet_diagnostic.CA1308.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1309.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1310.severity = suggestion
|
||||||
dotnet_diagnostic.CA1401.severity = suggestion
|
dotnet_diagnostic.CA1401.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1416.severity = suggestion
|
||||||
dotnet_diagnostic.CA1507.severity = suggestion
|
dotnet_diagnostic.CA1507.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1508.severity = suggestion
|
||||||
dotnet_diagnostic.CA1707.severity = suggestion
|
dotnet_diagnostic.CA1707.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1708.severity = suggestion
|
||||||
dotnet_diagnostic.CA1710.severity = suggestion
|
dotnet_diagnostic.CA1710.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1711.severity = suggestion
|
||||||
dotnet_diagnostic.CA1712.severity = suggestion
|
dotnet_diagnostic.CA1712.severity = suggestion
|
||||||
dotnet_diagnostic.CA1714.severity = suggestion
|
dotnet_diagnostic.CA1714.severity = suggestion
|
||||||
dotnet_diagnostic.CA1715.severity = suggestion
|
dotnet_diagnostic.CA1715.severity = suggestion
|
||||||
@@ -175,12 +181,14 @@ dotnet_diagnostic.CA1717.severity = suggestion
|
|||||||
dotnet_diagnostic.CA1720.severity = suggestion
|
dotnet_diagnostic.CA1720.severity = suggestion
|
||||||
dotnet_diagnostic.CA1721.severity = suggestion
|
dotnet_diagnostic.CA1721.severity = suggestion
|
||||||
dotnet_diagnostic.CA1724.severity = suggestion
|
dotnet_diagnostic.CA1724.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1725.severity = suggestion
|
||||||
dotnet_diagnostic.CA1801.severity = suggestion
|
dotnet_diagnostic.CA1801.severity = suggestion
|
||||||
dotnet_diagnostic.CA1802.severity = suggestion
|
dotnet_diagnostic.CA1802.severity = suggestion
|
||||||
dotnet_diagnostic.CA1805.severity = suggestion
|
dotnet_diagnostic.CA1805.severity = suggestion
|
||||||
dotnet_diagnostic.CA1806.severity = suggestion
|
dotnet_diagnostic.CA1806.severity = suggestion
|
||||||
dotnet_diagnostic.CA1810.severity = suggestion
|
dotnet_diagnostic.CA1810.severity = suggestion
|
||||||
dotnet_diagnostic.CA1812.severity = suggestion
|
dotnet_diagnostic.CA1812.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA1813.severity = suggestion
|
||||||
dotnet_diagnostic.CA1814.severity = suggestion
|
dotnet_diagnostic.CA1814.severity = suggestion
|
||||||
dotnet_diagnostic.CA1815.severity = suggestion
|
dotnet_diagnostic.CA1815.severity = suggestion
|
||||||
dotnet_diagnostic.CA1816.severity = suggestion
|
dotnet_diagnostic.CA1816.severity = suggestion
|
||||||
@@ -202,6 +210,7 @@ dotnet_diagnostic.CA2101.severity = suggestion
|
|||||||
dotnet_diagnostic.CA2119.severity = suggestion
|
dotnet_diagnostic.CA2119.severity = suggestion
|
||||||
dotnet_diagnostic.CA2153.severity = suggestion
|
dotnet_diagnostic.CA2153.severity = suggestion
|
||||||
dotnet_diagnostic.CA2200.severity = suggestion
|
dotnet_diagnostic.CA2200.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA2201.severity = suggestion
|
||||||
dotnet_diagnostic.CA2207.severity = suggestion
|
dotnet_diagnostic.CA2207.severity = suggestion
|
||||||
dotnet_diagnostic.CA2208.severity = suggestion
|
dotnet_diagnostic.CA2208.severity = suggestion
|
||||||
dotnet_diagnostic.CA2211.severity = suggestion
|
dotnet_diagnostic.CA2211.severity = suggestion
|
||||||
@@ -247,6 +256,8 @@ dotnet_diagnostic.CA5374.severity = suggestion
|
|||||||
dotnet_diagnostic.CA5379.severity = suggestion
|
dotnet_diagnostic.CA5379.severity = suggestion
|
||||||
dotnet_diagnostic.CA5384.severity = suggestion
|
dotnet_diagnostic.CA5384.severity = suggestion
|
||||||
dotnet_diagnostic.CA5385.severity = suggestion
|
dotnet_diagnostic.CA5385.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA5392.severity = suggestion
|
||||||
|
dotnet_diagnostic.CA5394.severity = suggestion
|
||||||
dotnet_diagnostic.CA5397.severity = suggestion
|
dotnet_diagnostic.CA5397.severity = suggestion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"paths": [
|
"paths": [
|
||||||
"frontend/src/**/*.js"
|
"frontend/src/**/*.js",
|
||||||
|
"src/NzbDrone.Core/Localization/Core/*.json"
|
||||||
],
|
],
|
||||||
"ignored": [
|
"ignored": [
|
||||||
"**/node_modules/**/*"
|
"**/node_modules/**/*"
|
||||||
|
|||||||
@@ -1,305 +0,0 @@
|
|||||||
# -*- coding: utf-8; mode: python -*-
|
|
||||||
##
|
|
||||||
## Format
|
|
||||||
##
|
|
||||||
## ACTION: [AUDIENCE:] COMMIT_MSG [!TAG ...]
|
|
||||||
##
|
|
||||||
## Description
|
|
||||||
##
|
|
||||||
## ACTION is one of 'chg', 'fix', 'new'
|
|
||||||
##
|
|
||||||
## Is WHAT the change is about.
|
|
||||||
##
|
|
||||||
## 'chg' is for refactor, small improvement, cosmetic changes...
|
|
||||||
## 'fix' is for bug fixes
|
|
||||||
## 'new' is for new features, big improvement
|
|
||||||
##
|
|
||||||
## AUDIENCE is optional and one of 'dev', 'usr', 'pkg', 'test', 'doc'
|
|
||||||
##
|
|
||||||
## Is WHO is concerned by the change.
|
|
||||||
##
|
|
||||||
## 'dev' is for developpers (API changes, refactors...)
|
|
||||||
## 'usr' is for final users (UI changes)
|
|
||||||
## 'pkg' is for packagers (packaging changes)
|
|
||||||
## 'test' is for testers (test only related changes)
|
|
||||||
## 'doc' is for doc guys (doc only changes)
|
|
||||||
##
|
|
||||||
## COMMIT_MSG is ... well ... the commit message itself.
|
|
||||||
##
|
|
||||||
## TAGs are additionnal adjective as 'refactor' 'minor' 'cosmetic'
|
|
||||||
##
|
|
||||||
## They are preceded with a '!' or a '@' (prefer the former, as the
|
|
||||||
## latter is wrongly interpreted in github.) Commonly used tags are:
|
|
||||||
##
|
|
||||||
## 'refactor' is obviously for refactoring code only
|
|
||||||
## 'minor' is for a very meaningless change (a typo, adding a comment)
|
|
||||||
## 'cosmetic' is for cosmetic driven change (re-indentation, 80-col...)
|
|
||||||
## 'wip' is for partial functionality but complete subfunctionality.
|
|
||||||
##
|
|
||||||
## Example:
|
|
||||||
##
|
|
||||||
## new: usr: support of bazaar implemented
|
|
||||||
## chg: re-indentend some lines !cosmetic
|
|
||||||
## new: dev: updated code to be compatible with last version of killer lib.
|
|
||||||
## fix: pkg: updated year of licence coverage.
|
|
||||||
## new: test: added a bunch of test around user usability of feature X.
|
|
||||||
## fix: typo in spelling my name in comment. !minor
|
|
||||||
##
|
|
||||||
## Please note that multi-line commit message are supported, and only the
|
|
||||||
## first line will be considered as the "summary" of the commit message. So
|
|
||||||
## tags, and other rules only applies to the summary. The body of the commit
|
|
||||||
## message will be displayed in the changelog without reformatting.
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
## ``ignore_regexps`` is a line of regexps
|
|
||||||
##
|
|
||||||
## Any commit having its full commit message matching any regexp listed here
|
|
||||||
## will be ignored and won't be reported in the changelog.
|
|
||||||
##
|
|
||||||
ignore_regexps = [
|
|
||||||
r'@minor', r'!minor',
|
|
||||||
r'@cosmetic', r'!cosmetic',
|
|
||||||
r'@refactor', r'!refactor',
|
|
||||||
r'@wip', r'!wip',
|
|
||||||
r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:',
|
|
||||||
r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:',
|
|
||||||
r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$',
|
|
||||||
r'^$', ## ignore commits with empty messages
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
## ``section_regexps`` is a list of 2-tuples associating a string label and a
|
|
||||||
## list of regexp
|
|
||||||
##
|
|
||||||
## Commit messages will be classified in sections thanks to this. Section
|
|
||||||
## titles are the label, and a commit is classified under this section if any
|
|
||||||
## of the regexps associated is matching.
|
|
||||||
##
|
|
||||||
## Please note that ``section_regexps`` will only classify commits and won't
|
|
||||||
## make any changes to the contents. So you'll probably want to go check
|
|
||||||
## ``subject_process`` (or ``body_process``) to do some changes to the subject,
|
|
||||||
## whenever you are tweaking this variable.
|
|
||||||
##
|
|
||||||
section_regexps = [
|
|
||||||
('**New features**', [
|
|
||||||
r'^[aA]dded?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?(.*)$',
|
|
||||||
r'^[uU]pdated?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
|
||||||
r'^[cC]hanged?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
|
||||||
r'^[nN]ew?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
|
||||||
]),
|
|
||||||
('**Fixes**', [
|
|
||||||
r'^(?![mM]erge\s*)'
|
|
||||||
]
|
|
||||||
),
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
## ``body_process`` is a callable
|
|
||||||
##
|
|
||||||
## This callable will be given the original body and result will
|
|
||||||
## be used in the changelog.
|
|
||||||
##
|
|
||||||
## Available constructs are:
|
|
||||||
##
|
|
||||||
## - any python callable that take one txt argument and return txt argument.
|
|
||||||
##
|
|
||||||
## - ReSub(pattern, replacement): will apply regexp substitution.
|
|
||||||
##
|
|
||||||
## - Indent(chars=" "): will indent the text with the prefix
|
|
||||||
## Please remember that template engines gets also to modify the text and
|
|
||||||
## will usually indent themselves the text if needed.
|
|
||||||
##
|
|
||||||
## - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns
|
|
||||||
##
|
|
||||||
## - noop: do nothing
|
|
||||||
##
|
|
||||||
## - ucfirst: ensure the first letter is uppercase.
|
|
||||||
## (usually used in the ``subject_process`` pipeline)
|
|
||||||
##
|
|
||||||
## - final_dot: ensure text finishes with a dot
|
|
||||||
## (usually used in the ``subject_process`` pipeline)
|
|
||||||
##
|
|
||||||
## - strip: remove any spaces before or after the content of the string
|
|
||||||
##
|
|
||||||
## - SetIfEmpty(msg="No commit message."): will set the text to
|
|
||||||
## whatever given ``msg`` if the current text is empty.
|
|
||||||
##
|
|
||||||
## Additionally, you can `pipe` the provided filters, for instance:
|
|
||||||
#body_process = Wrap(regexp=r'\n(?=\w+\s*:)') | Indent(chars=" ")
|
|
||||||
#body_process = Wrap(regexp=r'\n(?=\w+\s*:)')
|
|
||||||
#body_process = noop
|
|
||||||
body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip
|
|
||||||
|
|
||||||
|
|
||||||
## ``subject_process`` is a callable
|
|
||||||
##
|
|
||||||
## This callable will be given the original subject and result will
|
|
||||||
## be used in the changelog.
|
|
||||||
## subject_process = (strip |
|
|
||||||
## ReSub(r'^([aA]dd(ed?)?|[nN]ew)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
## ReSub(r'^([cC]hang(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
## ReSub(r'^([fF]ix(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
## ReSub(r'^([uU]pdat(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
## ReSub(r'#(\d{3,4})', r'[#\1](https://github.com/Radarr/Radarr/issues/\1)') |
|
|
||||||
## SetIfEmpty("No commit message.") | ucfirst | final_dot)
|
|
||||||
|
|
||||||
## Available constructs are those listed in ``body_process`` doc.
|
|
||||||
subject_process = (strip |
|
|
||||||
ReSub(r'^([aA]dd(ed?)?|[nN]ew)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
ReSub(r'^([cC]hang(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
ReSub(r'^([fF]ix(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
ReSub(r'^([uU]pdat(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
ReSub(r'#(\d{3,4})', r'[#\1](https://github.com/Radarr/Radarr/issues/\1)') |
|
|
||||||
SetIfEmpty("No commit message.") | ucfirst | final_dot)
|
|
||||||
|
|
||||||
|
|
||||||
## ``tag_filter_regexp`` is a regexp
|
|
||||||
##
|
|
||||||
## Tags that will be used for the changelog must match this regexp.
|
|
||||||
##
|
|
||||||
tag_filter_regexp = r'^v[0]+\.[2-9]+\.[0-9]+\.[0-9]{3,4}$'
|
|
||||||
|
|
||||||
|
|
||||||
## ``unreleased_version_label`` is a string or a callable that outputs a string
|
|
||||||
##
|
|
||||||
## This label will be used as the changelog Title of the last set of changes
|
|
||||||
## between last valid tag and HEAD if any.
|
|
||||||
unreleased_version_label = "(unreleased)"
|
|
||||||
|
|
||||||
|
|
||||||
## ``output_engine`` is a callable
|
|
||||||
##
|
|
||||||
## This will change the output format of the generated changelog file
|
|
||||||
##
|
|
||||||
## Available choices are:
|
|
||||||
##
|
|
||||||
## - rest_py
|
|
||||||
##
|
|
||||||
## Legacy pure python engine, outputs ReSTructured text.
|
|
||||||
## This is the default.
|
|
||||||
##
|
|
||||||
## - mustache(<template_name>)
|
|
||||||
##
|
|
||||||
## Template name could be any of the available templates in
|
|
||||||
## ``templates/mustache/*.tpl``.
|
|
||||||
## Requires python package ``pystache``.
|
|
||||||
## Examples:
|
|
||||||
## - mustache("markdown")
|
|
||||||
## - mustache("restructuredtext")
|
|
||||||
##
|
|
||||||
## - makotemplate(<template_name>)
|
|
||||||
##
|
|
||||||
## Template name could be any of the available templates in
|
|
||||||
## ``templates/mako/*.tpl``.
|
|
||||||
## Requires python package ``mako``.
|
|
||||||
## Examples:
|
|
||||||
## - makotemplate("restructuredtext")
|
|
||||||
##
|
|
||||||
#output_engine = rest_py
|
|
||||||
#output_engine = mustache("restructuredtext")
|
|
||||||
output_engine = mustache("changelog.tpl")
|
|
||||||
#output_engine = makotemplate("restructuredtext")
|
|
||||||
|
|
||||||
|
|
||||||
## ``include_merge`` is a boolean
|
|
||||||
##
|
|
||||||
## This option tells git-log whether to include merge commits in the log.
|
|
||||||
## The default is to include them.
|
|
||||||
include_merge = False
|
|
||||||
|
|
||||||
|
|
||||||
## ``log_encoding`` is a string identifier
|
|
||||||
##
|
|
||||||
## This option tells gitchangelog what encoding is outputed by ``git log``.
|
|
||||||
## The default is to be clever about it: it checks ``git config`` for
|
|
||||||
## ``i18n.logOutputEncoding``, and if not found will default to git's own
|
|
||||||
## default: ``utf-8``.
|
|
||||||
#log_encoding = 'utf-8'
|
|
||||||
|
|
||||||
|
|
||||||
## ``publish`` is a callable
|
|
||||||
##
|
|
||||||
## Sets what ``gitchangelog`` should do with the output generated by
|
|
||||||
## the output engine. ``publish`` is a callable taking one argument
|
|
||||||
## that is an interator on lines from the output engine.
|
|
||||||
##
|
|
||||||
## Some helper callable are provided:
|
|
||||||
##
|
|
||||||
## Available choices are:
|
|
||||||
##
|
|
||||||
## - stdout
|
|
||||||
##
|
|
||||||
## Outputs directly to standard output
|
|
||||||
## (This is the default)
|
|
||||||
##
|
|
||||||
## - FileInsertAtFirstRegexMatch(file, pattern, idx=lamda m: m.start())
|
|
||||||
##
|
|
||||||
## Creates a callable that will parse given file for the given
|
|
||||||
## regex pattern and will insert the output in the file.
|
|
||||||
## ``idx`` is a callable that receive the matching object and
|
|
||||||
## must return a integer index point where to insert the
|
|
||||||
## the output in the file. Default is to return the position of
|
|
||||||
## the start of the matched string.
|
|
||||||
##
|
|
||||||
## - FileRegexSubst(file, pattern, replace, flags)
|
|
||||||
##
|
|
||||||
## Apply a replace inplace in the given file. Your regex pattern must
|
|
||||||
## take care of everything and might be more complex. Check the README
|
|
||||||
## for a complete copy-pastable example.
|
|
||||||
##
|
|
||||||
# publish = FileInsertIntoFirstRegexMatch(
|
|
||||||
# "CHANGELOG.rst",
|
|
||||||
# r'/(?P<rev>[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n/',
|
|
||||||
# idx=lambda m: m.start(1)
|
|
||||||
# )
|
|
||||||
#publish = stdout
|
|
||||||
|
|
||||||
def write_to_file(content):
|
|
||||||
with open("CHANGELOG.md", "w+") as f:
|
|
||||||
for chunk in content:
|
|
||||||
f.write(chunk)
|
|
||||||
|
|
||||||
publish = write_to_file
|
|
||||||
|
|
||||||
|
|
||||||
## ``revs`` is a list of callable or a list of string
|
|
||||||
##
|
|
||||||
## callable will be called to resolve as strings and allow dynamical
|
|
||||||
## computation of these. The result will be used as revisions for
|
|
||||||
## gitchangelog (as if directly stated on the command line). This allows
|
|
||||||
## to filter exaclty which commits will be read by gitchangelog.
|
|
||||||
##
|
|
||||||
## To get a full documentation on the format of these strings, please
|
|
||||||
## refer to the ``git rev-list`` arguments. There are many examples.
|
|
||||||
##
|
|
||||||
## Using callables is especially useful, for instance, if you
|
|
||||||
## are using gitchangelog to generate incrementally your changelog.
|
|
||||||
##
|
|
||||||
## Some helpers are provided, you can use them::
|
|
||||||
##
|
|
||||||
## - FileFirstRegexMatch(file, pattern): will return a callable that will
|
|
||||||
## return the first string match for the given pattern in the given file.
|
|
||||||
## If you use named sub-patterns in your regex pattern, it'll output only
|
|
||||||
## the string matching the regex pattern named "rev".
|
|
||||||
##
|
|
||||||
## - Caret(rev): will return the rev prefixed by a "^", which is a
|
|
||||||
## way to remove the given revision and all its ancestor.
|
|
||||||
##
|
|
||||||
## Please note that if you provide a rev-list on the command line, it'll
|
|
||||||
## replace this value (which will then be ignored).
|
|
||||||
##
|
|
||||||
## If empty, then ``gitchangelog`` will act as it had to generate a full
|
|
||||||
## changelog.
|
|
||||||
##
|
|
||||||
## The default is to use all commits to make the changelog.
|
|
||||||
#revs = ["^1.0.3", ]
|
|
||||||
#revs = [
|
|
||||||
# Caret(
|
|
||||||
# FileFirstRegexMatch(
|
|
||||||
# "CHANGELOG.rst",
|
|
||||||
# r"(?P<rev>[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n")),
|
|
||||||
# "HEAD"
|
|
||||||
#]
|
|
||||||
revs = ["v0.2.0.134..."]
|
|
||||||
@@ -1,311 +0,0 @@
|
|||||||
# -*- coding: utf-8; mode: python -*-
|
|
||||||
##
|
|
||||||
## Format
|
|
||||||
##
|
|
||||||
## ACTION: [AUDIENCE:] COMMIT_MSG [!TAG ...]
|
|
||||||
##
|
|
||||||
## Description
|
|
||||||
##
|
|
||||||
## ACTION is one of 'chg', 'fix', 'new'
|
|
||||||
##
|
|
||||||
## Is WHAT the change is about.
|
|
||||||
##
|
|
||||||
## 'chg' is for refactor, small improvement, cosmetic changes...
|
|
||||||
## 'fix' is for bug fixes
|
|
||||||
## 'new' is for new features, big improvement
|
|
||||||
##
|
|
||||||
## AUDIENCE is optional and one of 'dev', 'usr', 'pkg', 'test', 'doc'
|
|
||||||
##
|
|
||||||
## Is WHO is concerned by the change.
|
|
||||||
##
|
|
||||||
## 'dev' is for developpers (API changes, refactors...)
|
|
||||||
## 'usr' is for final users (UI changes)
|
|
||||||
## 'pkg' is for packagers (packaging changes)
|
|
||||||
## 'test' is for testers (test only related changes)
|
|
||||||
## 'doc' is for doc guys (doc only changes)
|
|
||||||
##
|
|
||||||
## COMMIT_MSG is ... well ... the commit message itself.
|
|
||||||
##
|
|
||||||
## TAGs are additionnal adjective as 'refactor' 'minor' 'cosmetic'
|
|
||||||
##
|
|
||||||
## They are preceded with a '!' or a '@' (prefer the former, as the
|
|
||||||
## latter is wrongly interpreted in github.) Commonly used tags are:
|
|
||||||
##
|
|
||||||
## 'refactor' is obviously for refactoring code only
|
|
||||||
## 'minor' is for a very meaningless change (a typo, adding a comment)
|
|
||||||
## 'cosmetic' is for cosmetic driven change (re-indentation, 80-col...)
|
|
||||||
## 'wip' is for partial functionality but complete subfunctionality.
|
|
||||||
##
|
|
||||||
## Example:
|
|
||||||
##
|
|
||||||
## new: usr: support of bazaar implemented
|
|
||||||
## chg: re-indentend some lines !cosmetic
|
|
||||||
## new: dev: updated code to be compatible with last version of killer lib.
|
|
||||||
## fix: pkg: updated year of licence coverage.
|
|
||||||
## new: test: added a bunch of test around user usability of feature X.
|
|
||||||
## fix: typo in spelling my name in comment. !minor
|
|
||||||
##
|
|
||||||
## Please note that multi-line commit message are supported, and only the
|
|
||||||
## first line will be considered as the "summary" of the commit message. So
|
|
||||||
## tags, and other rules only applies to the summary. The body of the commit
|
|
||||||
## message will be displayed in the changelog without reformatting.
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
## ``ignore_regexps`` is a line of regexps
|
|
||||||
##
|
|
||||||
## Any commit having its full commit message matching any regexp listed here
|
|
||||||
## will be ignored and won't be reported in the changelog.
|
|
||||||
##
|
|
||||||
ignore_regexps = [
|
|
||||||
r'@minor', r'!minor',
|
|
||||||
r'@cosmetic', r'!cosmetic',
|
|
||||||
r'@refactor', r'!refactor',
|
|
||||||
r'@wip', r'!wip',
|
|
||||||
r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:',
|
|
||||||
r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:',
|
|
||||||
r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$',
|
|
||||||
r'^$', ## ignore commits with empty messages
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
## ``section_regexps`` is a list of 2-tuples associating a string label and a
|
|
||||||
## list of regexp
|
|
||||||
##
|
|
||||||
## Commit messages will be classified in sections thanks to this. Section
|
|
||||||
## titles are the label, and a commit is classified under this section if any
|
|
||||||
## of the regexps associated is matching.
|
|
||||||
##
|
|
||||||
## Please note that ``section_regexps`` will only classify commits and won't
|
|
||||||
## make any changes to the contents. So you'll probably want to go check
|
|
||||||
## ``subject_process`` (or ``body_process``) to do some changes to the subject,
|
|
||||||
## whenever you are tweaking this variable.
|
|
||||||
##
|
|
||||||
section_regexps = [
|
|
||||||
('**New features:**', [
|
|
||||||
r'^[aA]dded?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
|
||||||
r'^[uU]pdated?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
|
||||||
r'^[cC]hanged?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
|
||||||
r'^[nN]ew?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
|
||||||
]),
|
|
||||||
('**Fixes:**', [
|
|
||||||
r'^(?![mM]erge\s*)'
|
|
||||||
]
|
|
||||||
),
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
## ``body_process`` is a callable
|
|
||||||
##
|
|
||||||
## This callable will be given the original body and result will
|
|
||||||
## be used in the changelog.
|
|
||||||
##
|
|
||||||
## Available constructs are:
|
|
||||||
##
|
|
||||||
## - any python callable that take one txt argument and return txt argument.
|
|
||||||
##
|
|
||||||
## - ReSub(pattern, replacement): will apply regexp substitution.
|
|
||||||
##
|
|
||||||
## - Indent(chars=" "): will indent the text with the prefix
|
|
||||||
## Please remember that template engines gets also to modify the text and
|
|
||||||
## will usually indent themselves the text if needed.
|
|
||||||
##
|
|
||||||
## - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns
|
|
||||||
##
|
|
||||||
## - noop: do nothing
|
|
||||||
##
|
|
||||||
## - ucfirst: ensure the first letter is uppercase.
|
|
||||||
## (usually used in the ``subject_process`` pipeline)
|
|
||||||
##
|
|
||||||
## - final_dot: ensure text finishes with a dot
|
|
||||||
## (usually used in the ``subject_process`` pipeline)
|
|
||||||
##
|
|
||||||
## - strip: remove any spaces before or after the content of the string
|
|
||||||
##
|
|
||||||
## - SetIfEmpty(msg="No commit message."): will set the text to
|
|
||||||
## whatever given ``msg`` if the current text is empty.
|
|
||||||
##
|
|
||||||
## Additionally, you can `pipe` the provided filters, for instance:
|
|
||||||
#body_process = Wrap(regexp=r'\n(?=\w+\s*:)') | Indent(chars=" ")
|
|
||||||
#body_process = Wrap(regexp=r'\n(?=\w+\s*:)')
|
|
||||||
#body_process = noop
|
|
||||||
body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip
|
|
||||||
|
|
||||||
|
|
||||||
## ``subject_process`` is a callable
|
|
||||||
##
|
|
||||||
## This callable will be given the original subject and result will
|
|
||||||
## be used in the changelog.
|
|
||||||
## subject_process = (strip |
|
|
||||||
## ReSub(r'^([aA]dd(ed?)?|[nN]ew)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
## ReSub(r'^([cC]hang(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
## ReSub(r'^([fF]ix(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
## ReSub(r'^([uU]pdat(ed?)?)(\s?:?\s)(.*)$', r' \4') |
|
|
||||||
## ReSub(r'#(\d{3,4})', r'[#\1](https://github.com/Radarr/Radarr/issues/\1)') |
|
|
||||||
## SetIfEmpty("No commit message.") | ucfirst | final_dot)
|
|
||||||
|
|
||||||
## Available constructs are those listed in ``body_process`` doc.
|
|
||||||
subject_process = (strip |
|
|
||||||
ReSub(r'^([aA]dd(ed?)?|[nN]ew)(\s?:?\s)(.*)$', r'\4') |
|
|
||||||
ReSub(r'^([cC]hang(ed?)?)(\s?:?\s)(.*)$', r'\4') |
|
|
||||||
ReSub(r'^([fF]ix(ed?)?)(\s?:?\s)(.*)$', r'\4') |
|
|
||||||
ReSub(r'^([uU]pdat(ed?)?)(\s?:?\s)(.*)$', r'\4') |
|
|
||||||
ReSub(r'#(\d{3,4})', r'Issue #\1') |
|
|
||||||
SetIfEmpty("No commit message.") | ucfirst | final_dot)
|
|
||||||
|
|
||||||
|
|
||||||
## ``tag_filter_regexp`` is a regexp
|
|
||||||
##
|
|
||||||
## Tags that will be used for the changelog must match this regexp.
|
|
||||||
##
|
|
||||||
tag_filter_regexp = r'^v[0]+\.[2-9]+\.[0-9]+\.[0-9]+$'
|
|
||||||
|
|
||||||
|
|
||||||
## ``unreleased_version_label`` is a string or a callable that outputs a string
|
|
||||||
##
|
|
||||||
## This label will be used as the changelog Title of the last set of changes
|
|
||||||
## between last valid tag and HEAD if any.
|
|
||||||
unreleased_version_label = "(unreleased)"
|
|
||||||
|
|
||||||
|
|
||||||
## ``output_engine`` is a callable
|
|
||||||
##
|
|
||||||
## This will change the output format of the generated changelog file
|
|
||||||
##
|
|
||||||
## Available choices are:
|
|
||||||
##
|
|
||||||
## - rest_py
|
|
||||||
##
|
|
||||||
## Legacy pure python engine, outputs ReSTructured text.
|
|
||||||
## This is the default.
|
|
||||||
##
|
|
||||||
## - mustache(<template_name>)
|
|
||||||
##
|
|
||||||
## Template name could be any of the available templates in
|
|
||||||
## ``templates/mustache/*.tpl``.
|
|
||||||
## Requires python package ``pystache``.
|
|
||||||
## Examples:
|
|
||||||
## - mustache("markdown")
|
|
||||||
## - mustache("restructuredtext")
|
|
||||||
##
|
|
||||||
## - makotemplate(<template_name>)
|
|
||||||
##
|
|
||||||
## Template name could be any of the available templates in
|
|
||||||
## ``templates/mako/*.tpl``.
|
|
||||||
## Requires python package ``mako``.
|
|
||||||
## Examples:
|
|
||||||
## - makotemplate("restructuredtext")
|
|
||||||
##
|
|
||||||
#output_engine = rest_py
|
|
||||||
#output_engine = mustache("restructuredtext")
|
|
||||||
output_engine = mustache("changelog_release.tpl")
|
|
||||||
#output_engine = makotemplate("restructuredtext")
|
|
||||||
|
|
||||||
|
|
||||||
## ``include_merge`` is a boolean
|
|
||||||
##
|
|
||||||
## This option tells git-log whether to include merge commits in the log.
|
|
||||||
## The default is to include them.
|
|
||||||
include_merge = False
|
|
||||||
|
|
||||||
|
|
||||||
## ``log_encoding`` is a string identifier
|
|
||||||
##
|
|
||||||
## This option tells gitchangelog what encoding is outputed by ``git log``.
|
|
||||||
## The default is to be clever about it: it checks ``git config`` for
|
|
||||||
## ``i18n.logOutputEncoding``, and if not found will default to git's own
|
|
||||||
## default: ``utf-8``.
|
|
||||||
#log_encoding = 'utf-8'
|
|
||||||
|
|
||||||
|
|
||||||
## ``publish`` is a callable
|
|
||||||
##
|
|
||||||
## Sets what ``gitchangelog`` should do with the output generated by
|
|
||||||
## the output engine. ``publish`` is a callable taking one argument
|
|
||||||
## that is an interator on lines from the output engine.
|
|
||||||
##
|
|
||||||
## Some helper callable are provided:
|
|
||||||
##
|
|
||||||
## Available choices are:
|
|
||||||
##
|
|
||||||
## - stdout
|
|
||||||
##
|
|
||||||
## Outputs directly to standard output
|
|
||||||
## (This is the default)
|
|
||||||
##
|
|
||||||
## - FileInsertAtFirstRegexMatch(file, pattern, idx=lamda m: m.start())
|
|
||||||
##
|
|
||||||
## Creates a callable that will parse given file for the given
|
|
||||||
## regex pattern and will insert the output in the file.
|
|
||||||
## ``idx`` is a callable that receive the matching object and
|
|
||||||
## must return a integer index point where to insert the
|
|
||||||
## the output in the file. Default is to return the position of
|
|
||||||
## the start of the matched string.
|
|
||||||
##
|
|
||||||
## - FileRegexSubst(file, pattern, replace, flags)
|
|
||||||
##
|
|
||||||
## Apply a replace inplace in the given file. Your regex pattern must
|
|
||||||
## take care of everything and might be more complex. Check the README
|
|
||||||
## for a complete copy-pastable example.
|
|
||||||
##
|
|
||||||
# publish = FileInsertIntoFirstRegexMatch(
|
|
||||||
# "CHANGELOG.rst",
|
|
||||||
# r'/(?P<rev>[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n/',
|
|
||||||
# idx=lambda m: m.start(1)
|
|
||||||
# )
|
|
||||||
publish = stdout
|
|
||||||
|
|
||||||
#def write_to_file(content):
|
|
||||||
# with open("CHANGELOG.md", "w+") as f:
|
|
||||||
# for chunk in content:
|
|
||||||
# f.write(chunk)
|
|
||||||
|
|
||||||
#publish = write_to_file
|
|
||||||
|
|
||||||
|
|
||||||
## ``revs`` is a list of callable or a list of string
|
|
||||||
##
|
|
||||||
## callable will be called to resolve as strings and allow dynamical
|
|
||||||
## computation of these. The result will be used as revisions for
|
|
||||||
## gitchangelog (as if directly stated on the command line). This allows
|
|
||||||
## to filter exaclty which commits will be read by gitchangelog.
|
|
||||||
##
|
|
||||||
## To get a full documentation on the format of these strings, please
|
|
||||||
## refer to the ``git rev-list`` arguments. There are many examples.
|
|
||||||
##
|
|
||||||
## Using callables is especially useful, for instance, if you
|
|
||||||
## are using gitchangelog to generate incrementally your changelog.
|
|
||||||
##
|
|
||||||
## Some helpers are provided, you can use them::
|
|
||||||
##
|
|
||||||
## - FileFirstRegexMatch(file, pattern): will return a callable that will
|
|
||||||
## return the first string match for the given pattern in the given file.
|
|
||||||
## If you use named sub-patterns in your regex pattern, it'll output only
|
|
||||||
## the string matching the regex pattern named "rev".
|
|
||||||
##
|
|
||||||
## - Caret(rev): will return the rev prefixed by a "^", which is a
|
|
||||||
## way to remove the given revision and all its ancestor.
|
|
||||||
##
|
|
||||||
## Please note that if you provide a rev-list on the command line, it'll
|
|
||||||
## replace this value (which will then be ignored).
|
|
||||||
##
|
|
||||||
## If empty, then ``gitchangelog`` will act as it had to generate a full
|
|
||||||
## changelog.
|
|
||||||
##
|
|
||||||
## The default is to use all commits to make the changelog.
|
|
||||||
#revs = ["^1.0.3", ]
|
|
||||||
#revs = [
|
|
||||||
# Caret(
|
|
||||||
# FileFirstRegexMatch(
|
|
||||||
# "CHANGELOG.rst",
|
|
||||||
# r"(?P<rev>[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n")),
|
|
||||||
# "HEAD"
|
|
||||||
#]
|
|
||||||
# Gets the latest annoted tag and uses that as a base for new changes.
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
proc = subprocess.Popen(["git", "describe", "--abbrev=0", "--tags"], stdout=subprocess.PIPE)
|
|
||||||
out = str(proc.communicate()[0].strip(), "utf-8")
|
|
||||||
revs = [out+"..."]
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
**Description:**
|
|
||||||
|
|
||||||
<!-- Check first that your problem is not listed in our wiki section:
|
|
||||||
* https://github.com/Radarr/Radarr/wiki/Common-Problems
|
|
||||||
* https://github.com/Radarr/Radarr/wiki/FAQ
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
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. -->
|
|
||||||
|
|
||||||
**Radarr Version:**
|
|
||||||
|
|
||||||
**Mono Version:**
|
|
||||||
|
|
||||||
**Debug Logs:**
|
|
||||||
|
|
||||||
# Do not remove anything from your logs and post the full logs! If not everything fits in here use a service like https://pastebin.com to upload them.
|
|
||||||
|
|
||||||
<!-- Please use the search bar (make sure to include closed issues as well) and make sure you are not submitting an already submitted issue. -->
|
|
||||||
@@ -1,34 +1,37 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug Report
|
||||||
about: Support requests will be closed immediately, if you are unsure go to our Discord
|
about: Support Requests will be closed immediately, if you are not 100% certain this is a bug please go to our Reddit or Discord first. Exceptions do not mean you found a bug!
|
||||||
or Subreddit first. Exceptions do not mean you found a bug!
|
|
||||||
title: ''
|
title: ''
|
||||||
labels: bug
|
labels: 'Type: Bug'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
<!-- Support Requests will be closed immediately, if you are unsure go to our Reddit or Discord first. Exceptions do not mean you found a bug! -->
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
<!-- Steps to reproduce the behavior:
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error -->
|
||||||
|
|
||||||
**Expected behavior**
|
**Expected behavior**
|
||||||
A clear and concise description of what you expected to happen.
|
<!-- A clear and concise description of what you expected to happen.-->
|
||||||
|
|
||||||
**Screenshots**
|
**Screenshots**
|
||||||
If applicable, add screenshots to help explain your problem.
|
<!-- If applicable, add screenshots to help explain your problem.-->
|
||||||
|
|
||||||
**Platform Information (please complete the following information):**
|
**Platform Information (please complete the following information):**
|
||||||
- OS: [e.g. Windows]
|
- OS: <!-- [e.g. Windows 10 2004 / Ubuntu 20.04] -->
|
||||||
- Mono Version: [e.g. Mono 5.8] (Only needed under Linux and Mac, found under System -> Status)
|
- Docker: <!-- [Yes/No] -->
|
||||||
- Browser and Version [e.g. chrome, safari] (Only needed for UI issues)
|
- Mono or .NET Version (System -> Status): <!--[e.g. Mono 5.8 or .Net Core 3.1.10 or .NET 5.0.1] -->
|
||||||
- Radarr Version [e.g. 3.0.0.2956]
|
- Browser and Version (Only needed for UI issues): <!--[e.g. chrome 86.0.4240.198] -->
|
||||||
|
- Radarr Version: <!--[e.g. 3.0.1.4259, 3.0.2.4369]-->
|
||||||
|
- Radarr Branch: <!--[e.g. master, develop]-->
|
||||||
|
|
||||||
**Debug Logs**
|
**Trace Logs**
|
||||||
Turn on debug logs under Settings -> General and wait for the bug to occur again. **Upload the full log file here (or another site and link it). Issues will be closed, if they do not include this!**
|
Turn on Trace logs under Settings -> General and wait for the bug to occur again.
|
||||||
|
**Upload the full log file here (or another site (e.g. pastebin) and link it). Issues will be closed, if they do not include this!**
|
||||||
|
<!-- Trace logs are named Radarr.trace.txt or Radarr.trace.#.txt and will contain "trace" in them-->
|
||||||
|
|||||||
@@ -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://radarr.video/discord
|
||||||
about: Chat with users and devs on support and setup related topics.
|
about: Chat with users and devs on support and setup related topics.
|
||||||
- name: Support via Reddit
|
- name: Support via Reddit
|
||||||
url: https://reddit.com/r/radarr
|
url: https://reddit.com/r/radarr
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
A clear and concise description of what you want to happen.
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
**Describe alternatives you've considered**
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ YES | NO
|
|||||||
|
|
||||||
#### Description
|
#### Description
|
||||||
|
|
||||||
|
#### Screenshot (if UI related)
|
||||||
|
|
||||||
#### Todos
|
#### Todos
|
||||||
- [ ] Tests
|
- [ ] Tests
|
||||||
- [ ] Translation Keys
|
- [ ] Translation Keys
|
||||||
|
- [ ] Wiki Updates
|
||||||
|
|
||||||
#### Issues Fixed or Closed by this PR
|
#### Issues Fixed or Closed by this PR
|
||||||
|
|
||||||
* #
|
* Fixes #XXXX
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
todo:
|
|
||||||
keyword: "TODO"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
+1
-3
@@ -5,12 +5,10 @@ daysUntilClose: 7
|
|||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- feature request
|
- feature request
|
||||||
- parser
|
- 'Status: Confirmed'
|
||||||
- confirmed
|
|
||||||
- sonarr-pull
|
- sonarr-pull
|
||||||
- lidarr-pull
|
- lidarr-pull
|
||||||
- readarr-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,13 +0,0 @@
|
|||||||
# Configuration for support-requests - https://github.com/dessant/support-requests
|
|
||||||
|
|
||||||
# Label used to mark issues as support requests
|
|
||||||
supportLabel: support
|
|
||||||
# Comment to post on issues marked as support requests. Add a link
|
|
||||||
# to a support page, or set to `false` to disable
|
|
||||||
supportComment: >
|
|
||||||
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)
|
|
||||||
# Whether to close issues marked as support requests
|
|
||||||
close: true
|
|
||||||
# Whether to lock issues marked as support requests
|
|
||||||
lock: false
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: 'Lock threads'
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lock:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v2
|
||||||
|
with:
|
||||||
|
github-token: ${{ github.token }}
|
||||||
|
issue-lock-inactive-days: '90'
|
||||||
|
issue-exclude-created-before: ''
|
||||||
|
issue-exclude-labels: ''
|
||||||
|
issue-lock-labels: ''
|
||||||
|
issue-lock-comment: ''
|
||||||
|
issue-lock-reason: 'resolved'
|
||||||
|
process-only: ''
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: 'Support requests'
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [labeled, unlabeled, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
support:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/support-requests@v2
|
||||||
|
with:
|
||||||
|
github-token: ${{ github.token }}
|
||||||
|
support-label: 'Type: Support'
|
||||||
|
issue-comment: >
|
||||||
|
:wave: @{issue-author}, we use the issue tracker exclusively
|
||||||
|
for bug reports and feature requests. However, this issue appears
|
||||||
|
to be a support request. Please hop over onto our [Discord](https://radarr.video/discord)
|
||||||
|
or [Subreddit](https://reddit.com/r/radarr)
|
||||||
|
close-issue: true
|
||||||
|
lock-issue: false
|
||||||
-1096
File diff suppressed because it is too large
Load Diff
+19
-3
@@ -3,11 +3,27 @@
|
|||||||
We're always looking for people to help make Radarr even better, there are a number of ways to contribute.
|
We're always looking for people to help make Radarr even better, there are a number of ways to contribute.
|
||||||
|
|
||||||
## Documentation ##
|
## Documentation ##
|
||||||
Setup guides, FAQ, the more information we have on the wiki the better.
|
Setup guides, FAQ, the more information we have on the [wiki](https://wiki.servarr.com/Radarr) the better.
|
||||||
|
|
||||||
## Development ##
|
## Development ##
|
||||||
|
|
||||||
See the readme for information on setting up your development environment.
|
### Tools required ###
|
||||||
|
- Visual Studio 2019 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/).
|
||||||
|
- HTML/Javascript editor of choice (VS Code/Sublime Text/Webstorm/Atom/etc)
|
||||||
|
- [Git](https://git-scm.com/downloads)
|
||||||
|
- [NodeJS](https://nodejs.org/en/download/) (Node 12.X.X or higher)
|
||||||
|
- [Yarn](https://yarnpkg.com/)
|
||||||
|
- .NET Core 5.0.
|
||||||
|
|
||||||
|
### Getting started ###
|
||||||
|
|
||||||
|
1. Fork Radarr
|
||||||
|
2. Clone the repository into your development machine. [*info*](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github)
|
||||||
|
3. Install the required Node Packages `yarn install`
|
||||||
|
4. Start gulp to monitor your dev environment for any changes that need post processing using `yarn start` command.
|
||||||
|
5. Build the project in Visual Studio, Setting startup project to `Radarr.Console` and framework to `net5.0`
|
||||||
|
6. Debug the project in Visual Studio
|
||||||
|
7. Open http://localhost:7878
|
||||||
|
|
||||||
### Contributing Code ###
|
### Contributing Code ###
|
||||||
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Radarr/Radarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
|
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Radarr/Radarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
|
||||||
@@ -18,7 +34,7 @@ See the readme for information on setting up your development environment.
|
|||||||
- 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
|
||||||
- Use 4 spaces instead of tabs, this is the default for VS 2012 and WebStorm (to my knowledge)
|
- Use 4 spaces instead of tabs, this is the default for VS 2019 and WebStorm (to my knowledge)
|
||||||
|
|
||||||
### Pull Requesting ###
|
### Pull Requesting ###
|
||||||
- Only make pull requests to develop, never master, if you make a PR to master we'll comment on it and close it
|
- Only make pull requests to develop, never master, if you make a PR to master we'll comment on it and close it
|
||||||
|
|||||||
@@ -2,65 +2,14 @@
|
|||||||
|
|
||||||
[](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
|
[](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
|
||||||
[](https://translate.servarr.com/engage/radarr/?utm_source=widget)
|
[](https://translate.servarr.com/engage/radarr/?utm_source=widget)
|
||||||
[](https://github.com/Radarr/Radarr/wiki/Docker)
|
[](https://wiki.servarr.com/Radarr_Installation#Docker)
|
||||||

|

|
||||||
[](#backers) [](#sponsors)
|
[](#backers)
|
||||||
|
[](#sponsors)
|
||||||
|
|
||||||
Radarr is an __independent__ fork of [Sonarr](https://github.com/Sonarr/Sonarr) reworked for automatically downloading movies via Usenet and BitTorrent.
|
Radarr is a movie collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new movies and will interface with clients and indexers to grab, sort, and rename them. It can also be configured to automatically upgrade the quality of existing files in the library when a better quality format becomes available.
|
||||||
|
|
||||||
The project was inspired by other Usenet/BitTorrent movie downloaders such as CouchPotato.
|
## Major Features Include:
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
[](https://github.com/Radarr/Radarr/wiki/Installation)
|
|
||||||
[](https://github.com/Radarr/Radarr/wiki/Docker)
|
|
||||||
[](https://github.com/Radarr/Radarr/wiki/Setup-Guide)
|
|
||||||
[](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)
|
|
||||||
* *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
|
|
||||||
* See the [Setup Guide](https://github.com/Radarr/Radarr/wiki/Setup-Guide) for further configuration
|
|
||||||
|
|
||||||
## 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 Channel Type | Branch: develop (stable) (v0.2) | Branch: nightly (semi-unstable) (v3.0) |
|
|
||||||
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
||||||
| 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
|
|
||||||
|
|
||||||
[](https://discord.gg/r5wJPt9)
|
|
||||||
[](https://www.reddit.com/r/radarr)
|
|
||||||
[](https://github.com/Radarr/Radarr/issues)
|
|
||||||
[](https://github.com/Radarr/Radarr/wiki)
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
[](https://github.com/Radarr/Radarr/issues)
|
|
||||||
[](https://github.com/Radarr/Radarr/pulls)
|
|
||||||
[](http://www.gnu.org/licenses/gpl.html)
|
|
||||||
[](https://github.com/Radarr/Radarr)
|
|
||||||
[](https://github.com/Radarr/Radarr/releases/)
|
|
||||||
[](https://hub.docker.com/r/linuxserver/radarr/)
|
|
||||||
[](/CHANGELOG.md#unreleased)
|
|
||||||
|
|
||||||
### [Site and API Status](https://status.radarr.video)
|
|
||||||
|
|
||||||
Radarr is currently undergoing rapid development and pull requests are actively added into the repository.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Current Features
|
|
||||||
|
|
||||||
* Adding new movies with lots of information, such as trailers, ratings, etc.
|
* Adding new movies with lots of information, such as trailers, ratings, etc.
|
||||||
* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc.
|
* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc.
|
||||||
@@ -72,78 +21,57 @@ Radarr is currently undergoing rapid development and pull requests are actively
|
|||||||
* Automatically importing downloaded movies
|
* Automatically importing downloaded movies
|
||||||
* Recognizing Special Editions, Director's Cut, etc.
|
* Recognizing Special Editions, Director's Cut, etc.
|
||||||
* Identifying releases with hardcoded subs
|
* Identifying releases with hardcoded subs
|
||||||
* All indexers supported by Sonarr also supported
|
* QBittorrent, Deluge, rTorrent, Transmission, uTorrent, and other download clients are supported
|
||||||
* New PassThePopcorn Indexer
|
|
||||||
* QBittorrent, Deluge, rTorrent, Transmission and uTorrent download client (Other clients are coming)
|
|
||||||
* New TorrentPotato Indexer
|
|
||||||
* Torznab Indexer now supports Movies (Works well with [Jackett](https://github.com/Jackett/Jackett))
|
|
||||||
* 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))
|
|
||||||
* Full integration with Kodi, Plex (notification, library update)
|
* Full integration with Kodi, Plex (notification, library update)
|
||||||
* And a new beautiful UI (v3)
|
* 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=)
|
## Support
|
||||||
|
Note: GitHub Issues are for Bugs and Feature Requests Only
|
||||||
|
|
||||||
## Configuring the Development Environment
|
[](https://radarr.video/discord)
|
||||||
|
[](https://www.reddit.com/r/Radarr)
|
||||||
|
[](https://github.com/Radarr/Radarr/issues)
|
||||||
|
[](https://wiki.servarr.com/Radarr)
|
||||||
|
|
||||||
### Requirements
|
## Feature Requests
|
||||||
|
|
||||||
* [Visual Studio Community 2019](https://www.visualstudio.com/vs/community/) or [Rider](http://www.jetbrains.com/rider/)
|
[Feature Requests](https://github.com/Radarr/Radarr/issues/new?assignees=&labels=Type%3A+Enhancement&template=feature_request.md&title=)
|
||||||
* [Git](https://git-scm.com/downloads)
|
|
||||||
* [Node.js](https://nodejs.org/en/download/)
|
|
||||||
* [Yarn](https://yarnpkg.com/)
|
|
||||||
|
|
||||||
### Setup
|
## Contributors & Developers
|
||||||
|
[API Documentation](https://radarr.video/docs/api/)
|
||||||
|
|
||||||
* Make sure all the required software mentioned above are installed
|
This project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).
|
||||||
* Clone the repository into your development machine ([*info*](https://help.github.com/desktop/guides/contributing/working-with-your-remote-repository-on-github-or-github-enterprise))
|
<a href="https://github.com/Radarr/Radarr/graphs/contributors"><img src="https://opencollective.com/Radarr/contributors.svg?width=890&button=false" /></a>
|
||||||
* Install the required Node Packages `yarn install`
|
|
||||||
* Start gulp to monitor your dev environment for any changes that need post processing using `yarn start` command.
|
|
||||||
|
|
||||||
> **Notice**
|
|
||||||
> Gulp must be running at all times while you are working with Radarr client source files.
|
|
||||||
|
|
||||||
### Build
|
## Backers
|
||||||
|
|
||||||
* To build run `sh build.sh`
|
Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/Radarr#backer)
|
||||||
|
|
||||||
**Note:** Windows users must have bash available to do this. If you installed git, you should have a git bash utility that works.
|
<img src="https://opencollective.com/Radarr/backers.svg?width=890"></a>
|
||||||
|
|
||||||
### Development
|
## Sponsors
|
||||||
|
|
||||||
* Open `Radarr.sln` in Visual Studio 2019 or run the build.sh script, if Mono is installed. Alternatively you can use Jetbrains Rider, since it works on all Platforms.
|
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor](https://opencollective.com/Radarr#sponsor)
|
||||||
* Make sure `NzbDrone.Console` is set as the startup project
|
|
||||||
* Run `build.sh` before running, or build in VS
|
|
||||||
|
|
||||||
## Supporters
|
<img src="https://opencollective.com/Radarr/sponsors.svg?width=890"></a>
|
||||||
|
|
||||||
This project would not be possible without the support by these amazing folks. [**Become a sponsor or backer**](https://opencollective.com/radarr) to help us out!
|
## Mega Sponsors
|
||||||
|
|
||||||
### Sponsors
|
<img src="https://opencollective.com/Radarr/tiers/mega-sponsor.svg?width=890"></a>
|
||||||
|
|
||||||
[](https://opencollective.com/radarr/order/3851)
|
## JetBrains
|
||||||
|
Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools.
|
||||||
### Flexible Sponsors
|
|
||||||
|
|
||||||
[](https://opencollective.com/radarr/order/3856)
|
|
||||||
|
|
||||||
### Backers
|
|
||||||
|
|
||||||
[](https://opencollective.com/radarr/order/3850)
|
|
||||||
|
|
||||||
### JetBrains
|
|
||||||
|
|
||||||
Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
|
|
||||||
|
|
||||||
* [<img src="/Logo/resharper.svg" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
|
* [<img src="/Logo/resharper.svg" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
|
||||||
* [<img src="/Logo/webstorm.svg" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
|
* [<img src="/Logo/webstorm.svg" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
|
||||||
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
|
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
|
||||||
* [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
|
* [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
|
||||||
|
|
||||||
## License
|
### License
|
||||||
|
|
||||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||||
* Copyright 2010-2020
|
* Copyright 2010-2021
|
||||||
|
|||||||
+146
-46
@@ -7,13 +7,13 @@ variables:
|
|||||||
outputFolder: './_output'
|
outputFolder: './_output'
|
||||||
artifactsFolder: './_artifacts'
|
artifactsFolder: './_artifacts'
|
||||||
testsFolder: './_tests'
|
testsFolder: './_tests'
|
||||||
majorVersion: '3.0.0'
|
majorVersion: '3.2.2'
|
||||||
minorVersion: $[counter('minorVersion', 2000)]
|
minorVersion: $[counter('minorVersion', 2000)]
|
||||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||||
sentryOrg: 'servarr'
|
sentryOrg: 'servarr'
|
||||||
sentryUrl: 'https://sentry.servarr.com'
|
sentryUrl: 'https://sentry.servarr.com'
|
||||||
dotnetVersion: '3.1.401'
|
dotnetVersion: '5.0.202'
|
||||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
@@ -23,7 +23,12 @@ trigger:
|
|||||||
- master
|
- master
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
- develop
|
branches:
|
||||||
|
include:
|
||||||
|
- develop
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- src/NzbDrone.Core/Localization/Core
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- stage: Setup
|
- stage: Setup
|
||||||
@@ -59,18 +64,21 @@ stages:
|
|||||||
Linux:
|
Linux:
|
||||||
osName: 'Linux'
|
osName: 'Linux'
|
||||||
imageName: 'ubuntu-18.04'
|
imageName: 'ubuntu-18.04'
|
||||||
|
enableAnalysis: 'true'
|
||||||
Mac:
|
Mac:
|
||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
imageName: 'macos-10.14'
|
imageName: 'macos-10.14'
|
||||||
|
enableAnalysis: 'false'
|
||||||
Windows:
|
Windows:
|
||||||
osName: 'Windows'
|
osName: 'Windows'
|
||||||
imageName: 'windows-2019'
|
imageName: 'windows-2019'
|
||||||
|
enableAnalysis: 'false'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
variables:
|
variables:
|
||||||
# Disable stylecop here - linting errors get caught by the analyze task
|
# Disable stylecop here - linting errors get caught by the analyze task
|
||||||
EnableAnalyzers: 'false'
|
EnableAnalyzers: $(enableAnalysis)
|
||||||
steps:
|
steps:
|
||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
@@ -79,7 +87,18 @@ stages:
|
|||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core'
|
||||||
inputs:
|
inputs:
|
||||||
version: $(dotnetVersion)
|
version: $(dotnetVersion)
|
||||||
- bash: ./build.sh --backend
|
- bash: |
|
||||||
|
BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
|
||||||
|
echo $BUNDLEDVERSIONS
|
||||||
|
grep osx-x64 $BUNDLEDVERSIONS
|
||||||
|
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
||||||
|
echo "BSD already enabled"
|
||||||
|
else
|
||||||
|
echo "Enabling BSD support"
|
||||||
|
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64/' $BUNDLEDVERSIONS
|
||||||
|
fi
|
||||||
|
displayName: Enable FreeBSD Support
|
||||||
|
- bash: ./build.sh --backend --enable-bsd
|
||||||
displayName: Build Radarr Backend
|
displayName: Build Radarr Backend
|
||||||
- bash: |
|
- bash: |
|
||||||
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
||||||
@@ -92,23 +111,27 @@ stages:
|
|||||||
artifact: '$(osName)Backend'
|
artifact: '$(osName)Backend'
|
||||||
displayName: Publish Backend
|
displayName: Publish Backend
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
- publish: '$(testsFolder)/netcoreapp3.1/win-x64/publish'
|
- publish: '$(testsFolder)/net5.0/win-x64/publish'
|
||||||
artifact: WindowsCoreTests
|
artifact: WindowsCoreTests
|
||||||
displayName: Publish Windows Test Package
|
displayName: Publish Windows Test Package
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
- publish: '$(testsFolder)/net462/linux-x64/publish'
|
- publish: '$(testsFolder)/net472/linux-x64/publish'
|
||||||
artifact: LinuxTests
|
artifact: LinuxTests
|
||||||
displayName: Publish Linux Mono Test Package
|
displayName: Publish Linux Mono Test Package
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
- publish: '$(testsFolder)/netcoreapp3.1/linux-x64/publish'
|
- publish: '$(testsFolder)/net5.0/linux-x64/publish'
|
||||||
artifact: LinuxCoreTests
|
artifact: LinuxCoreTests
|
||||||
displayName: Publish Linux Test Package
|
displayName: Publish Linux Test Package
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
- publish: '$(testsFolder)/netcoreapp3.1/linux-musl-x64/publish'
|
- publish: '$(testsFolder)/net5.0/linux-musl-x64/publish'
|
||||||
artifact: LinuxMuslCoreTests
|
artifact: LinuxMuslCoreTests
|
||||||
displayName: Publish Linux Musl Test Package
|
displayName: Publish Linux Musl Test Package
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
- publish: '$(testsFolder)/netcoreapp3.1/osx-x64/publish'
|
- publish: '$(testsFolder)/net5.0/freebsd-x64/publish'
|
||||||
|
artifact: FreebsdCoreTests
|
||||||
|
displayName: Publish FreeBSD Test Package
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
- publish: '$(testsFolder)/net5.0/osx-x64/publish'
|
||||||
artifact: MacCoreTests
|
artifact: MacCoreTests
|
||||||
displayName: Publish MacOS Test Package
|
displayName: Publish MacOS Test Package
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
@@ -135,7 +158,7 @@ stages:
|
|||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
displayName: Set Node.js version
|
displayName: Set Node.js version
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '12.x'
|
||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
@@ -184,8 +207,12 @@ stages:
|
|||||||
- bash: ./build.sh --packages
|
- bash: ./build.sh --packages
|
||||||
displayName: Create Packages
|
displayName: Create Packages
|
||||||
- bash: |
|
- bash: |
|
||||||
setup/inno/ISCC.exe setup/radarr.iss //DFramework=netcoreapp3.1
|
setup/inno/ISCC.exe setup/radarr.iss //DFramework=net5.0 //DRuntime=win-x86
|
||||||
cp setup/output/Radarr.*windows.netcoreapp3.1.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
|
cp setup/output/Radarr.*windows.net5.0.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x86-installer.exe
|
||||||
|
displayName: Create .NET Core Windows installer
|
||||||
|
- bash: |
|
||||||
|
setup/inno/ISCC.exe setup/radarr.iss //DFramework=net5.0 //DRuntime=win-x64
|
||||||
|
cp setup/output/Radarr.*windows.net5.0.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||||
displayName: Create .NET Core Windows installer
|
displayName: Create .NET Core Windows installer
|
||||||
- publish: $(Build.ArtifactStagingDirectory)
|
- publish: $(Build.ArtifactStagingDirectory)
|
||||||
artifact: 'WindowsInstaller'
|
artifact: 'WindowsInstaller'
|
||||||
@@ -215,7 +242,7 @@ stages:
|
|||||||
artifactName: WindowsFrontend
|
artifactName: WindowsFrontend
|
||||||
targetPath: _output
|
targetPath: _output
|
||||||
displayName: Fetch Frontend
|
displayName: Fetch Frontend
|
||||||
- bash: ./build.sh --packages
|
- bash: ./build.sh --packages --enable-bsd
|
||||||
displayName: Create Packages
|
displayName: Create Packages
|
||||||
- bash: |
|
- bash: |
|
||||||
find . -name "Radarr" -exec chmod a+x {} \;
|
find . -name "Radarr" -exec chmod a+x {} \;
|
||||||
@@ -227,14 +254,21 @@ stages:
|
|||||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).windows-core-x64.zip'
|
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).windows-core-x64.zip'
|
||||||
archiveType: 'zip'
|
archiveType: 'zip'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/windows/netcoreapp3.1
|
rootFolderOrFile: $(artifactsFolder)/win-x64/net5.0
|
||||||
|
- task: ArchiveFiles@2
|
||||||
|
displayName: Create Windows x86 Core zip
|
||||||
|
inputs:
|
||||||
|
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).windows-core-x86.zip'
|
||||||
|
archiveType: 'zip'
|
||||||
|
includeRootFolder: false
|
||||||
|
rootFolderOrFile: $(artifactsFolder)/win-x86/net5.0
|
||||||
- task: ArchiveFiles@2
|
- task: ArchiveFiles@2
|
||||||
displayName: Create MacOS Core app
|
displayName: Create MacOS Core app
|
||||||
inputs:
|
inputs:
|
||||||
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-app-core-x64.zip'
|
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-app-core-x64.zip'
|
||||||
archiveType: 'zip'
|
archiveType: 'zip'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/macos-app/netcoreapp3.1
|
rootFolderOrFile: $(artifactsFolder)/macos-app/net5.0
|
||||||
- task: ArchiveFiles@2
|
- task: ArchiveFiles@2
|
||||||
displayName: Create MacOS Core tar
|
displayName: Create MacOS Core tar
|
||||||
inputs:
|
inputs:
|
||||||
@@ -242,7 +276,7 @@ stages:
|
|||||||
archiveType: 'tar'
|
archiveType: 'tar'
|
||||||
tarCompression: 'gz'
|
tarCompression: 'gz'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/macos/netcoreapp3.1
|
rootFolderOrFile: $(artifactsFolder)/macos/net5.0
|
||||||
- task: ArchiveFiles@2
|
- task: ArchiveFiles@2
|
||||||
displayName: Create Linux Mono tar
|
displayName: Create Linux Mono tar
|
||||||
inputs:
|
inputs:
|
||||||
@@ -250,7 +284,7 @@ stages:
|
|||||||
archiveType: 'tar'
|
archiveType: 'tar'
|
||||||
tarCompression: 'gz'
|
tarCompression: 'gz'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/linux-x64/net462
|
rootFolderOrFile: $(artifactsFolder)/linux-x64/net472
|
||||||
- task: ArchiveFiles@2
|
- task: ArchiveFiles@2
|
||||||
displayName: Create Linux Core tar
|
displayName: Create Linux Core tar
|
||||||
inputs:
|
inputs:
|
||||||
@@ -258,7 +292,7 @@ stages:
|
|||||||
archiveType: 'tar'
|
archiveType: 'tar'
|
||||||
tarCompression: 'gz'
|
tarCompression: 'gz'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/linux-x64/netcoreapp3.1
|
rootFolderOrFile: $(artifactsFolder)/linux-x64/net5.0
|
||||||
- task: ArchiveFiles@2
|
- task: ArchiveFiles@2
|
||||||
displayName: Create Linux Musl Core tar
|
displayName: Create Linux Musl Core tar
|
||||||
inputs:
|
inputs:
|
||||||
@@ -266,7 +300,7 @@ stages:
|
|||||||
archiveType: 'tar'
|
archiveType: 'tar'
|
||||||
tarCompression: 'gz'
|
tarCompression: 'gz'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/netcoreapp3.1
|
rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net5.0
|
||||||
- task: ArchiveFiles@2
|
- task: ArchiveFiles@2
|
||||||
displayName: Create ARM32 Linux Core tar
|
displayName: Create ARM32 Linux Core tar
|
||||||
inputs:
|
inputs:
|
||||||
@@ -274,7 +308,7 @@ stages:
|
|||||||
archiveType: 'tar'
|
archiveType: 'tar'
|
||||||
tarCompression: 'gz'
|
tarCompression: 'gz'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/linux-arm/netcoreapp3.1
|
rootFolderOrFile: $(artifactsFolder)/linux-arm/net5.0
|
||||||
- task: ArchiveFiles@2
|
- task: ArchiveFiles@2
|
||||||
displayName: Create ARM64 Linux Core tar
|
displayName: Create ARM64 Linux Core tar
|
||||||
inputs:
|
inputs:
|
||||||
@@ -282,7 +316,7 @@ stages:
|
|||||||
archiveType: 'tar'
|
archiveType: 'tar'
|
||||||
tarCompression: 'gz'
|
tarCompression: 'gz'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/linux-arm64/netcoreapp3.1
|
rootFolderOrFile: $(artifactsFolder)/linux-arm64/net5.0
|
||||||
- task: ArchiveFiles@2
|
- task: ArchiveFiles@2
|
||||||
displayName: Create ARM64 Linux Musl Core tar
|
displayName: Create ARM64 Linux Musl Core tar
|
||||||
inputs:
|
inputs:
|
||||||
@@ -290,7 +324,15 @@ stages:
|
|||||||
archiveType: 'tar'
|
archiveType: 'tar'
|
||||||
tarCompression: 'gz'
|
tarCompression: 'gz'
|
||||||
includeRootFolder: false
|
includeRootFolder: false
|
||||||
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/netcoreapp3.1
|
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net5.0
|
||||||
|
- task: ArchiveFiles@2
|
||||||
|
displayName: Create FreeBSD Core Core tar
|
||||||
|
inputs:
|
||||||
|
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).freebsd-core-x64.tar.gz'
|
||||||
|
archiveType: 'tar'
|
||||||
|
tarCompression: 'gz'
|
||||||
|
includeRootFolder: false
|
||||||
|
rootFolderOrFile: $(artifactsFolder)/freebsd-x64/net5.0
|
||||||
- publish: $(Build.ArtifactStagingDirectory)
|
- publish: $(Build.ArtifactStagingDirectory)
|
||||||
artifact: 'Packages'
|
artifact: 'Packages'
|
||||||
displayName: Publish Packages
|
displayName: Publish Packages
|
||||||
@@ -344,24 +386,34 @@ stages:
|
|||||||
displayName: Unit Native
|
displayName: Unit Native
|
||||||
dependsOn: Prepare
|
dependsOn: Prepare
|
||||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
|
workspace:
|
||||||
|
clean: all
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
MacCore:
|
MacCore:
|
||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
testName: 'MacCore'
|
testName: 'MacCore'
|
||||||
|
poolName: 'Azure Pipelines'
|
||||||
imageName: 'macos-10.14'
|
imageName: 'macos-10.14'
|
||||||
WindowsCore:
|
WindowsCore:
|
||||||
osName: 'Windows'
|
osName: 'Windows'
|
||||||
testName: 'WindowsCore'
|
testName: 'WindowsCore'
|
||||||
|
poolName: 'Azure Pipelines'
|
||||||
imageName: 'windows-2019'
|
imageName: 'windows-2019'
|
||||||
LinuxCore:
|
LinuxCore:
|
||||||
osName: 'Linux'
|
osName: 'Linux'
|
||||||
testName: 'LinuxCore'
|
testName: 'LinuxCore'
|
||||||
|
poolName: 'Azure Pipelines'
|
||||||
imageName: 'ubuntu-18.04'
|
imageName: 'ubuntu-18.04'
|
||||||
pattern: 'Radarr.**.linux-core-x64.tar.gz'
|
FreebsdCore:
|
||||||
|
osName: 'Linux'
|
||||||
|
testName: 'FreebsdCore'
|
||||||
|
poolName: 'FreeBSD'
|
||||||
|
imageName:
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
|
name: $(poolName)
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -370,6 +422,7 @@ stages:
|
|||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core'
|
||||||
inputs:
|
inputs:
|
||||||
version: $(dotnetVersion)
|
version: $(dotnetVersion)
|
||||||
|
condition: ne(variables['poolName'], 'FreeBSD')
|
||||||
- task: DownloadPipelineArtifact@2
|
- task: DownloadPipelineArtifact@2
|
||||||
displayName: Download Test Artifact
|
displayName: Download Test Artifact
|
||||||
inputs:
|
inputs:
|
||||||
@@ -382,7 +435,7 @@ stages:
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y --allow-unauthenticated libmediainfo-dev libmediainfo0v5 mediainfo
|
sudo apt-get install -y --allow-unauthenticated libmediainfo-dev libmediainfo0v5 mediainfo
|
||||||
displayName: Install mediainfo
|
displayName: Install mediainfo
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
|
condition: and(succeeded(), eq(variables['testName'], 'LinuxCore'))
|
||||||
- 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'))
|
||||||
@@ -420,19 +473,19 @@ stages:
|
|||||||
mono520:
|
mono520:
|
||||||
testName: 'Mono 5.20'
|
testName: 'Mono 5.20'
|
||||||
artifactName: LinuxTests
|
artifactName: LinuxTests
|
||||||
containerImage: servarr/testimages:mono-5.20
|
containerImage: ghcr.io/servarr/testimages:mono-5.20
|
||||||
mono610:
|
mono610:
|
||||||
testName: 'Mono 6.10'
|
testName: 'Mono 6.10'
|
||||||
artifactName: LinuxTests
|
artifactName: LinuxTests
|
||||||
containerImage: servarr/testimages:mono-6.10
|
containerImage: ghcr.io/servarr/testimages:mono-6.10
|
||||||
mono612:
|
mono612:
|
||||||
testName: 'Mono 6.12'
|
testName: 'Mono 6.12'
|
||||||
artifactName: LinuxTests
|
artifactName: LinuxTests
|
||||||
containerImage: servarr/testimages:mono-6.12
|
containerImage: ghcr.io/servarr/testimages:mono-6.12
|
||||||
alpine:
|
alpine:
|
||||||
testName: 'Musl Net Core'
|
testName: 'Musl Net Core'
|
||||||
artifactName: LinuxMuslCoreTests
|
artifactName: LinuxMuslCoreTests
|
||||||
containerImage: servarr/testimages:alpine
|
containerImage: ghcr.io/servarr/testimages:alpine
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-18.04'
|
vmImage: 'ubuntu-18.04'
|
||||||
@@ -497,17 +550,17 @@ stages:
|
|||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
testName: 'MacCore'
|
testName: 'MacCore'
|
||||||
imageName: 'macos-10.14'
|
imageName: 'macos-10.14'
|
||||||
pattern: 'Radarr.**.osx-core-x64.tar.gz'
|
pattern: 'Radarr.*.osx-core-x64.tar.gz'
|
||||||
WindowsCore:
|
WindowsCore:
|
||||||
osName: 'Windows'
|
osName: 'Windows'
|
||||||
testName: 'WindowsCore'
|
testName: 'WindowsCore'
|
||||||
imageName: 'windows-2019'
|
imageName: 'windows-2019'
|
||||||
pattern: 'Radarr.**.windows-core-x64.zip'
|
pattern: 'Radarr.*.windows-core-x64.zip'
|
||||||
LinuxCore:
|
LinuxCore:
|
||||||
osName: 'Linux'
|
osName: 'Linux'
|
||||||
testName: 'LinuxCore'
|
testName: 'LinuxCore'
|
||||||
imageName: 'ubuntu-18.04'
|
imageName: 'ubuntu-18.04'
|
||||||
pattern: 'Radarr.**.linux-core-x64.tar.gz'
|
pattern: 'Radarr.*.linux-core-x64.tar.gz'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
@@ -560,6 +613,52 @@ stages:
|
|||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
|
- job: Integration_FreeBSD
|
||||||
|
displayName: Integration Native FreeBSD
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
|
workspace:
|
||||||
|
clean: all
|
||||||
|
variables:
|
||||||
|
pattern: 'Radarr.*.freebsd-core-x64.tar.gz'
|
||||||
|
pool:
|
||||||
|
name: 'FreeBSD'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- checkout: none
|
||||||
|
- task: DownloadPipelineArtifact@2
|
||||||
|
displayName: Download Test Artifact
|
||||||
|
inputs:
|
||||||
|
buildType: 'current'
|
||||||
|
artifactName: 'FreebsdCoreTests'
|
||||||
|
targetPath: $(testsFolder)
|
||||||
|
- task: DownloadPipelineArtifact@2
|
||||||
|
displayName: Download Build Artifact
|
||||||
|
inputs:
|
||||||
|
buildType: 'current'
|
||||||
|
artifactName: Packages
|
||||||
|
itemPattern: '/$(pattern)'
|
||||||
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
|
- bash: |
|
||||||
|
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
|
||||||
|
tar xf ${BUILD_ARTIFACTSTAGINGDIRECTORY}/$(pattern) -C ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
|
||||||
|
displayName: Extract Package
|
||||||
|
- bash: |
|
||||||
|
mkdir -p ./bin/
|
||||||
|
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
||||||
|
displayName: Move Package Contents
|
||||||
|
- bash: |
|
||||||
|
chmod a+x ${TESTSFOLDER}/test.sh
|
||||||
|
${TESTSFOLDER}/test.sh Linux Integration Test
|
||||||
|
displayName: Run Integration Tests
|
||||||
|
- task: PublishTestResults@2
|
||||||
|
inputs:
|
||||||
|
testResultsFormat: 'NUnit'
|
||||||
|
testResultsFiles: '**/TestResult.xml'
|
||||||
|
testRunTitle: 'FreeBSD Integration Tests'
|
||||||
|
failTaskOnFailedTests: true
|
||||||
|
displayName: Publish Test Results
|
||||||
|
|
||||||
- job: Integration_Docker
|
- job: Integration_Docker
|
||||||
displayName: Integration Docker
|
displayName: Integration Docker
|
||||||
dependsOn: Prepare
|
dependsOn: Prepare
|
||||||
@@ -569,23 +668,23 @@ stages:
|
|||||||
mono520:
|
mono520:
|
||||||
testName: 'Mono 5.20'
|
testName: 'Mono 5.20'
|
||||||
artifactName: LinuxTests
|
artifactName: LinuxTests
|
||||||
containerImage: servarr/testimages:mono-5.20
|
containerImage: ghcr.io/servarr/testimages:mono-5.20
|
||||||
pattern: 'Radarr.**.linux.tar.gz'
|
pattern: 'Radarr.*.linux.tar.gz'
|
||||||
mono610:
|
mono610:
|
||||||
testName: 'Mono 6.10'
|
testName: 'Mono 6.10'
|
||||||
artifactName: LinuxTests
|
artifactName: LinuxTests
|
||||||
containerImage: servarr/testimages:mono-6.10
|
containerImage: ghcr.io/servarr/testimages:mono-6.10
|
||||||
pattern: 'Radarr.**.linux.tar.gz'
|
pattern: 'Radarr.*.linux.tar.gz'
|
||||||
mono612:
|
mono612:
|
||||||
testName: 'Mono 6.12'
|
testName: 'Mono 6.12'
|
||||||
artifactName: LinuxTests
|
artifactName: LinuxTests
|
||||||
containerImage: servarr/testimages:mono-6.12
|
containerImage: ghcr.io/servarr/testimages:mono-6.12
|
||||||
pattern: 'Radarr.**.linux.tar.gz'
|
pattern: 'Radarr.*.linux.tar.gz'
|
||||||
alpine:
|
alpine:
|
||||||
testName: 'Musl Net Core'
|
testName: 'Musl Net Core'
|
||||||
artifactName: LinuxMuslCoreTests
|
artifactName: LinuxMuslCoreTests
|
||||||
containerImage: servarr/testimages:alpine
|
containerImage: ghcr.io/servarr/testimages:alpine
|
||||||
pattern: 'Radarr.**.linux-musl-core-x64.tar.gz'
|
pattern: 'Radarr.*.linux-musl-core-x64.tar.gz'
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-18.04'
|
vmImage: 'ubuntu-18.04'
|
||||||
|
|
||||||
@@ -644,17 +743,17 @@ stages:
|
|||||||
Linux:
|
Linux:
|
||||||
osName: 'Linux'
|
osName: 'Linux'
|
||||||
imageName: 'ubuntu-18.04'
|
imageName: 'ubuntu-18.04'
|
||||||
pattern: 'Radarr.**.linux-core-x64.tar.gz'
|
pattern: 'Radarr.*.linux-core-x64.tar.gz'
|
||||||
failBuild: true
|
failBuild: true
|
||||||
Mac:
|
Mac:
|
||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
imageName: 'macos-10.14'
|
imageName: 'macos-10.14'
|
||||||
pattern: 'Radarr.**.osx-core-x64.tar.gz'
|
pattern: 'Radarr.*.osx-core-x64.tar.gz'
|
||||||
failBuild: true
|
failBuild: true
|
||||||
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: true
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
@@ -746,7 +845,7 @@ stages:
|
|||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
displayName: Set Node.js version
|
displayName: Set Node.js version
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '12.x'
|
||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
@@ -792,6 +891,7 @@ stages:
|
|||||||
|
|
||||||
variables:
|
variables:
|
||||||
disable.coverage.autogenerate: 'true'
|
disable.coverage.autogenerate: 'true'
|
||||||
|
EnableAnalyzers: 'false'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: windows-2019
|
vmImage: windows-2019
|
||||||
@@ -820,8 +920,8 @@ stages:
|
|||||||
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
|
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
|
||||||
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
|
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
|
||||||
- bash: |
|
- bash: |
|
||||||
./build.sh --backend -f netcoreapp3.1 -r win-x64
|
./build.sh --backend -f net5.0 -r win-x64
|
||||||
TEST_DIR=_tests/netcoreapp3.1/win-x64/publish/ ./test.sh Windows Unit Coverage
|
TEST_DIR=_tests/net5.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||||
displayName: Coverage Unit Tests
|
displayName: Coverage Unit Tests
|
||||||
- task: SonarCloudAnalyze@1
|
- task: SonarCloudAnalyze@1
|
||||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#! /bin/bash
|
#! /usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
outputFolder='_output'
|
outputFolder='_output'
|
||||||
@@ -25,6 +25,18 @@ UpdateVersionNumber()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EnableBsdSupport()
|
||||||
|
{
|
||||||
|
#todo enable sdk with
|
||||||
|
#SDK_PATH=$(dotnet --list-sdks | grep -P '5\.\d\.\d+' | head -1 | sed 's/\(5\.[0-9]*\.[0-9]*\).*\[\(.*\)\]/\2\/\1/g')
|
||||||
|
# BUNDLED_VERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
||||||
|
|
||||||
|
if grep -qv freebsd-x64 src/Directory.Build.props; then
|
||||||
|
sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64</RuntimeIdentifiers>^g" src/Directory.Build.props
|
||||||
|
sed -i'' -e "s^<ExcludedRuntimeFrameworkPairs>\(.*\)</ExcludedRuntimeFrameworkPairs>^<ExcludedRuntimeFrameworkPairs>\1;freebsd-x64:net472</ExcludedRuntimeFrameworkPairs>^g" src/Directory.Build.props
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
LintUI()
|
LintUI()
|
||||||
{
|
{
|
||||||
ProgressStart 'ESLint'
|
ProgressStart 'ESLint'
|
||||||
@@ -75,11 +87,11 @@ YarnInstall()
|
|||||||
ProgressEnd 'yarn install'
|
ProgressEnd 'yarn install'
|
||||||
}
|
}
|
||||||
|
|
||||||
RunGulp()
|
RunWebpack()
|
||||||
{
|
{
|
||||||
ProgressStart 'Running gulp'
|
ProgressStart 'Running webpack'
|
||||||
yarn run build --production
|
yarn run build --env production
|
||||||
ProgressEnd 'Running gulp'
|
ProgressEnd 'Running webpack'
|
||||||
}
|
}
|
||||||
|
|
||||||
PackageFiles()
|
PackageFiles()
|
||||||
@@ -118,7 +130,7 @@ PackageLinux()
|
|||||||
|
|
||||||
echo "Adding Radarr.Mono to UpdatePackage"
|
echo "Adding Radarr.Mono to UpdatePackage"
|
||||||
cp $folder/Radarr.Mono.* $folder/Radarr.Update
|
cp $folder/Radarr.Mono.* $folder/Radarr.Update
|
||||||
if [ "$framework" = "netcoreapp3.1" ]; then
|
if [ "$framework" = "net5.0" ]; then
|
||||||
cp $folder/Mono.Posix.NETStandard.* $folder/Radarr.Update
|
cp $folder/Mono.Posix.NETStandard.* $folder/Radarr.Update
|
||||||
cp $folder/libMonoPosixHelper.* $folder/Radarr.Update
|
cp $folder/libMonoPosixHelper.* $folder/Radarr.Update
|
||||||
fi
|
fi
|
||||||
@@ -136,7 +148,7 @@ PackageMacOS()
|
|||||||
|
|
||||||
PackageFiles "$folder" "$framework" "osx-x64"
|
PackageFiles "$folder" "$framework" "osx-x64"
|
||||||
|
|
||||||
if [ "$framework" = "net462" ]; then
|
if [ "$framework" = "net472" ]; then
|
||||||
echo "Adding Startup script"
|
echo "Adding Startup script"
|
||||||
cp macOS/Radarr $folder
|
cp macOS/Radarr $folder
|
||||||
fi
|
fi
|
||||||
@@ -150,7 +162,7 @@ PackageMacOS()
|
|||||||
|
|
||||||
echo "Adding Radarr.Mono to UpdatePackage"
|
echo "Adding Radarr.Mono to UpdatePackage"
|
||||||
cp $folder/Radarr.Mono.* $folder/Radarr.Update
|
cp $folder/Radarr.Mono.* $folder/Radarr.Update
|
||||||
if [ "$framework" = "netcoreapp3.1" ]; then
|
if [ "$framework" = "net5.0" ]; then
|
||||||
cp $folder/Mono.Posix.NETStandard.* $folder/Radarr.Update
|
cp $folder/Mono.Posix.NETStandard.* $folder/Radarr.Update
|
||||||
cp $folder/libMonoPosixHelper.* $folder/Radarr.Update
|
cp $folder/libMonoPosixHelper.* $folder/Radarr.Update
|
||||||
fi
|
fi
|
||||||
@@ -183,12 +195,14 @@ PackageMacOSApp()
|
|||||||
PackageWindows()
|
PackageWindows()
|
||||||
{
|
{
|
||||||
local framework="$1"
|
local framework="$1"
|
||||||
|
local runtime="$2"
|
||||||
|
|
||||||
ProgressStart "Creating Windows Package for $framework"
|
ProgressStart "Creating Windows Package for $framework"
|
||||||
|
|
||||||
local folder=$artifactsFolder/windows/$framework/Radarr
|
local folder=$artifactsFolder/$runtime/$framework/Radarr
|
||||||
|
|
||||||
PackageFiles "$folder" "$framework" "win-x64"
|
PackageFiles "$folder" "$framework" "$runtime"
|
||||||
|
cp -r $outputFolder/$framework-windows/$runtime/publish/* $folder
|
||||||
|
|
||||||
echo "Removing Radarr.Mono"
|
echo "Removing Radarr.Mono"
|
||||||
rm -f $folder/Radarr.Mono.*
|
rm -f $folder/Radarr.Mono.*
|
||||||
@@ -210,11 +224,11 @@ Package()
|
|||||||
IFS='-' read -ra SPLIT <<< "$runtime"
|
IFS='-' read -ra SPLIT <<< "$runtime"
|
||||||
|
|
||||||
case "${SPLIT[0]}" in
|
case "${SPLIT[0]}" in
|
||||||
linux)
|
linux|freebsd*)
|
||||||
PackageLinux "$framework" "$runtime"
|
PackageLinux "$framework" "$runtime"
|
||||||
;;
|
;;
|
||||||
win)
|
win)
|
||||||
PackageWindows "$framework"
|
PackageWindows "$framework" "$runtime"
|
||||||
;;
|
;;
|
||||||
osx)
|
osx)
|
||||||
PackageMacOS "$framework"
|
PackageMacOS "$framework"
|
||||||
@@ -232,14 +246,6 @@ PackageTests()
|
|||||||
|
|
||||||
rm -f $testPackageFolder/$framework/$runtime/*.log.config
|
rm -f $testPackageFolder/$framework/$runtime/*.log.config
|
||||||
|
|
||||||
# geckodriver.exe isn't copied by dotnet publish
|
|
||||||
if [ "$runtime" = "win-x64" ];
|
|
||||||
then
|
|
||||||
curl -Lso gecko.zip "https://github.com/mozilla/geckodriver/releases/download/v0.27.0/geckodriver-v0.27.0-win64.zip"
|
|
||||||
unzip -o gecko.zip
|
|
||||||
cp geckodriver.exe "$testPackageFolder/$framework/win-x64/publish"
|
|
||||||
fi
|
|
||||||
|
|
||||||
ProgressEnd 'Creating Test Package'
|
ProgressEnd 'Creating Test Package'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +269,7 @@ if [ $# -eq 0 ]; then
|
|||||||
FRONTEND=YES
|
FRONTEND=YES
|
||||||
PACKAGES=YES
|
PACKAGES=YES
|
||||||
LINT=YES
|
LINT=YES
|
||||||
|
ENABLE_BSD=NO
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]
|
while [[ $# -gt 0 ]]
|
||||||
@@ -274,6 +281,10 @@ case $key in
|
|||||||
BACKEND=YES
|
BACKEND=YES
|
||||||
shift # past argument
|
shift # past argument
|
||||||
;;
|
;;
|
||||||
|
--enable-bsd)
|
||||||
|
ENABLE_BSD=YES
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
-r|--runtime)
|
-r|--runtime)
|
||||||
RID="$2"
|
RID="$2"
|
||||||
shift # past argument
|
shift # past argument
|
||||||
@@ -314,14 +325,23 @@ set -- "${POSITIONAL[@]}" # restore positional parameters
|
|||||||
if [ "$BACKEND" = "YES" ];
|
if [ "$BACKEND" = "YES" ];
|
||||||
then
|
then
|
||||||
UpdateVersionNumber
|
UpdateVersionNumber
|
||||||
|
if [ "$ENABLE_BSD" = "YES" ];
|
||||||
|
then
|
||||||
|
EnableBsdSupport
|
||||||
|
fi
|
||||||
Build
|
Build
|
||||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||||
then
|
then
|
||||||
PackageTests "netcoreapp3.1" "win-x64"
|
PackageTests "net5.0" "win-x64"
|
||||||
PackageTests "netcoreapp3.1" "linux-x64"
|
PackageTests "net5.0" "win-x86"
|
||||||
PackageTests "netcoreapp3.1" "linux-musl-x64"
|
PackageTests "net5.0" "linux-x64"
|
||||||
PackageTests "netcoreapp3.1" "osx-x64"
|
PackageTests "net5.0" "linux-musl-x64"
|
||||||
PackageTests "net462" "linux-x64"
|
PackageTests "net5.0" "osx-x64"
|
||||||
|
PackageTests "net472" "linux-x64"
|
||||||
|
if [ "$ENABLE_BSD" = "YES" ];
|
||||||
|
then
|
||||||
|
PackageTests "net5.0" "freebsd-x64"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
PackageTests "$FRAMEWORK" "$RID"
|
PackageTests "$FRAMEWORK" "$RID"
|
||||||
fi
|
fi
|
||||||
@@ -330,7 +350,7 @@ fi
|
|||||||
if [ "$FRONTEND" = "YES" ];
|
if [ "$FRONTEND" = "YES" ];
|
||||||
then
|
then
|
||||||
YarnInstall
|
YarnInstall
|
||||||
RunGulp
|
RunWebpack
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$LINT" = "YES" ];
|
if [ "$LINT" = "YES" ];
|
||||||
@@ -349,14 +369,19 @@ then
|
|||||||
|
|
||||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||||
then
|
then
|
||||||
Package "netcoreapp3.1" "win-x64"
|
Package "net5.0" "win-x64"
|
||||||
Package "netcoreapp3.1" "linux-x64"
|
Package "net5.0" "win-x86"
|
||||||
Package "netcoreapp3.1" "linux-musl-x64"
|
Package "net5.0" "linux-x64"
|
||||||
Package "netcoreapp3.1" "linux-arm64"
|
Package "net5.0" "linux-musl-x64"
|
||||||
Package "netcoreapp3.1" "linux-musl-arm64"
|
Package "net5.0" "linux-arm64"
|
||||||
Package "netcoreapp3.1" "linux-arm"
|
Package "net5.0" "linux-musl-arm64"
|
||||||
Package "netcoreapp3.1" "osx-x64"
|
Package "net5.0" "linux-arm"
|
||||||
Package "net462" "linux-x64"
|
Package "net5.0" "osx-x64"
|
||||||
|
Package "net472" "linux-x64"
|
||||||
|
if [ "$ENABLE_BSD" = "YES" ];
|
||||||
|
then
|
||||||
|
Package "net5.0" "freebsd-x64"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
Package "$FRAMEWORK" "$RID"
|
Package "$FRAMEWORK" "$RID"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
{{#versions}}
|
|
||||||
## {{{label}}}
|
|
||||||
|
|
||||||
{{#sections}}
|
|
||||||
### {{{label}}}
|
|
||||||
{{#commits}}
|
|
||||||
- {{{subject}}} [<a href="https://github.com/{{{author}}}">{{{author}}}</a>]
|
|
||||||
{{/commits}}
|
|
||||||
|
|
||||||
{{/sections}}
|
|
||||||
|
|
||||||
{{/versions}}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
**To receive further Pre-Release updates, please change the branch to develop. (Settings -> General (Show Advanced Settings) -> Updates -> Branch)**
|
|
||||||
|
|
||||||
{{#versions}}
|
|
||||||
|
|
||||||
{{#sections}}
|
|
||||||
{{{label}}}
|
|
||||||
{{#commits}}
|
|
||||||
- {{{subject}}} [{{{author}}}]
|
|
||||||
{{/commits}}
|
|
||||||
|
|
||||||
{{/sections}}
|
|
||||||
|
|
||||||
{{/versions}}
|
|
||||||
|
|
||||||
**Note**: The OSX version does not automatically launch the browser. You have to go to http://localhost:7878 by yourself in a browser of your choice.
|
|
||||||
@@ -6,8 +6,10 @@ const dirs = fs
|
|||||||
.map((dirent) => dirent.name)
|
.map((dirent) => dirent.name)
|
||||||
.join('|');
|
.join('|');
|
||||||
|
|
||||||
|
const frontendFolder = __dirname;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
parser: 'babel-eslint',
|
parser: '@babel/eslint-parser',
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
@@ -25,6 +27,9 @@ module.exports = {
|
|||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 6,
|
ecmaVersion: 6,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
|
babelOptions: {
|
||||||
|
configFile: `${frontendFolder}/babel.config.js`
|
||||||
|
},
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
modules: true,
|
modules: true,
|
||||||
impliedStrict: true
|
impliedStrict: true
|
||||||
@@ -271,7 +276,7 @@ module.exports = {
|
|||||||
|
|
||||||
// ImportSort
|
// ImportSort
|
||||||
|
|
||||||
'simple-import-sort/sort': 'error',
|
'simple-import-sort/imports': 'error',
|
||||||
'import/newline-after-import': 'error',
|
'import/newline-after-import': 'error',
|
||||||
|
|
||||||
// React
|
// React
|
||||||
@@ -309,7 +314,7 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
files: ['*.js'],
|
files: ['*.js'],
|
||||||
rules: {
|
rules: {
|
||||||
'simple-import-sort/sort': [
|
'simple-import-sort/imports': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
groups: [
|
groups: [
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = (env) => {
|
||||||
|
const uiFolder = 'UI';
|
||||||
|
const frontendFolder = path.join(__dirname, '..');
|
||||||
|
const srcFolder = path.join(frontendFolder, 'src');
|
||||||
|
const isProduction = !!env.production;
|
||||||
|
const isProfiling = isProduction && !!env.profile;
|
||||||
|
const inlineWebWorkers = 'no-fallback';
|
||||||
|
|
||||||
|
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
|
||||||
|
|
||||||
|
console.log('Source Folder:', srcFolder);
|
||||||
|
console.log('Output Folder:', distFolder);
|
||||||
|
console.log('isProduction:', isProduction);
|
||||||
|
console.log('isProfiling:', isProfiling);
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
mode: isProduction ? 'production' : 'development',
|
||||||
|
devtool: 'source-map',
|
||||||
|
|
||||||
|
stats: {
|
||||||
|
children: false
|
||||||
|
},
|
||||||
|
|
||||||
|
watchOptions: {
|
||||||
|
ignored: /node_modules/
|
||||||
|
},
|
||||||
|
|
||||||
|
entry: {
|
||||||
|
index: 'index.js'
|
||||||
|
},
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
modules: [
|
||||||
|
srcFolder,
|
||||||
|
path.join(srcFolder, 'Shims'),
|
||||||
|
'node_modules'
|
||||||
|
],
|
||||||
|
alias: {
|
||||||
|
jquery: 'jquery/src/jquery'
|
||||||
|
},
|
||||||
|
fallback: {
|
||||||
|
buffer: false,
|
||||||
|
http: false,
|
||||||
|
https: false,
|
||||||
|
url: false,
|
||||||
|
util: false,
|
||||||
|
net: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
output: {
|
||||||
|
path: distFolder,
|
||||||
|
publicPath: '/',
|
||||||
|
filename: '[name].js',
|
||||||
|
sourceMapFilename: '[file].map'
|
||||||
|
},
|
||||||
|
|
||||||
|
optimization: {
|
||||||
|
moduleIds: 'deterministic',
|
||||||
|
chunkIds: 'named',
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'initial',
|
||||||
|
name: 'vendors'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
performance: {
|
||||||
|
hints: false
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
__DEV__: !isProduction,
|
||||||
|
'process.env.NODE_ENV': isProduction ? JSON.stringify('production') : JSON.stringify('development')
|
||||||
|
}),
|
||||||
|
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: 'Content/styles.css'
|
||||||
|
}),
|
||||||
|
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: 'frontend/src/index.html',
|
||||||
|
filename: 'index.html',
|
||||||
|
publicPath: '/'
|
||||||
|
}),
|
||||||
|
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
// HTML
|
||||||
|
{
|
||||||
|
from: 'frontend/src/*.html',
|
||||||
|
to: path.join(distFolder, '[name][ext]'),
|
||||||
|
globOptions: {
|
||||||
|
ignore: ['**/index.html']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Fonts
|
||||||
|
{
|
||||||
|
from: 'frontend/src/Content/Fonts/*.*',
|
||||||
|
to: path.join(distFolder, 'Content/Fonts', '[name][ext]')
|
||||||
|
},
|
||||||
|
|
||||||
|
// Icon Images
|
||||||
|
{
|
||||||
|
from: 'frontend/src/Content/Images/Icons/*.*',
|
||||||
|
to: path.join(distFolder, 'Content/Images/Icons', '[name][ext]')
|
||||||
|
},
|
||||||
|
|
||||||
|
// Images
|
||||||
|
{
|
||||||
|
from: 'frontend/src/Content/Images/*.*',
|
||||||
|
to: path.join(distFolder, 'Content/Images', '[name][ext]')
|
||||||
|
},
|
||||||
|
|
||||||
|
// Robots
|
||||||
|
{
|
||||||
|
from: 'frontend/src/Content/robots.txt',
|
||||||
|
to: path.join(distFolder, 'Content', '[name][ext]')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
|
||||||
|
new LiveReloadPlugin()
|
||||||
|
],
|
||||||
|
|
||||||
|
resolveLoader: {
|
||||||
|
modules: [
|
||||||
|
'node_modules',
|
||||||
|
'frontend/build/webpack/'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.worker\.js$/,
|
||||||
|
use: {
|
||||||
|
loader: 'worker-loader',
|
||||||
|
options: {
|
||||||
|
filename: '[name].js',
|
||||||
|
inline: inlineWebWorkers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.js?$/,
|
||||||
|
exclude: /(node_modules|JsLibraries)/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
configFile: `${frontendFolder}/babel.config.js`,
|
||||||
|
envName: isProduction ? 'production' : 'development',
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
modules: false,
|
||||||
|
loose: true,
|
||||||
|
debug: false,
|
||||||
|
useBuiltIns: 'entry',
|
||||||
|
corejs: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// CSS Modules
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
exclude: /(node_modules|globals.css)/,
|
||||||
|
use: [
|
||||||
|
{ loader: MiniCssExtractPlugin.loader },
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
importLoaders: 1,
|
||||||
|
modules: {
|
||||||
|
localIdentName: '[name]/[local]/[hash:base64:5]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
config: 'frontend/postcss.config.js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Global styles
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
include: /(node_modules|globals.css)/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Fonts
|
||||||
|
{
|
||||||
|
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'url-loader',
|
||||||
|
options: {
|
||||||
|
limit: 10240,
|
||||||
|
mimetype: 'application/font-woff',
|
||||||
|
emitFile: false,
|
||||||
|
name: 'Content/Fonts/[name].[ext]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'file-loader',
|
||||||
|
options: {
|
||||||
|
emitFile: false,
|
||||||
|
name: 'Content/Fonts/[name].[ext]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isProfiling) {
|
||||||
|
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
||||||
|
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
||||||
|
|
||||||
|
config.optimization.minimizer = [
|
||||||
|
new TerserPlugin({
|
||||||
|
cache: true,
|
||||||
|
parallel: true,
|
||||||
|
sourceMap: true, // Must be set to true if using source-maps in production
|
||||||
|
terserOptions: {
|
||||||
|
mangle: false,
|
||||||
|
keep_classnames: true,
|
||||||
|
keep_fnames: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
const gulp = require('gulp');
|
|
||||||
|
|
||||||
require('./clean');
|
|
||||||
require('./copy');
|
|
||||||
require('./webpack');
|
|
||||||
|
|
||||||
gulp.task('build',
|
|
||||||
gulp.series('clean',
|
|
||||||
gulp.parallel(
|
|
||||||
'webpack',
|
|
||||||
'copyHtml',
|
|
||||||
'copyFonts',
|
|
||||||
'copyImages'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
const gulp = require('gulp');
|
|
||||||
const del = require('del');
|
|
||||||
|
|
||||||
const paths = require('./helpers/paths');
|
|
||||||
|
|
||||||
gulp.task('clean', () => {
|
|
||||||
return del([paths.dest.root]);
|
|
||||||
});
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const gulp = require('gulp');
|
|
||||||
const print = require('gulp-print').default;
|
|
||||||
const cache = require('gulp-cached');
|
|
||||||
const livereload = require('gulp-livereload');
|
|
||||||
const paths = require('./helpers/paths.js');
|
|
||||||
|
|
||||||
gulp.task('copyHtml', () => {
|
|
||||||
return gulp.src(paths.src.html, { base: paths.src.root })
|
|
||||||
.pipe(cache('copyHtml'))
|
|
||||||
.pipe(print())
|
|
||||||
.pipe(gulp.dest(paths.dest.root))
|
|
||||||
.pipe(livereload());
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('copyFonts', () => {
|
|
||||||
return gulp.src(
|
|
||||||
path.join(paths.src.fonts, '**', '*.*'), { base: paths.src.root }
|
|
||||||
)
|
|
||||||
.pipe(cache('copyFonts'))
|
|
||||||
.pipe(print())
|
|
||||||
.pipe(gulp.dest(paths.dest.root))
|
|
||||||
.pipe(livereload());
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('copyImages', () => {
|
|
||||||
return gulp.src(
|
|
||||||
path.join(paths.src.images, '**', '*.*'), { base: paths.src.root }
|
|
||||||
)
|
|
||||||
.pipe(cache('copyImages'))
|
|
||||||
.pipe(print())
|
|
||||||
.pipe(gulp.dest(paths.dest.root))
|
|
||||||
.pipe(livereload());
|
|
||||||
});
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
require('./build.js');
|
|
||||||
require('./clean.js');
|
|
||||||
require('./copy.js');
|
|
||||||
require('./watch.js');
|
|
||||||
require('./webpack.js');
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
const colors = require('ansi-colors');
|
|
||||||
|
|
||||||
module.exports = function errorHandler(error) {
|
|
||||||
console.log(colors.red(`Error (${error.plugin}): ${error.message}`));
|
|
||||||
this.emit('end');
|
|
||||||
};
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
const root = './frontend/src';
|
|
||||||
|
|
||||||
const paths = {
|
|
||||||
src: {
|
|
||||||
root,
|
|
||||||
html: `${root}/*.html`,
|
|
||||||
scripts: `${root}/**/*.js`,
|
|
||||||
content: `${root}/Content/`,
|
|
||||||
fonts: `${root}/Content/Fonts/`,
|
|
||||||
images: `${root}/Content/Images/`,
|
|
||||||
exclude: {
|
|
||||||
libs: `!${root}/JsLibraries/**`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dest: {
|
|
||||||
root: './_output/UI/',
|
|
||||||
content: './_output/UI/Content/',
|
|
||||||
fonts: './_output/UI/Content/Fonts/',
|
|
||||||
images: './_output/UI/Content/Images/'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = paths;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
const gulp = require('gulp');
|
|
||||||
const livereload = require('gulp-livereload');
|
|
||||||
const gulpWatch = require('gulp-watch');
|
|
||||||
const paths = require('./helpers/paths.js');
|
|
||||||
|
|
||||||
require('./copy.js');
|
|
||||||
require('./webpack.js');
|
|
||||||
|
|
||||||
function watch() {
|
|
||||||
livereload.listen({ start: true });
|
|
||||||
|
|
||||||
gulp.task('webpackWatch')();
|
|
||||||
gulpWatch(paths.src.html, gulp.series('copyHtml'));
|
|
||||||
gulpWatch(`${paths.src.fonts}**/*.*`, gulp.series('copyFonts'));
|
|
||||||
gulpWatch(`${paths.src.images}**/*.*`, gulp.series('copyImages'));
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.task('watch', gulp.series('build', watch));
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
const gulp = require('gulp');
|
|
||||||
const webpackStream = require('webpack-stream');
|
|
||||||
const livereload = require('gulp-livereload');
|
|
||||||
const path = require('path');
|
|
||||||
const webpack = require('webpack');
|
|
||||||
const errorHandler = require('./helpers/errorHandler');
|
|
||||||
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
||||||
const HtmlWebpackPluginHtmlTags = require('html-webpack-plugin/lib/html-tags');
|
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
|
||||||
|
|
||||||
const uiFolder = 'UI';
|
|
||||||
const frontendFolder = path.join(__dirname, '..');
|
|
||||||
const srcFolder = path.join(frontendFolder, 'src');
|
|
||||||
const isProduction = process.argv.indexOf('--production') > -1;
|
|
||||||
const isProfiling = isProduction && process.argv.indexOf('--profile') > -1;
|
|
||||||
const inlineWebWorkers = 'no-fallback';
|
|
||||||
|
|
||||||
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
|
|
||||||
|
|
||||||
console.log('Source Folder:', srcFolder);
|
|
||||||
console.log('Output Folder:', distFolder);
|
|
||||||
console.log('isProduction:', isProduction);
|
|
||||||
console.log('isProfiling:', isProfiling);
|
|
||||||
|
|
||||||
const cssVarsFiles = [
|
|
||||||
'../src/Styles/Variables/colors',
|
|
||||||
'../src/Styles/Variables/dimensions',
|
|
||||||
'../src/Styles/Variables/fonts',
|
|
||||||
'../src/Styles/Variables/animations',
|
|
||||||
'../src/Styles/Variables/zIndexes'
|
|
||||||
].map(require.resolve);
|
|
||||||
|
|
||||||
// Override the way HtmlWebpackPlugin injects the scripts
|
|
||||||
// TODO: Find a better way to get these paths without
|
|
||||||
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets, assetTags) {
|
|
||||||
const head = assetTags.headTags.map((v) => {
|
|
||||||
const href = v.attributes.href
|
|
||||||
.replace('\\', '/')
|
|
||||||
.replace('%5C', '/');
|
|
||||||
|
|
||||||
v.attributes = { rel: 'stylesheet', type: 'text/css', href: `/${href}` };
|
|
||||||
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
|
|
||||||
});
|
|
||||||
const body = assetTags.bodyTags.map((v) => {
|
|
||||||
v.attributes = { src: `/${v.attributes.src}` };
|
|
||||||
return HtmlWebpackPluginHtmlTags.htmlTagObjectToString(v, this.options.xhtml);
|
|
||||||
});
|
|
||||||
|
|
||||||
return html
|
|
||||||
.replace('<!-- webpack bundles head -->', head.join('\r\n '))
|
|
||||||
.replace('<!-- webpack bundles body -->', body.join('\r\n '));
|
|
||||||
};
|
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
new OptimizeCssAssetsPlugin({}),
|
|
||||||
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
__DEV__: !isProduction,
|
|
||||||
'process.env.NODE_ENV': isProduction ? JSON.stringify('production') : JSON.stringify('development')
|
|
||||||
}),
|
|
||||||
|
|
||||||
new MiniCssExtractPlugin({
|
|
||||||
filename: path.join('Content', 'styles.css')
|
|
||||||
}),
|
|
||||||
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: 'frontend/src/index.html',
|
|
||||||
filename: 'index.html'
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
mode: isProduction ? 'production' : 'development',
|
|
||||||
devtool: '#source-map',
|
|
||||||
|
|
||||||
stats: {
|
|
||||||
children: false
|
|
||||||
},
|
|
||||||
|
|
||||||
watchOptions: {
|
|
||||||
ignored: /node_modules/
|
|
||||||
},
|
|
||||||
|
|
||||||
entry: {
|
|
||||||
index: 'index.js'
|
|
||||||
},
|
|
||||||
|
|
||||||
resolve: {
|
|
||||||
modules: [
|
|
||||||
srcFolder,
|
|
||||||
path.join(srcFolder, 'Shims'),
|
|
||||||
'node_modules'
|
|
||||||
],
|
|
||||||
alias: {
|
|
||||||
jquery: 'jquery/src/jquery'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
output: {
|
|
||||||
path: distFolder,
|
|
||||||
filename: '[name].js',
|
|
||||||
sourceMapFilename: '[file].map'
|
|
||||||
},
|
|
||||||
|
|
||||||
optimization: {
|
|
||||||
chunkIds: 'named',
|
|
||||||
splitChunks: {
|
|
||||||
chunks: 'initial'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
performance: {
|
|
||||||
hints: false
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins,
|
|
||||||
|
|
||||||
resolveLoader: {
|
|
||||||
modules: [
|
|
||||||
'node_modules',
|
|
||||||
'frontend/gulp/webpack/'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.worker\.js$/,
|
|
||||||
use: {
|
|
||||||
loader: 'worker-loader',
|
|
||||||
options: {
|
|
||||||
filename: '[name].js',
|
|
||||||
inline: inlineWebWorkers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.js?$/,
|
|
||||||
exclude: /(node_modules|JsLibraries)/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'babel-loader',
|
|
||||||
options: {
|
|
||||||
configFile: `${frontendFolder}/babel.config.js`,
|
|
||||||
envName: isProduction ? 'production' : 'development',
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
'@babel/preset-env',
|
|
||||||
{
|
|
||||||
modules: false,
|
|
||||||
loose: true,
|
|
||||||
debug: false,
|
|
||||||
useBuiltIns: 'entry',
|
|
||||||
corejs: 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
// CSS Modules
|
|
||||||
{
|
|
||||||
test: /\.css$/,
|
|
||||||
exclude: /(node_modules|globals.css)/,
|
|
||||||
use: [
|
|
||||||
{ loader: MiniCssExtractPlugin.loader },
|
|
||||||
{
|
|
||||||
loader: 'css-loader',
|
|
||||||
options: {
|
|
||||||
importLoaders: 1,
|
|
||||||
modules: {
|
|
||||||
localIdentName: '[name]/[local]/[hash:base64:5]'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: 'postcss-loader',
|
|
||||||
options: {
|
|
||||||
ident: 'postcss',
|
|
||||||
config: {
|
|
||||||
ctx: {
|
|
||||||
cssVarsFiles
|
|
||||||
},
|
|
||||||
path: 'frontend/postcss.config.js'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
// Global styles
|
|
||||||
{
|
|
||||||
test: /\.css$/,
|
|
||||||
include: /(node_modules|globals.css)/,
|
|
||||||
use: [
|
|
||||||
'style-loader',
|
|
||||||
{
|
|
||||||
loader: 'css-loader'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
// Fonts
|
|
||||||
{
|
|
||||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'url-loader',
|
|
||||||
options: {
|
|
||||||
limit: 10240,
|
|
||||||
mimetype: 'application/font-woff',
|
|
||||||
emitFile: false,
|
|
||||||
name: 'Content/Fonts/[name].[ext]'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'file-loader',
|
|
||||||
options: {
|
|
||||||
emitFile: false,
|
|
||||||
name: 'Content/Fonts/[name].[ext]'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isProfiling) {
|
|
||||||
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
|
|
||||||
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
|
|
||||||
|
|
||||||
config.optimization.minimizer = [
|
|
||||||
new TerserPlugin({
|
|
||||||
cache: true,
|
|
||||||
parallel: true,
|
|
||||||
sourceMap: true, // Must be set to true if using source-maps in production
|
|
||||||
terserOptions: {
|
|
||||||
mangle: false,
|
|
||||||
keep_classnames: true,
|
|
||||||
keep_fnames: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.task('webpack', () => {
|
|
||||||
return webpackStream(config)
|
|
||||||
.pipe(gulp.dest('_output/UI'));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('webpackWatch', () => {
|
|
||||||
config.watch = true;
|
|
||||||
|
|
||||||
return webpackStream(config, webpack)
|
|
||||||
.on('error', errorHandler)
|
|
||||||
.pipe(gulp.dest('_output/UI'))
|
|
||||||
.on('error', errorHandler)
|
|
||||||
.pipe(livereload())
|
|
||||||
.on('error', errorHandler);
|
|
||||||
});
|
|
||||||
+29
-20
@@ -1,23 +1,32 @@
|
|||||||
const reload = require('require-nocache')(module);
|
const reload = require('require-nocache')(module);
|
||||||
|
|
||||||
module.exports = (ctx, configPath, options) => {
|
const cssVarsFiles = [
|
||||||
const config = {
|
'./src/Styles/Variables/colors',
|
||||||
plugins: {
|
'./src/Styles/Variables/dimensions',
|
||||||
'postcss-mixins': {
|
'./src/Styles/Variables/fonts',
|
||||||
mixinsDir: [
|
'./src/Styles/Variables/animations',
|
||||||
'frontend/src/Styles/Mixins'
|
'./src/Styles/Variables/zIndexes'
|
||||||
]
|
].map(require.resolve);
|
||||||
},
|
|
||||||
'postcss-simple-vars': {
|
|
||||||
variables: () =>
|
|
||||||
ctx.options.cssVarsFiles.reduce((acc, vars) => {
|
|
||||||
return Object.assign(acc, reload(vars));
|
|
||||||
}, {})
|
|
||||||
},
|
|
||||||
'postcss-color-function': {},
|
|
||||||
'postcss-nested': {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return config;
|
const mixinsFiles = [
|
||||||
};
|
'frontend/src/Styles/Mixins/cover.css',
|
||||||
|
'frontend/src/Styles/Mixins/linkOverlay.css',
|
||||||
|
'frontend/src/Styles/Mixins/scroller.css',
|
||||||
|
'frontend/src/Styles/Mixins/truncate.css'
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
['postcss-mixins', {
|
||||||
|
mixinsFiles
|
||||||
|
}],
|
||||||
|
['postcss-simple-vars', {
|
||||||
|
variables: () =>
|
||||||
|
cssVarsFiles.reduce((acc, vars) => {
|
||||||
|
return Object.assign(acc, reload(vars));
|
||||||
|
}, {})
|
||||||
|
}],
|
||||||
|
'postcss-color-function',
|
||||||
|
'postcss-nested'
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -208,9 +208,9 @@ class Blacklist extends Component {
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={isConfirmRemoveModalOpen}
|
isOpen={isConfirmRemoveModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title="Remove Selected"
|
title={translate('RemoveSelected')}
|
||||||
message={'Are you sure you want to remove the selected items from the blacklist?'}
|
message={translate('AreYouSureYouWantToRemoveTheSelectedItemsFromBlacklist')}
|
||||||
confirmLabel="Remove Selected"
|
confirmLabel={translate('RemoveSelected')}
|
||||||
onConfirm={this.onRemoveSelectedConfirmed}
|
onConfirm={this.onRemoveSelectedConfirmed}
|
||||||
onCancel={this.onConfirmRemoveModalClose}
|
onCancel={this.onConfirmRemoveModalClose}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -173,13 +173,13 @@ function HistoryDetails(props) {
|
|||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case 'Manual':
|
case 'Manual':
|
||||||
reasonMessage = 'File was deleted by via UI';
|
reasonMessage = translate('FileWasDeletedByViaUI');
|
||||||
break;
|
break;
|
||||||
case 'MissingFromDisk':
|
case 'MissingFromDisk':
|
||||||
reasonMessage = 'Radarr was unable to find the file on disk so it was removed';
|
reasonMessage = translate('MissingFromDisk');
|
||||||
break;
|
break;
|
||||||
case 'Upgrade':
|
case 'Upgrade':
|
||||||
reasonMessage = 'File was deleted to import an upgrade';
|
reasonMessage = translate('FileWasDeletedByUpgrade');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
reasonMessage = '';
|
reasonMessage = '';
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class History extends Component {
|
|||||||
|
|
||||||
isPopulated && !hasError && !items.length &&
|
isPopulated && !hasError && !items.length &&
|
||||||
<div>
|
<div>
|
||||||
No history found
|
{translate('NoHistory')}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ class Queue extends Component {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
|
this._shouldBlockRefresh = false;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
allSelected: false,
|
allSelected: false,
|
||||||
allUnselected: false,
|
allUnselected: false,
|
||||||
@@ -43,6 +45,14 @@ class Queue extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
if (this._shouldBlockRefresh) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const {
|
const {
|
||||||
items,
|
items,
|
||||||
@@ -85,6 +95,10 @@ class Queue extends Component {
|
|||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
|
onQueueRowModalOpenOrClose = (isOpen) => {
|
||||||
|
this._shouldBlockRefresh = isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
onSelectAllChange = ({ value }) => {
|
onSelectAllChange = ({ value }) => {
|
||||||
this.setState(selectAll(this.state.selectedState, value));
|
this.setState(selectAll(this.state.selectedState, value));
|
||||||
}
|
}
|
||||||
@@ -100,15 +114,19 @@ class Queue extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRemoveSelectedPress = () => {
|
onRemoveSelectedPress = () => {
|
||||||
this.setState({ isConfirmRemoveModalOpen: true });
|
this.setState({ isConfirmRemoveModalOpen: true }, () => {
|
||||||
|
this._shouldBlockRefresh = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemoveSelectedConfirmed = (payload) => {
|
onRemoveSelectedConfirmed = (payload) => {
|
||||||
|
this._shouldBlockRefresh = false;
|
||||||
this.props.onRemoveSelectedPress({ ids: this.getSelectedIds(), ...payload });
|
this.props.onRemoveSelectedPress({ ids: this.getSelectedIds(), ...payload });
|
||||||
this.setState({ isConfirmRemoveModalOpen: false });
|
this.setState({ isConfirmRemoveModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
onConfirmRemoveModalClose = () => {
|
onConfirmRemoveModalClose = () => {
|
||||||
|
this._shouldBlockRefresh = false;
|
||||||
this.setState({ isConfirmRemoveModalOpen: false });
|
this.setState({ isConfirmRemoveModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,14 +221,14 @@ class Queue extends Component {
|
|||||||
{
|
{
|
||||||
!isRefreshing && hasError &&
|
!isRefreshing && hasError &&
|
||||||
<div>
|
<div>
|
||||||
Failed to load Queue
|
{translate('FailedToLoadQueue')}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !hasError && !items.length &&
|
isAllPopulated && !hasError && !items.length &&
|
||||||
<div>
|
<div>
|
||||||
Queue is empty
|
{translate('QueueIsEmpty')}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +255,7 @@ class Queue extends Component {
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
{...item}
|
{...item}
|
||||||
onSelectedChange={this.onSelectedChange}
|
onSelectedChange={this.onSelectedChange}
|
||||||
|
onQueueRowModalOpenOrClose={this.onQueueRowModalOpenOrClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class QueueConnector extends Component {
|
|||||||
const {
|
const {
|
||||||
useCurrentPage,
|
useCurrentPage,
|
||||||
fetchQueue,
|
fetchQueue,
|
||||||
|
fetchQueueStatus,
|
||||||
gotoQueueFirstPage
|
gotoQueueFirstPage
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -53,6 +54,8 @@ class QueueConnector extends Component {
|
|||||||
} else {
|
} else {
|
||||||
gotoQueueFirstPage();
|
gotoQueueFirstPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchQueueStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
@@ -152,6 +155,7 @@ QueueConnector.propTypes = {
|
|||||||
useCurrentPage: PropTypes.bool.isRequired,
|
useCurrentPage: PropTypes.bool.isRequired,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
fetchQueue: PropTypes.func.isRequired,
|
fetchQueue: PropTypes.func.isRequired,
|
||||||
|
fetchQueueStatus: PropTypes.func.isRequired,
|
||||||
gotoQueueFirstPage: PropTypes.func.isRequired,
|
gotoQueueFirstPage: PropTypes.func.isRequired,
|
||||||
gotoQueuePreviousPage: PropTypes.func.isRequired,
|
gotoQueuePreviousPage: PropTypes.func.isRequired,
|
||||||
gotoQueueNextPage: PropTypes.func.isRequired,
|
gotoQueueNextPage: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function QueueDetails(props) {
|
|||||||
<Icon
|
<Icon
|
||||||
name={icons.DOWNLOAD}
|
name={icons.DOWNLOAD}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
title={'Downloaded - Unable to Import: check logs for details'}
|
title={translate('UnableToImportCheckLogs')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,19 +43,32 @@ class QueueRow extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRemoveQueueItemModalConfirmed = (blacklist) => {
|
onRemoveQueueItemModalConfirmed = (blacklist) => {
|
||||||
this.props.onRemoveQueueItemPress(blacklist);
|
const {
|
||||||
|
onRemoveQueueItemPress,
|
||||||
|
onQueueRowModalOpenOrClose
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
onQueueRowModalOpenOrClose(false);
|
||||||
|
onRemoveQueueItemPress(blacklist);
|
||||||
|
|
||||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemoveQueueItemModalClose = () => {
|
onRemoveQueueItemModalClose = () => {
|
||||||
|
this.props.onQueueRowModalOpenOrClose(false);
|
||||||
|
|
||||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
onInteractiveImportPress = () => {
|
onInteractiveImportPress = () => {
|
||||||
|
this.props.onQueueRowModalOpenOrClose(true);
|
||||||
|
|
||||||
this.setState({ isInteractiveImportModalOpen: true });
|
this.setState({ isInteractiveImportModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onInteractiveImportModalClose = () => {
|
onInteractiveImportModalClose = () => {
|
||||||
|
this.props.onQueueRowModalOpenOrClose(false);
|
||||||
|
|
||||||
this.setState({ isInteractiveImportModalOpen: false });
|
this.setState({ isInteractiveImportModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +372,8 @@ QueueRow.propTypes = {
|
|||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onSelectedChange: PropTypes.func.isRequired,
|
onSelectedChange: PropTypes.func.isRequired,
|
||||||
onGrabPress: PropTypes.func.isRequired,
|
onGrabPress: PropTypes.func.isRequired,
|
||||||
onRemoveQueueItemPress: PropTypes.func.isRequired
|
onRemoveQueueItemPress: PropTypes.func.isRequired,
|
||||||
|
onQueueRowModalOpenOrClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
QueueRow.defaultProps = {
|
QueueRow.defaultProps = {
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ class RemoveQueueItemModal extends Component {
|
|||||||
onModalClose={this.onModalClose}
|
onModalClose={this.onModalClose}
|
||||||
>
|
>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Remove - {sourceTitle}
|
{translate('Remove')} - {sourceTitle}
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<div>
|
||||||
Are you sure you want to remove '{sourceTitle}' from the queue?
|
{translate('RemoveFromQueueText', [sourceTitle])}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue?
|
{translate('AreYouSureYouWantToRemoveSelectedItemsFromQueue', [selectedCount, selectedCount > 1 ? 's' : ''])}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ class AddNewMovie extends Component {
|
|||||||
const {
|
const {
|
||||||
error,
|
error,
|
||||||
items,
|
items,
|
||||||
hasExistingMovies
|
hasExistingMovies,
|
||||||
|
colorImpairedMode
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const term = this.state.term;
|
const term = this.state.term;
|
||||||
@@ -141,6 +142,7 @@ class AddNewMovie extends Component {
|
|||||||
return (
|
return (
|
||||||
<AddNewMovieSearchResultConnector
|
<AddNewMovieSearchResultConnector
|
||||||
key={item.tmdbId}
|
key={item.tmdbId}
|
||||||
|
colorImpairedMode={colorImpairedMode}
|
||||||
{...item}
|
{...item}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -159,7 +161,7 @@ class AddNewMovie extends Component {
|
|||||||
{translate('YouCanAlsoSearch')}
|
{translate('YouCanAlsoSearch')}
|
||||||
</div>
|
</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://wiki.servarr.com/Radarr_FAQ#Why_cant_I_add_a_new_movie_to_Radarr">
|
||||||
{translate('CantFindMovie')}
|
{translate('CantFindMovie')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -213,7 +215,8 @@ AddNewMovie.propTypes = {
|
|||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
hasExistingMovies: PropTypes.bool.isRequired,
|
hasExistingMovies: PropTypes.bool.isRequired,
|
||||||
onMovieLookupChange: PropTypes.func.isRequired,
|
onMovieLookupChange: PropTypes.func.isRequired,
|
||||||
onClearMovieLookup: PropTypes.func.isRequired
|
onClearMovieLookup: PropTypes.func.isRequired,
|
||||||
|
colorImpairedMode: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddNewMovie;
|
export default AddNewMovie;
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import React, { Component } from 'react';
|
|||||||
import { connect } from 'react-redux';
|
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 { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||||
import { fetchImportExclusions } from 'Store/Actions/Settings/importExclusions';
|
import { fetchImportExclusions } from 'Store/Actions/Settings/importExclusions';
|
||||||
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
import parseUrl from 'Utilities/String/parseUrl';
|
import parseUrl from 'Utilities/String/parseUrl';
|
||||||
import AddNewMovie from './AddNewMovie';
|
import AddNewMovie from './AddNewMovie';
|
||||||
|
|
||||||
@@ -13,13 +15,15 @@ function createMapStateToProps() {
|
|||||||
(state) => state.addMovie,
|
(state) => state.addMovie,
|
||||||
(state) => state.movies.items.length,
|
(state) => state.movies.items.length,
|
||||||
(state) => state.router.location,
|
(state) => state.router.location,
|
||||||
(addMovie, existingMoviesCount, location) => {
|
createUISettingsSelector(),
|
||||||
|
(addMovie, existingMoviesCount, location, uiSettings) => {
|
||||||
const { params } = parseUrl(location.search);
|
const { params } = parseUrl(location.search);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...addMovie,
|
...addMovie,
|
||||||
term: params.term,
|
term: params.term,
|
||||||
hasExistingMovies: existingMoviesCount > 0
|
hasExistingMovies: existingMoviesCount > 0,
|
||||||
|
colorImpairedMode: uiSettings.enableColorImpairedMode
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -29,7 +33,9 @@ const mapDispatchToProps = {
|
|||||||
lookupMovie,
|
lookupMovie,
|
||||||
clearAddMovie,
|
clearAddMovie,
|
||||||
fetchRootFolders,
|
fetchRootFolders,
|
||||||
fetchImportExclusions
|
fetchImportExclusions,
|
||||||
|
fetchQueueDetails,
|
||||||
|
clearQueueDetails
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddNewMovieConnector extends Component {
|
class AddNewMovieConnector extends Component {
|
||||||
@@ -46,6 +52,7 @@ class AddNewMovieConnector extends Component {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.fetchRootFolders();
|
this.props.fetchRootFolders();
|
||||||
this.props.fetchImportExclusions();
|
this.props.fetchImportExclusions();
|
||||||
|
this.props.fetchQueueDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@@ -54,6 +61,7 @@ class AddNewMovieConnector extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.props.clearAddMovie();
|
this.props.clearAddMovie();
|
||||||
|
this.props.clearQueueDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -102,7 +110,9 @@ 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
|
fetchImportExclusions: PropTypes.func.isRequired,
|
||||||
|
fetchQueueDetails: PropTypes.func.isRequired,
|
||||||
|
clearQueueDetails: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector);
|
||||||
|
|||||||
@@ -17,30 +17,15 @@ import styles from './AddNewMovieModalContent.css';
|
|||||||
|
|
||||||
class AddNewMovieModalContent extends Component {
|
class AddNewMovieModalContent extends Component {
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
searchForMovie: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSearchForMissingMovieChange = ({ value }) => {
|
|
||||||
this.setState({ searchForMovie: value });
|
|
||||||
}
|
|
||||||
|
|
||||||
onQualityProfileIdChange = ({ value }) => {
|
onQualityProfileIdChange = ({ value }) => {
|
||||||
this.props.onInputChange({ name: 'qualityProfileId', value: parseInt(value) });
|
this.props.onInputChange({ name: 'qualityProfileId', value: parseInt(value) });
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddMoviePress = () => {
|
onAddMoviePress = () => {
|
||||||
this.props.onAddMoviePress(this.state.searchForMovie);
|
this.props.onAddMoviePress();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -57,6 +42,7 @@ class AddNewMovieModalContent extends Component {
|
|||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
minimumAvailability,
|
minimumAvailability,
|
||||||
|
searchForMovie,
|
||||||
folder,
|
folder,
|
||||||
tags,
|
tags,
|
||||||
isSmallScreen,
|
isSmallScreen,
|
||||||
@@ -175,8 +161,8 @@ class AddNewMovieModalContent extends Component {
|
|||||||
containerClassName={styles.searchForMissingMovieContainer}
|
containerClassName={styles.searchForMissingMovieContainer}
|
||||||
className={styles.searchForMissingMovieInput}
|
className={styles.searchForMissingMovieInput}
|
||||||
name="searchForMovie"
|
name="searchForMovie"
|
||||||
value={this.state.searchForMovie}
|
onChange={onInputChange}
|
||||||
onChange={this.onSearchForMissingMovieChange}
|
{...searchForMovie}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -205,6 +191,7 @@ AddNewMovieModalContent.propTypes = {
|
|||||||
monitor: PropTypes.object.isRequired,
|
monitor: PropTypes.object.isRequired,
|
||||||
qualityProfileId: PropTypes.object,
|
qualityProfileId: PropTypes.object,
|
||||||
minimumAvailability: PropTypes.object.isRequired,
|
minimumAvailability: PropTypes.object.isRequired,
|
||||||
|
searchForMovie: PropTypes.object.isRequired,
|
||||||
folder: PropTypes.string.isRequired,
|
folder: PropTypes.string.isRequired,
|
||||||
tags: PropTypes.object.isRequired,
|
tags: PropTypes.object.isRequired,
|
||||||
isSmallScreen: PropTypes.bool.isRequired,
|
isSmallScreen: PropTypes.bool.isRequired,
|
||||||
|
|||||||
@@ -53,13 +53,14 @@ class AddNewMovieModalContentConnector extends Component {
|
|||||||
this.props.setAddMovieDefault({ [name]: value });
|
this.props.setAddMovieDefault({ [name]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddMoviePress = (searchForMovie) => {
|
onAddMoviePress = () => {
|
||||||
const {
|
const {
|
||||||
tmdbId,
|
tmdbId,
|
||||||
rootFolderPath,
|
rootFolderPath,
|
||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
minimumAvailability,
|
minimumAvailability,
|
||||||
|
searchForMovie,
|
||||||
tags
|
tags
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -69,8 +70,8 @@ class AddNewMovieModalContentConnector extends Component {
|
|||||||
monitor: monitor.value,
|
monitor: monitor.value,
|
||||||
qualityProfileId: qualityProfileId.value,
|
qualityProfileId: qualityProfileId.value,
|
||||||
minimumAvailability: minimumAvailability.value,
|
minimumAvailability: minimumAvailability.value,
|
||||||
tags: tags.value,
|
searchForMovie: searchForMovie.value,
|
||||||
searchForMovie
|
tags: tags.value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@ AddNewMovieModalContentConnector.propTypes = {
|
|||||||
monitor: PropTypes.object.isRequired,
|
monitor: PropTypes.object.isRequired,
|
||||||
qualityProfileId: PropTypes.object,
|
qualityProfileId: PropTypes.object,
|
||||||
minimumAvailability: PropTypes.object.isRequired,
|
minimumAvailability: PropTypes.object.isRequired,
|
||||||
|
searchForMovie: PropTypes.object.isRequired,
|
||||||
tags: PropTypes.object.isRequired,
|
tags: PropTypes.object.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired,
|
onModalClose: PropTypes.func.isRequired,
|
||||||
setAddMovieDefault: PropTypes.func.isRequired,
|
setAddMovieDefault: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -27,9 +27,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.poster {
|
.poster {
|
||||||
flex: 0 0 170px;
|
position: relative;
|
||||||
|
display: block;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
height: 250px;
|
height: 250px;
|
||||||
|
background-color: $defaultColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@@ -86,6 +88,28 @@
|
|||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.posterContainer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certification {
|
||||||
|
margin-left: 2px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.runtime {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusContainer {
|
||||||
|
margin-right: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointMedium) {
|
@media only screen and (max-width: $breakpointMedium) {
|
||||||
.titleRow {
|
.titleRow {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ import Link from 'Components/Link/Link';
|
|||||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||||
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
|
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
|
||||||
import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks';
|
import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks';
|
||||||
|
import MovieStatusLabel from 'Movie/Details/MovieStatusLabel';
|
||||||
|
import MovieIndexProgressBar from 'Movie/Index/ProgressBar/MovieIndexProgressBar';
|
||||||
import MoviePoster from 'Movie/MoviePoster';
|
import MoviePoster from 'Movie/MoviePoster';
|
||||||
|
import formatRuntime from 'Utilities/Date/formatRuntime';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import AddNewMovieModal from './AddNewMovieModal';
|
import AddNewMovieModal from './AddNewMovieModal';
|
||||||
import styles from './AddNewMovieSearchResult.css';
|
import styles from './AddNewMovieSearchResult.css';
|
||||||
@@ -65,7 +68,17 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
images,
|
images,
|
||||||
isExistingMovie,
|
isExistingMovie,
|
||||||
isExclusionMovie,
|
isExclusionMovie,
|
||||||
isSmallScreen
|
isSmallScreen,
|
||||||
|
colorImpairedMode,
|
||||||
|
id,
|
||||||
|
monitored,
|
||||||
|
hasFile,
|
||||||
|
isAvailable,
|
||||||
|
queueStatus,
|
||||||
|
queueState,
|
||||||
|
runtime,
|
||||||
|
movieRuntimeFormat,
|
||||||
|
certification
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -85,12 +98,30 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
{
|
{
|
||||||
isSmallScreen ?
|
isSmallScreen ?
|
||||||
null :
|
null :
|
||||||
<MoviePoster
|
<div>
|
||||||
className={styles.poster}
|
<div className={styles.posterContainer}>
|
||||||
images={images}
|
<MoviePoster
|
||||||
size={250}
|
className={styles.poster}
|
||||||
overflow={true}
|
images={images}
|
||||||
/>
|
size={250}
|
||||||
|
overflow={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
isExistingMovie &&
|
||||||
|
<MovieIndexProgressBar
|
||||||
|
monitored={monitored}
|
||||||
|
hasFile={hasFile}
|
||||||
|
status={status}
|
||||||
|
posterWidth={167}
|
||||||
|
detailedProgressBar={true}
|
||||||
|
queueStatus={queueStatus}
|
||||||
|
queueState={queueState}
|
||||||
|
isAvailable={isAvailable}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
@@ -133,6 +164,22 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
!!certification &&
|
||||||
|
<span className={styles.certification}>
|
||||||
|
{certification}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
!!runtime &&
|
||||||
|
<span className={styles.runtime}>
|
||||||
|
{formatRuntime(runtime, movieRuntimeFormat)}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Label size={sizes.LARGE}>
|
<Label size={sizes.LARGE}>
|
||||||
<HeartRating
|
<HeartRating
|
||||||
@@ -176,13 +223,15 @@ class AddNewMovieSearchResult extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
status === 'ended' &&
|
isExistingMovie && isSmallScreen &&
|
||||||
<Label
|
<MovieStatusLabel
|
||||||
kind={kinds.DANGER}
|
hasMovieFiles={hasFile}
|
||||||
size={sizes.LARGE}
|
monitored={monitored}
|
||||||
>
|
isAvailable={isAvailable}
|
||||||
Ended
|
id={id}
|
||||||
</Label>
|
useLabel={true}
|
||||||
|
colorImpairedMode={colorImpairedMode}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -222,7 +271,18 @@ AddNewMovieSearchResult.propTypes = {
|
|||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isExistingMovie: PropTypes.bool.isRequired,
|
isExistingMovie: PropTypes.bool.isRequired,
|
||||||
isExclusionMovie: PropTypes.bool.isRequired,
|
isExclusionMovie: PropTypes.bool.isRequired,
|
||||||
isSmallScreen: PropTypes.bool.isRequired
|
isSmallScreen: PropTypes.bool.isRequired,
|
||||||
|
id: PropTypes.number,
|
||||||
|
queueItems: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
monitored: PropTypes.bool.isRequired,
|
||||||
|
hasFile: PropTypes.bool.isRequired,
|
||||||
|
isAvailable: PropTypes.bool.isRequired,
|
||||||
|
colorImpairedMode: PropTypes.bool,
|
||||||
|
queueStatus: PropTypes.string,
|
||||||
|
queueState: PropTypes.string,
|
||||||
|
runtime: PropTypes.number.isRequired,
|
||||||
|
movieRuntimeFormat: PropTypes.string.isRequired,
|
||||||
|
certification: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddNewMovieSearchResult;
|
export default AddNewMovieSearchResult;
|
||||||
|
|||||||
@@ -10,11 +10,17 @@ function createMapStateToProps() {
|
|||||||
createExistingMovieSelector(),
|
createExistingMovieSelector(),
|
||||||
createExclusionMovieSelector(),
|
createExclusionMovieSelector(),
|
||||||
createDimensionsSelector(),
|
createDimensionsSelector(),
|
||||||
(isExistingMovie, isExclusionMovie, dimensions) => {
|
(state) => state.queue.details.items,
|
||||||
|
(state, { internalId }) => internalId,
|
||||||
|
(isExistingMovie, isExclusionMovie, dimensions, queueItems, internalId) => {
|
||||||
|
const firstQueueItem = queueItems.find((q) => q.movieId === internalId && internalId > 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isExistingMovie,
|
isExistingMovie,
|
||||||
isExclusionMovie,
|
isExclusionMovie,
|
||||||
isSmallScreen: dimensions.isSmallScreen
|
isSmallScreen: dimensions.isSmallScreen,
|
||||||
|
queueStatus: firstQueueItem ? firstQueueItem.status : null,
|
||||||
|
queueState: firstQueueItem ? firstQueueItem.trackedDownloadState : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class ImportMovie extends Component {
|
|||||||
rootFoldersPopulated &&
|
rootFoldersPopulated &&
|
||||||
!unmappedFolders.length ?
|
!unmappedFolders.length ?
|
||||||
<div>
|
<div>
|
||||||
All movies in {path} have been imported
|
{translate('AllMoviesInPathHaveBeenImported', [path])}
|
||||||
</div> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ function ImportMovieHeader(props) {
|
|||||||
{translate('Folder')}
|
{translate('Folder')}
|
||||||
</VirtualTableHeaderCell>
|
</VirtualTableHeaderCell>
|
||||||
|
|
||||||
|
<VirtualTableHeaderCell
|
||||||
|
className={styles.movie}
|
||||||
|
name="movie"
|
||||||
|
>
|
||||||
|
{translate('Movie')}
|
||||||
|
</VirtualTableHeaderCell>
|
||||||
|
|
||||||
<VirtualTableHeaderCell
|
<VirtualTableHeaderCell
|
||||||
className={styles.monitor}
|
className={styles.monitor}
|
||||||
name="monitor"
|
name="monitor"
|
||||||
@@ -48,13 +55,6 @@ function ImportMovieHeader(props) {
|
|||||||
>
|
>
|
||||||
{translate('QualityProfile')}
|
{translate('QualityProfile')}
|
||||||
</VirtualTableHeaderCell>
|
</VirtualTableHeaderCell>
|
||||||
|
|
||||||
<VirtualTableHeaderCell
|
|
||||||
className={styles.movie}
|
|
||||||
name="movie"
|
|
||||||
>
|
|
||||||
{translate('Movie')}
|
|
||||||
</VirtualTableHeaderCell>
|
|
||||||
</VirtualTableHeader>
|
</VirtualTableHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ function ImportMovieRow(props) {
|
|||||||
{id}
|
{id}
|
||||||
</VirtualTableRowCell>
|
</VirtualTableRowCell>
|
||||||
|
|
||||||
|
<VirtualTableRowCell className={styles.movie}>
|
||||||
|
<ImportMovieSelectMovieConnector
|
||||||
|
id={id}
|
||||||
|
isExistingMovie={isExistingMovie}
|
||||||
|
/>
|
||||||
|
</VirtualTableRowCell>
|
||||||
|
|
||||||
<VirtualTableRowCell className={styles.monitor}>
|
<VirtualTableRowCell className={styles.monitor}>
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.MOVIE_MONITORED_SELECT}
|
type={inputTypes.MOVIE_MONITORED_SELECT}
|
||||||
@@ -60,13 +67,6 @@ function ImportMovieRow(props) {
|
|||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
/>
|
/>
|
||||||
</VirtualTableRowCell>
|
</VirtualTableRowCell>
|
||||||
|
|
||||||
<VirtualTableRowCell className={styles.movie}>
|
|
||||||
<ImportMovieSelectMovieConnector
|
|
||||||
id={id}
|
|
||||||
isExistingMovie={isExistingMovie}
|
|
||||||
/>
|
|
||||||
</VirtualTableRowCell>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ class ImportMovieSelectFolder extends Component {
|
|||||||
<ul>
|
<ul>
|
||||||
<li className={styles.tip} dangerouslySetInnerHTML={{ __html: translate('ImportIncludeQuality', ['<code>movie.2008.bluray.mkv</code>']) }} />
|
<li className={styles.tip} dangerouslySetInnerHTML={{ __html: translate('ImportIncludeQuality', ['<code>movie.2008.bluray.mkv</code>']) }} />
|
||||||
<li className={styles.tip} dangerouslySetInnerHTML={{ __html: translate('ImportRootPath', [`<code>${isWindows ? 'C:\\movies' : '/movies'}</code>`, `<code>${isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}</code>`]) }} />
|
<li className={styles.tip} dangerouslySetInnerHTML={{ __html: translate('ImportRootPath', [`<code>${isWindows ? 'C:\\movies' : '/movies'}</code>`, `<code>${isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}</code>`]) }} />
|
||||||
|
<li className={styles.tip}>{translate('ImportNotForDownloads')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,47 @@ import UpdateChanges from 'System/Updates/UpdateChanges';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './AppUpdatedModalContent.css';
|
import styles from './AppUpdatedModalContent.css';
|
||||||
|
|
||||||
|
function mergeUpdates(items, version, prevVersion) {
|
||||||
|
let installedIndex = items.findIndex((u) => u.version === version);
|
||||||
|
let installedPreviouslyIndex = items.findIndex((u) => u.version === prevVersion);
|
||||||
|
|
||||||
|
if (installedIndex === -1) {
|
||||||
|
installedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (installedPreviouslyIndex === -1) {
|
||||||
|
installedPreviouslyIndex = items.length;
|
||||||
|
} else if (installedPreviouslyIndex === installedIndex && items.length) {
|
||||||
|
installedPreviouslyIndex += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appliedUpdates = items.slice(installedIndex, installedPreviouslyIndex);
|
||||||
|
|
||||||
|
if (!appliedUpdates.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appliedChanges = { new: [], fixed: [] };
|
||||||
|
appliedUpdates.forEach((u) => {
|
||||||
|
if (u.changes) {
|
||||||
|
appliedChanges.new.push(... u.changes.new);
|
||||||
|
appliedChanges.fixed.push(... u.changes.fixed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const mergedUpdate = Object.assign({}, appliedUpdates[0], { changes: appliedChanges });
|
||||||
|
|
||||||
|
if (!appliedChanges.new.length && !appliedChanges.fixed.length) {
|
||||||
|
mergedUpdate.changes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
function AppUpdatedModalContent(props) {
|
function AppUpdatedModalContent(props) {
|
||||||
const {
|
const {
|
||||||
version,
|
version,
|
||||||
|
prevVersion,
|
||||||
isPopulated,
|
isPopulated,
|
||||||
error,
|
error,
|
||||||
items,
|
items,
|
||||||
@@ -21,32 +59,32 @@ function AppUpdatedModalContent(props) {
|
|||||||
onModalClose
|
onModalClose
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const update = items[0];
|
const update = mergeUpdates(items, version, prevVersion);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Radarr Updated
|
{translate('RadarrUpdated')}
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<div dangerouslySetInnerHTML={{ __html: translate('VersionUpdateText', [`<span className=${styles.version}>${version}</span>`]) }} />
|
||||||
Version <span className={styles.version}>{version}</span> of Radarr has been installed, in order to get the latest changes you'll need to reload Radarr.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !error && !!update &&
|
isPopulated && !error && !!update &&
|
||||||
<div>
|
<div>
|
||||||
{
|
{
|
||||||
!update.changes &&
|
!update.changes &&
|
||||||
<div className={styles.maintenance}>Maintenance release</div>
|
<div className={styles.maintenance}>
|
||||||
|
{translate('MaintenanceRelease')}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!!update.changes &&
|
!!update.changes &&
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.changes}>
|
<div className={styles.changes}>
|
||||||
What's new?
|
{translate('WhatsNew')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UpdateChanges
|
<UpdateChanges
|
||||||
@@ -73,14 +111,14 @@ function AppUpdatedModalContent(props) {
|
|||||||
<Button
|
<Button
|
||||||
onPress={onSeeChangesPress}
|
onPress={onSeeChangesPress}
|
||||||
>
|
>
|
||||||
Recent Changes
|
{translate('RecentChanges')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
kind={kinds.PRIMARY}
|
kind={kinds.PRIMARY}
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
Reload
|
{translate('Reload')}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
@@ -89,6 +127,7 @@ function AppUpdatedModalContent(props) {
|
|||||||
|
|
||||||
AppUpdatedModalContent.propTypes = {
|
AppUpdatedModalContent.propTypes = {
|
||||||
version: PropTypes.string.isRequired,
|
version: PropTypes.string.isRequired,
|
||||||
|
prevVersion: PropTypes.string,
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import AppUpdatedModalContent from './AppUpdatedModalContent';
|
|||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.app.version,
|
(state) => state.app.version,
|
||||||
|
(state) => state.app.prevVersion,
|
||||||
(state) => state.system.updates,
|
(state) => state.system.updates,
|
||||||
(version, updates) => {
|
(version, prevVersion, updates) => {
|
||||||
const {
|
const {
|
||||||
isPopulated,
|
isPopulated,
|
||||||
error,
|
error,
|
||||||
@@ -18,6 +19,7 @@ function createMapStateToProps() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
|
prevVersion,
|
||||||
isPopulated,
|
isPopulated,
|
||||||
error,
|
error,
|
||||||
items
|
items
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
flex: 0 0 120px;
|
flex: 0 0 125px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function createMissingMovieIdsSelector() {
|
|||||||
const inCinemas = movie.inCinemas;
|
const inCinemas = movie.inCinemas;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!movie.movieFileId &&
|
!movie.hasFile &&
|
||||||
moment(inCinemas).isAfter(start) &&
|
moment(inCinemas).isAfter(start) &&
|
||||||
moment(inCinemas).isBefore(end) &&
|
moment(inCinemas).isBefore(end) &&
|
||||||
isBefore(movie.inCinemas) &&
|
isBefore(movie.inCinemas) &&
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ class CalendarHeader extends Component {
|
|||||||
selectedView={view}
|
selectedView={view}
|
||||||
onPress={this.onViewChange}
|
onPress={this.onViewChange}
|
||||||
>
|
>
|
||||||
Month
|
{translate('Month')}
|
||||||
</ViewMenuItem>
|
</ViewMenuItem>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ class CalendarHeader extends Component {
|
|||||||
selectedView={view}
|
selectedView={view}
|
||||||
onPress={this.onViewChange}
|
onPress={this.onViewChange}
|
||||||
>
|
>
|
||||||
Week
|
{translate('Week')}
|
||||||
</ViewMenuItem>
|
</ViewMenuItem>
|
||||||
|
|
||||||
<ViewMenuItem
|
<ViewMenuItem
|
||||||
@@ -185,7 +185,7 @@ class CalendarHeader extends Component {
|
|||||||
selectedView={view}
|
selectedView={view}
|
||||||
onPress={this.onViewChange}
|
onPress={this.onViewChange}
|
||||||
>
|
>
|
||||||
Forecast
|
{translate('Forecast')}
|
||||||
</ViewMenuItem>
|
</ViewMenuItem>
|
||||||
|
|
||||||
<ViewMenuItem
|
<ViewMenuItem
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
import LegendIconItem from './LegendIconItem';
|
import LegendIconItem from './LegendIconItem';
|
||||||
import LegendItem from './LegendItem';
|
import LegendItem from './LegendItem';
|
||||||
import styles from './Legend.css';
|
import styles from './Legend.css';
|
||||||
@@ -16,10 +17,10 @@ function Legend(props) {
|
|||||||
if (showCutoffUnmetIcon) {
|
if (showCutoffUnmetIcon) {
|
||||||
iconsToShow.push(
|
iconsToShow.push(
|
||||||
<LegendIconItem
|
<LegendIconItem
|
||||||
name="Cutoff Not Met"
|
name={translate('CutoffUnmet')}
|
||||||
icon={icons.MOVIE_FILE}
|
icon={icons.MOVIE_FILE}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
tooltip="Quality or language cutoff has not been met"
|
tooltip={translate('QualityOrLangCutoffHasNotBeenMet')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -28,32 +29,45 @@ function Legend(props) {
|
|||||||
<div className={styles.legend}>
|
<div className={styles.legend}>
|
||||||
<div>
|
<div>
|
||||||
<LegendItem
|
<LegendItem
|
||||||
status="unreleased"
|
style='ended'
|
||||||
tooltip="Movie hasn't released yet"
|
name={translate('DownloadedAndMonitored')}
|
||||||
colorImpairedMode={colorImpairedMode}
|
colorImpairedMode={colorImpairedMode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LegendItem
|
<LegendItem
|
||||||
status="unmonitored"
|
style='availNotMonitored'
|
||||||
tooltip="Movie is unmonitored"
|
name={translate('DownloadedButNotMonitored')}
|
||||||
colorImpairedMode={colorImpairedMode}
|
colorImpairedMode={colorImpairedMode}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<LegendItem
|
<LegendItem
|
||||||
status="downloading"
|
style='missingMonitored'
|
||||||
tooltip="Movie is currently downloading"
|
name={translate('MissingMonitoredAndConsideredAvailable')}
|
||||||
colorImpairedMode={colorImpairedMode}
|
colorImpairedMode={colorImpairedMode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LegendItem
|
<LegendItem
|
||||||
status="downloaded"
|
style='missingUnmonitored'
|
||||||
tooltip="Movie was downloaded and sorted"
|
name={translate('MissingNotMonitored')}
|
||||||
colorImpairedMode={colorImpairedMode}
|
colorImpairedMode={colorImpairedMode}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<LegendItem
|
||||||
|
style='queue'
|
||||||
|
name={translate('Queued')}
|
||||||
|
colorImpairedMode={colorImpairedMode}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LegendItem
|
||||||
|
style='continuing'
|
||||||
|
name={translate('Unreleased')}
|
||||||
|
colorImpairedMode={colorImpairedMode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{
|
{
|
||||||
iconsToShow.length > 0 &&
|
iconsToShow.length > 0 &&
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
.legendIconItem {
|
.legendIconItem {
|
||||||
margin: 3px 0;
|
margin-left: 6px;
|
||||||
margin-right: 6px;
|
|
||||||
width: 150px;
|
|
||||||
cursor: default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
|||||||
@@ -1,33 +1,74 @@
|
|||||||
|
.legendItemContainer {
|
||||||
|
margin-right: 5px;
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
.legendItem {
|
.legendItem {
|
||||||
margin: 3px 0;
|
display: inline-flex;
|
||||||
margin-right: 6px;
|
margin-top: -1px;
|
||||||
padding-left: 5px;
|
vertical-align: middle;
|
||||||
width: 150px;
|
line-height: 16px;
|
||||||
border-left-width: 4px;
|
|
||||||
border-left-style: solid;
|
|
||||||
cursor: default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
.legendItemColor {
|
||||||
* Status
|
margin-right: 8px;
|
||||||
*/
|
width: 30px;
|
||||||
|
height: 16px;
|
||||||
.downloaded {
|
border-radius: 4px;
|
||||||
composes: downloaded from '~Calendar/Events/CalendarEvent.css';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.downloading {
|
.queue {
|
||||||
composes: downloading from '~Calendar/Events/CalendarEvent.css';
|
composes: legendItemColor;
|
||||||
|
|
||||||
|
background-color: $queueColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unmonitored {
|
.continuing {
|
||||||
composes: unmonitored from '~Calendar/Events/CalendarEvent.css';
|
composes: legendItemColor;
|
||||||
|
|
||||||
|
background-color: $primaryColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.missing {
|
.availNotMonitored {
|
||||||
composes: missing from '~Calendar/Events/CalendarEvent.css';
|
composes: legendItemColor;
|
||||||
|
|
||||||
|
background-color: $darkGray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unreleased {
|
.ended {
|
||||||
composes: unreleased from '~Calendar/Events/CalendarEvent.css';
|
composes: legendItemColor;
|
||||||
|
|
||||||
|
background-color: $successColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.missingMonitored {
|
||||||
|
composes: legendItemColor;
|
||||||
|
|
||||||
|
background-color: $dangerColor;
|
||||||
|
|
||||||
|
&:global(.colorImpaired) {
|
||||||
|
background: repeating-linear-gradient(90deg, color($dangerColor shade(5%)), color($dangerColor shade(5%)) 5px, color($dangerColor shade(15%)) 5px, color($dangerColor shade(15%)) 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.missingUnmonitored {
|
||||||
|
composes: legendItemColor;
|
||||||
|
|
||||||
|
background-color: $warningColor;
|
||||||
|
|
||||||
|
&:global(.colorImpaired) {
|
||||||
|
background: repeating-linear-gradient(45deg, $warningColor, $warningColor 5px, color($warningColor tint(15%)) 5px, color($warningColor tint(15%)) 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.missingMonitoredColorImpaired {
|
||||||
|
background: repeating-linear-gradient(90deg, $colorImpairedGradientDark, $colorImpairedGradientDark 5px, $colorImpairedGradient 5px, $colorImpairedGradient 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.missingUnmonitoredColorImpaired {
|
||||||
|
background: repeating-linear-gradient(45deg, $colorImpairedGradientDark, $colorImpairedGradientDark 5px, $colorImpairedGradient 5px, $colorImpairedGradient 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.legendItemText {
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,34 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import titleCase from 'Utilities/String/titleCase';
|
|
||||||
import styles from './LegendItem.css';
|
import styles from './LegendItem.css';
|
||||||
|
|
||||||
function LegendItem(props) {
|
function LegendItem(props) {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
status,
|
style,
|
||||||
tooltip,
|
|
||||||
colorImpairedMode
|
colorImpairedMode
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={styles.legendItemContainer}>
|
||||||
className={classNames(
|
<div
|
||||||
styles.legendItem,
|
className={classNames(
|
||||||
styles[status],
|
styles.legendItem,
|
||||||
colorImpairedMode && 'colorImpaired'
|
styles[style],
|
||||||
)}
|
colorImpairedMode && 'colorImpaired'
|
||||||
title={tooltip}
|
)}
|
||||||
>
|
/>
|
||||||
{name ? name : titleCase(status)}
|
<div className={classNames(styles.legendItemText, colorImpairedMode && styles[`${style}ColorImpaired`])}>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
LegendItem.propTypes = {
|
LegendItem.propTypes = {
|
||||||
name: PropTypes.string,
|
name: PropTypes.string.isRequired,
|
||||||
status: PropTypes.string.isRequired,
|
style: PropTypes.string.isRequired,
|
||||||
tooltip: PropTypes.string.isRequired,
|
|
||||||
colorImpairedMode: PropTypes.bool.isRequired
|
colorImpairedMode: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class CalendarOptionsModalContent extends Component {
|
|||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Calendar Options
|
{translate('CalendarOptions')}
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function getUrls(state) {
|
|||||||
tags
|
tags
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
let icalUrl = `${window.location.host}${window.Radarr.urlBase}/feed/calendar/Radarr.ics?`;
|
let icalUrl = `${window.location.host}${window.Radarr.urlBase}/feed/v3/calendar/Radarr.ics?`;
|
||||||
|
|
||||||
if (unmonitored) {
|
if (unmonitored) {
|
||||||
icalUrl += 'unmonitored=true&';
|
icalUrl += 'unmonitored=true&';
|
||||||
@@ -109,7 +109,7 @@ class CalendarLinkModalContent extends Component {
|
|||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Radarr Calendar Feed
|
{translate('RadarrCalendarFeed')}
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ class FileBrowserModalContent extends Component {
|
|||||||
className={styles.mappedDrivesWarning}
|
className={styles.mappedDrivesWarning}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
>
|
>
|
||||||
Mapped network drives are not available when running as a Windows Service, see the <Link className={styles.faqLink} to="https://github.com/Radarr/Radarr/wiki/FAQ">FAQ</Link> for more information.
|
<Link to="https://wiki.servarr.com/Radarr_FAQ#Why_cant_Radarr_see_my_files_on_a_remote_server">
|
||||||
|
{translate('MappedDrivesRunningAsService')}
|
||||||
|
</Link> .
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ 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, NOT_IN_LAST, NOT_IN_NEXT } from 'Helpers/Props/filterTypes';
|
||||||
import isString from 'Utilities/String/isString';
|
import isString from 'Utilities/String/isString';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
import { NAME } from './FilterBuilderRowValue';
|
import { NAME } from './FilterBuilderRowValue';
|
||||||
import styles from './DateFilterBuilderRowValue.css';
|
import styles from './DateFilterBuilderRowValue.css';
|
||||||
|
|
||||||
const timeOptions = [
|
const timeOptions = [
|
||||||
{ key: 'seconds', value: 'seconds' },
|
{ key: 'seconds', value: translate('Seconds') },
|
||||||
{ key: 'minutes', value: 'minutes' },
|
{ key: 'minutes', value: translate('Minutes') },
|
||||||
{ key: 'hours', value: 'hours' },
|
{ key: 'hours', value: translate('Hours') },
|
||||||
{ key: 'days', value: 'days' },
|
{ key: 'days', value: translate('Days') },
|
||||||
{ key: 'weeks', value: 'weeks' },
|
{ key: 'weeks', value: translate('Weeks') },
|
||||||
{ key: 'months', value: 'months' }
|
{ key: 'months', value: translate('Months') }
|
||||||
];
|
];
|
||||||
|
|
||||||
function isInFilter(filterType) {
|
function isInFilter(filterType) {
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
|
|||||||
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
|
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
|
||||||
import ImportListFilterBuilderRowValueConnector from './ImportListFilterBuilderRowValueConnector';
|
import ImportListFilterBuilderRowValueConnector from './ImportListFilterBuilderRowValueConnector';
|
||||||
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
|
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
|
||||||
import MovieStatusFilterBuilderRowValue from './MovieStatusFilterBuilderRowValue';
|
import MinimumAvailabilityFilterBuilderRowValue from './MinimumAvailabilityFilterBuilderRowValue';
|
||||||
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
|
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
|
||||||
import QualityFilterBuilderRowValueConnector from './QualityFilterBuilderRowValueConnector';
|
import QualityFilterBuilderRowValueConnector from './QualityFilterBuilderRowValueConnector';
|
||||||
import QualityProfileFilterBuilderRowValueConnector from './QualityProfileFilterBuilderRowValueConnector';
|
import QualityProfileFilterBuilderRowValueConnector from './QualityProfileFilterBuilderRowValueConnector';
|
||||||
|
import ReleaseStatusFilterBuilderRowValue from './ReleaseStatusFilterBuilderRowValue';
|
||||||
import TagFilterBuilderRowValueConnector from './TagFilterBuilderRowValueConnector';
|
import TagFilterBuilderRowValueConnector from './TagFilterBuilderRowValueConnector';
|
||||||
import styles from './FilterBuilderRow.css';
|
import styles from './FilterBuilderRow.css';
|
||||||
|
|
||||||
@@ -69,8 +70,11 @@ function getRowValueConnector(selectedFilterBuilderProp) {
|
|||||||
case filterBuilderValueTypes.QUALITY_PROFILE:
|
case filterBuilderValueTypes.QUALITY_PROFILE:
|
||||||
return QualityProfileFilterBuilderRowValueConnector;
|
return QualityProfileFilterBuilderRowValueConnector;
|
||||||
|
|
||||||
case filterBuilderValueTypes.MOVIE_STATUS:
|
case filterBuilderValueTypes.RELEASE_STATUS:
|
||||||
return MovieStatusFilterBuilderRowValue;
|
return ReleaseStatusFilterBuilderRowValue;
|
||||||
|
|
||||||
|
case filterBuilderValueTypes.MINIMUM_AVAILABILITY:
|
||||||
|
return MinimumAvailabilityFilterBuilderRowValue;
|
||||||
|
|
||||||
case filterBuilderValueTypes.TAG:
|
case filterBuilderValueTypes.TAG:
|
||||||
return TagFilterBuilderRowValueConnector;
|
return TagFilterBuilderRowValueConnector;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function createTagListSelector() {
|
|||||||
(selectedFilterBuilderProp.type === filterBuilderTypes.NUMBER ||
|
(selectedFilterBuilderProp.type === filterBuilderTypes.NUMBER ||
|
||||||
selectedFilterBuilderProp.type === filterBuilderTypes.STRING) &&
|
selectedFilterBuilderProp.type === filterBuilderTypes.STRING) &&
|
||||||
filterType !== filterTypes.EQUAL &&
|
filterType !== filterTypes.EQUAL &&
|
||||||
filterType !== filterBuilderTypes.NOT_EQUAL ||
|
filterType !== filterTypes.NOT_EQUAL ||
|
||||||
!selectedFilterBuilderProp.optionsSelector
|
!selectedFilterBuilderProp.optionsSelector
|
||||||
) {
|
) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
||||||
|
|
||||||
|
const protocols = [
|
||||||
|
{ id: 'announced', name: translate('Announced') },
|
||||||
|
{ id: 'inCinemas', name: translate('InCinemas') },
|
||||||
|
{ id: 'released', name: translate('Released') }
|
||||||
|
];
|
||||||
|
|
||||||
|
function MinimumAvailabilityFilterBuilderRowValue(props) {
|
||||||
|
return (
|
||||||
|
<FilterBuilderRowValue
|
||||||
|
tagList={protocols}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MinimumAvailabilityFilterBuilderRowValue;
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
|
||||||
|
|
||||||
const protocols = [
|
|
||||||
{ id: 'tba', name: 'TBA' },
|
|
||||||
{ id: 'announced', name: 'Announced' },
|
|
||||||
{ id: 'inCinemas', name: 'In Cinemas' },
|
|
||||||
{ id: 'released', name: 'Released' },
|
|
||||||
{ id: 'deleted', name: 'Deleted' }
|
|
||||||
];
|
|
||||||
|
|
||||||
function MovieStatusFilterBuilderRowValue(props) {
|
|
||||||
return (
|
|
||||||
<FilterBuilderRowValue
|
|
||||||
tagList={protocols}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MovieStatusFilterBuilderRowValue;
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
||||||
|
|
||||||
|
const protocols = [
|
||||||
|
{ id: 'tba', name: 'TBA' },
|
||||||
|
{ id: 'announced', name: translate('Announced') },
|
||||||
|
{ id: 'inCinemas', name: translate('InCinemas') },
|
||||||
|
{ id: 'released', name: translate('Released') },
|
||||||
|
{ id: 'deleted', name: translate('Deleted') }
|
||||||
|
];
|
||||||
|
|
||||||
|
function ReleaseStatusFilterBuilderRowValue(props) {
|
||||||
|
return (
|
||||||
|
<FilterBuilderRowValue
|
||||||
|
tagList={protocols}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReleaseStatusFilterBuilderRowValue;
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
import SelectInput from './SelectInput';
|
import SelectInput from './SelectInput';
|
||||||
|
|
||||||
const availabilityOptions = [
|
const availabilityOptions = [
|
||||||
{ key: 'announced', value: 'Announced' },
|
{ key: 'announced', value: translate('Announced') },
|
||||||
{ key: 'inCinemas', value: 'In Cinemas' },
|
{ key: 'inCinemas', value: translate('InCinemas') },
|
||||||
{ key: 'released', value: 'Released' },
|
{ key: 'released', value: translate('Released') },
|
||||||
{ key: 'preDB', value: 'PreDB' }
|
{ key: 'preDB', value: translate('PreDB') }
|
||||||
];
|
];
|
||||||
|
|
||||||
function AvailabilitySelectInput(props) {
|
function AvailabilitySelectInput(props) {
|
||||||
|
|||||||
@@ -2,34 +2,22 @@ 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';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { clearOptions, fetchOptions } from 'Store/Actions/providerOptionActions';
|
import { clearOptions, defaultState, fetchOptions } from 'Store/Actions/providerOptionActions';
|
||||||
import DeviceInput from './DeviceInput';
|
import DeviceInput from './DeviceInput';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { value }) => value,
|
(state, { value }) => value,
|
||||||
(state, { name }) => name,
|
(state) => state.providerOptions.devices || defaultState,
|
||||||
(state) => state.providerOptions,
|
(value, devices) => {
|
||||||
(value, name, 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 {
|
||||||
@@ -63,7 +51,7 @@ class DeviceInputConnector extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount = () => {
|
componentWillUnmount = () => {
|
||||||
this.props.dispatchClearOptions();
|
this.props.dispatchClearOptions({ section: 'devices' });
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -73,14 +61,12 @@ class DeviceInputConnector extends Component {
|
|||||||
const {
|
const {
|
||||||
provider,
|
provider,
|
||||||
providerData,
|
providerData,
|
||||||
dispatchFetchOptions,
|
dispatchFetchOptions
|
||||||
requestAction,
|
|
||||||
name
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
dispatchFetchOptions({
|
dispatchFetchOptions({
|
||||||
action: requestAction,
|
section: 'devices',
|
||||||
itemSection: name,
|
action: 'getDevices',
|
||||||
provider,
|
provider,
|
||||||
providerData
|
providerData
|
||||||
});
|
});
|
||||||
@@ -109,7 +95,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,
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editableContainer {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.hasError {
|
.hasError {
|
||||||
composes: hasError from '~Components/Form/Input.css';
|
composes: hasError from '~Components/Form/Input.css';
|
||||||
}
|
}
|
||||||
@@ -22,6 +26,16 @@
|
|||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdownArrowContainerEditable {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding-right: 17px;
|
||||||
|
width: 30%;
|
||||||
|
height: 35px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdownArrowContainerDisabled {
|
.dropdownArrowContainerDisabled {
|
||||||
composes: dropdownArrowContainer;
|
composes: dropdownArrowContainer;
|
||||||
|
|
||||||
@@ -66,3 +80,26 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 5px -5px 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobileCloseButtonContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
height: 40px;
|
||||||
|
border-bottom: 1px solid $borderColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobileCloseButton {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 40px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $modalCloseButtonHoverColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import React, { Component } from 'react';
|
|||||||
import { Manager, Popper, Reference } from 'react-popper';
|
import { Manager, Popper, Reference } from 'react-popper';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import Measure from 'Components/Measure';
|
import Measure from 'Components/Measure';
|
||||||
import Modal from 'Components/Modal/Modal';
|
import Modal from 'Components/Modal/Modal';
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
@@ -16,6 +17,7 @@ import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
|||||||
import { isMobile as isMobileUtil } from 'Utilities/mobile';
|
import { isMobile as isMobileUtil } from 'Utilities/mobile';
|
||||||
import HintedSelectInputOption from './HintedSelectInputOption';
|
import HintedSelectInputOption from './HintedSelectInputOption';
|
||||||
import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue';
|
import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue';
|
||||||
|
import TextInput from './TextInput';
|
||||||
import styles from './EnhancedSelectInput.css';
|
import styles from './EnhancedSelectInput.css';
|
||||||
|
|
||||||
function isArrowKey(keyCode) {
|
function isArrowKey(keyCode) {
|
||||||
@@ -168,11 +170,21 @@ class EnhancedSelectInput extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFocus = () => {
|
||||||
|
if (this.state.isOpen) {
|
||||||
|
this._removeListener();
|
||||||
|
this.setState({ isOpen: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onBlur = () => {
|
onBlur = () => {
|
||||||
// Calling setState without this check prevents the click event from being properly handled on Chrome (it is on firefox)
|
if (!this.props.isEditable) {
|
||||||
const origIndex = getSelectedIndex(this.props);
|
// Calling setState without this check prevents the click event from being properly handled on Chrome (it is on firefox)
|
||||||
if (origIndex !== this.state.selectedIndex) {
|
const origIndex = getSelectedIndex(this.props);
|
||||||
this.setState({ selectedIndex: origIndex });
|
|
||||||
|
if (origIndex !== this.state.selectedIndex) {
|
||||||
|
this.setState({ selectedIndex: origIndex });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,6 +262,10 @@ class EnhancedSelectInput extends Component {
|
|||||||
this._addListener();
|
this._addListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.state.isOpen && this.props.onOpen) {
|
||||||
|
this.props.onOpen();
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ isOpen: !this.state.isOpen });
|
this.setState({ isOpen: !this.state.isOpen });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,15 +308,19 @@ class EnhancedSelectInput extends Component {
|
|||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
disabledClassName,
|
disabledClassName,
|
||||||
|
name,
|
||||||
value,
|
value,
|
||||||
values,
|
values,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
|
isEditable,
|
||||||
|
isFetching,
|
||||||
hasError,
|
hasError,
|
||||||
hasWarning,
|
hasWarning,
|
||||||
valueOptions,
|
valueOptions,
|
||||||
selectedValueOptions,
|
selectedValueOptions,
|
||||||
selectedValueComponent: SelectedValueComponent,
|
selectedValueComponent: SelectedValueComponent,
|
||||||
optionComponent: OptionComponent
|
optionComponent: OptionComponent,
|
||||||
|
onChange
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -326,40 +346,94 @@ class EnhancedSelectInput extends Component {
|
|||||||
whitelist={['width']}
|
whitelist={['width']}
|
||||||
onMeasure={this.onMeasure}
|
onMeasure={this.onMeasure}
|
||||||
>
|
>
|
||||||
<Link
|
{
|
||||||
className={classNames(
|
isEditable ?
|
||||||
className,
|
<div
|
||||||
hasError && styles.hasError,
|
className={styles.editableContainer}
|
||||||
hasWarning && styles.hasWarning,
|
>
|
||||||
isDisabled && disabledClassName
|
<TextInput
|
||||||
)}
|
className={className}
|
||||||
isDisabled={isDisabled}
|
name={name}
|
||||||
onBlur={this.onBlur}
|
value={value}
|
||||||
onKeyDown={this.onKeyDown}
|
readOnly={isDisabled}
|
||||||
onPress={this.onPress}
|
hasError={hasError}
|
||||||
>
|
hasWarning={hasWarning}
|
||||||
<SelectedValueComponent
|
onFocus={this.onFocus}
|
||||||
value={value}
|
onBlur={this.onBlur}
|
||||||
values={values}
|
onChange={onChange}
|
||||||
{...selectedValueOptions}
|
/>
|
||||||
{...selectedOption}
|
<Link
|
||||||
isDisabled={isDisabled}
|
className={classNames(
|
||||||
isMultiSelect={isMultiSelect}
|
styles.dropdownArrowContainerEditable,
|
||||||
>
|
isDisabled ?
|
||||||
{selectedOption ? selectedOption.value : null}
|
styles.dropdownArrowContainerDisabled :
|
||||||
</SelectedValueComponent>
|
styles.dropdownArrowContainer)
|
||||||
|
}
|
||||||
|
onPress={this.onPress}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
isFetching &&
|
||||||
|
<LoadingIndicator
|
||||||
|
className={styles.loading}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
<div
|
{
|
||||||
className={isDisabled ?
|
!isFetching &&
|
||||||
styles.dropdownArrowContainerDisabled :
|
<Icon
|
||||||
styles.dropdownArrowContainer
|
name={icons.CARET_DOWN}
|
||||||
}
|
/>
|
||||||
>
|
}
|
||||||
<Icon
|
</Link>
|
||||||
name={icons.CARET_DOWN}
|
</div> :
|
||||||
/>
|
<Link
|
||||||
</div>
|
className={classNames(
|
||||||
</Link>
|
className,
|
||||||
|
hasError && styles.hasError,
|
||||||
|
hasWarning && styles.hasWarning,
|
||||||
|
isDisabled && disabledClassName
|
||||||
|
)}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
onBlur={this.onBlur}
|
||||||
|
onKeyDown={this.onKeyDown}
|
||||||
|
onPress={this.onPress}
|
||||||
|
>
|
||||||
|
<SelectedValueComponent
|
||||||
|
value={value}
|
||||||
|
values={values}
|
||||||
|
{...selectedValueOptions}
|
||||||
|
{...selectedOption}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
isMultiSelect={isMultiSelect}
|
||||||
|
>
|
||||||
|
{selectedOption ? selectedOption.value : null}
|
||||||
|
</SelectedValueComponent>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={isDisabled ?
|
||||||
|
styles.dropdownArrowContainerDisabled :
|
||||||
|
styles.dropdownArrowContainer
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
{
|
||||||
|
isFetching &&
|
||||||
|
<LoadingIndicator
|
||||||
|
className={styles.loading}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
!isFetching &&
|
||||||
|
<Icon
|
||||||
|
name={icons.CARET_DOWN}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
</Measure>
|
</Measure>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -405,6 +479,7 @@ class EnhancedSelectInput extends Component {
|
|||||||
<OptionComponent
|
<OptionComponent
|
||||||
key={v.key}
|
key={v.key}
|
||||||
id={v.key}
|
id={v.key}
|
||||||
|
dividerAfter={v.dividerAfter}
|
||||||
depth={depth}
|
depth={depth}
|
||||||
isSelected={isSelectedItem(index, this.props)}
|
isSelected={isSelectedItem(index, this.props)}
|
||||||
isDisabled={parentSelected}
|
isDisabled={parentSelected}
|
||||||
@@ -444,6 +519,18 @@ class EnhancedSelectInput extends Component {
|
|||||||
scrollDirection={scrollDirections.NONE}
|
scrollDirection={scrollDirections.NONE}
|
||||||
>
|
>
|
||||||
<Scroller className={styles.optionsModalScroller}>
|
<Scroller className={styles.optionsModalScroller}>
|
||||||
|
<div className={styles.mobileCloseButtonContainer}>
|
||||||
|
<Link
|
||||||
|
className={styles.mobileCloseButton}
|
||||||
|
onPress={this.onOptionsModalClose}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name={icons.CLOSE}
|
||||||
|
size={18}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
values.map((v, index) => {
|
values.map((v, index) => {
|
||||||
const hasParent = v.parentKey !== undefined;
|
const hasParent = v.parentKey !== undefined;
|
||||||
@@ -453,6 +540,7 @@ class EnhancedSelectInput extends Component {
|
|||||||
<OptionComponent
|
<OptionComponent
|
||||||
key={v.key}
|
key={v.key}
|
||||||
id={v.key}
|
id={v.key}
|
||||||
|
dividerAfter={v.dividerAfter}
|
||||||
depth={depth}
|
depth={depth}
|
||||||
isSelected={isSelectedItem(index, this.props)}
|
isSelected={isSelectedItem(index, this.props)}
|
||||||
isMultiSelect={isMultiSelect}
|
isMultiSelect={isMultiSelect}
|
||||||
@@ -483,12 +571,15 @@ EnhancedSelectInput.propTypes = {
|
|||||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.number)]).isRequired,
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.number)]).isRequired,
|
||||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
isDisabled: PropTypes.bool.isRequired,
|
||||||
|
isFetching: PropTypes.bool.isRequired,
|
||||||
|
isEditable: PropTypes.bool.isRequired,
|
||||||
hasError: PropTypes.bool,
|
hasError: PropTypes.bool,
|
||||||
hasWarning: PropTypes.bool,
|
hasWarning: PropTypes.bool,
|
||||||
valueOptions: PropTypes.object.isRequired,
|
valueOptions: PropTypes.object.isRequired,
|
||||||
selectedValueOptions: PropTypes.object.isRequired,
|
selectedValueOptions: PropTypes.object.isRequired,
|
||||||
selectedValueComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
|
selectedValueComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
|
||||||
optionComponent: PropTypes.elementType,
|
optionComponent: PropTypes.elementType,
|
||||||
|
onOpen: PropTypes.func,
|
||||||
onChange: PropTypes.func.isRequired
|
onChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -496,6 +587,8 @@ EnhancedSelectInput.defaultProps = {
|
|||||||
className: styles.enhancedSelect,
|
className: styles.enhancedSelect,
|
||||||
disabledClassName: styles.isDisabled,
|
disabledClassName: styles.isDisabled,
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
|
isFetching: false,
|
||||||
|
isEditable: false,
|
||||||
valueOptions: {},
|
valueOptions: {},
|
||||||
selectedValueOptions: {},
|
selectedValueOptions: {},
|
||||||
selectedValueComponent: HintedSelectInputSelectedValue,
|
selectedValueComponent: HintedSelectInputSelectedValue,
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { clearOptions, defaultState, fetchOptions } from 'Store/Actions/providerOptionActions';
|
||||||
|
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||||
|
|
||||||
|
const importantFieldNames = [
|
||||||
|
'baseUrl',
|
||||||
|
'apiPath',
|
||||||
|
'apiKey'
|
||||||
|
];
|
||||||
|
|
||||||
|
function getProviderDataKey(providerData) {
|
||||||
|
if (!providerData || !providerData.fields) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = providerData.fields
|
||||||
|
.filter((f) => importantFieldNames.includes(f.name))
|
||||||
|
.map((f) => f.value);
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectOptions(items) {
|
||||||
|
if (!items) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.map((option) => {
|
||||||
|
return {
|
||||||
|
key: option.value,
|
||||||
|
value: option.name,
|
||||||
|
hint: option.hint,
|
||||||
|
parentKey: option.parentValue
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state, { selectOptionsProviderAction }) => state.providerOptions[selectOptionsProviderAction] || defaultState,
|
||||||
|
(options) => {
|
||||||
|
if (options) {
|
||||||
|
return {
|
||||||
|
isFetching: options.isFetching,
|
||||||
|
values: getSelectOptions(options.items)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
dispatchFetchOptions: fetchOptions,
|
||||||
|
dispatchClearOptions: clearOptions
|
||||||
|
};
|
||||||
|
|
||||||
|
class EnhancedSelectInputConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
refetchRequired: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () => {
|
||||||
|
this._populate();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate = (prevProps) => {
|
||||||
|
const prevKey = getProviderDataKey(prevProps.providerData);
|
||||||
|
const nextKey = getProviderDataKey(this.props.providerData);
|
||||||
|
|
||||||
|
if (!_.isEqual(prevKey, nextKey)) {
|
||||||
|
this.setState({ refetchRequired: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount = () => {
|
||||||
|
this._cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onOpen = () => {
|
||||||
|
if (this.state.refetchRequired) {
|
||||||
|
this._populate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
_populate() {
|
||||||
|
const {
|
||||||
|
provider,
|
||||||
|
providerData,
|
||||||
|
selectOptionsProviderAction,
|
||||||
|
dispatchFetchOptions
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (selectOptionsProviderAction) {
|
||||||
|
this.setState({ refetchRequired: false });
|
||||||
|
dispatchFetchOptions({
|
||||||
|
section: selectOptionsProviderAction,
|
||||||
|
action: selectOptionsProviderAction,
|
||||||
|
provider,
|
||||||
|
providerData
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup() {
|
||||||
|
const {
|
||||||
|
selectOptionsProviderAction,
|
||||||
|
dispatchClearOptions
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (selectOptionsProviderAction) {
|
||||||
|
dispatchClearOptions({ section: selectOptionsProviderAction });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<EnhancedSelectInput
|
||||||
|
{...this.props}
|
||||||
|
onOpen={this.onOpen}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnhancedSelectInputConnector.propTypes = {
|
||||||
|
provider: PropTypes.string.isRequired,
|
||||||
|
providerData: PropTypes.object.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])).isRequired,
|
||||||
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
selectOptionsProviderAction: PropTypes.string,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
isFetching: PropTypes.bool.isRequired,
|
||||||
|
dispatchFetchOptions: PropTypes.func.isRequired,
|
||||||
|
dispatchClearOptions: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, mapDispatchToProps)(EnhancedSelectInputConnector);
|
||||||
@@ -54,4 +54,8 @@
|
|||||||
&:last-child {
|
&:last-child {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ class EnhancedSelectInputOption extends Component {
|
|||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onPress = () => {
|
onPress = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
onSelect
|
onSelect
|
||||||
@@ -32,6 +34,7 @@ class EnhancedSelectInputOption extends Component {
|
|||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
id,
|
id,
|
||||||
|
depth,
|
||||||
isSelected,
|
isSelected,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isHidden,
|
isHidden,
|
||||||
@@ -54,6 +57,11 @@ class EnhancedSelectInputOption extends Component {
|
|||||||
onPress={this.onPress}
|
onPress={this.onPress}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
{
|
||||||
|
depth !== 0 &&
|
||||||
|
<div style={{ width: `${depth * 20}px` }} />
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isMultiSelect &&
|
isMultiSelect &&
|
||||||
<CheckInput
|
<CheckInput
|
||||||
@@ -84,6 +92,7 @@ class EnhancedSelectInputOption extends Component {
|
|||||||
EnhancedSelectInputOption.propTypes = {
|
EnhancedSelectInputOption.propTypes = {
|
||||||
className: PropTypes.string.isRequired,
|
className: PropTypes.string.isRequired,
|
||||||
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
|
depth: PropTypes.number.isRequired,
|
||||||
isSelected: PropTypes.bool.isRequired,
|
isSelected: PropTypes.bool.isRequired,
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
isDisabled: PropTypes.bool.isRequired,
|
||||||
isHidden: PropTypes.bool.isRequired,
|
isHidden: PropTypes.bool.isRequired,
|
||||||
@@ -95,6 +104,7 @@ EnhancedSelectInputOption.propTypes = {
|
|||||||
|
|
||||||
EnhancedSelectInputOption.defaultProps = {
|
EnhancedSelectInputOption.defaultProps = {
|
||||||
className: styles.option,
|
className: styles.option,
|
||||||
|
depth: 0,
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
isMultiSelect: false
|
isMultiSelect: false
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import CaptchaInputConnector from './CaptchaInputConnector';
|
|||||||
import CheckInput from './CheckInput';
|
import CheckInput from './CheckInput';
|
||||||
import DeviceInputConnector from './DeviceInputConnector';
|
import DeviceInputConnector from './DeviceInputConnector';
|
||||||
import EnhancedSelectInput from './EnhancedSelectInput';
|
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||||
|
import EnhancedSelectInputConnector from './EnhancedSelectInputConnector';
|
||||||
import FormInputHelpText from './FormInputHelpText';
|
import FormInputHelpText from './FormInputHelpText';
|
||||||
import IndexerFlagsSelectInputConnector from './IndexerFlagsSelectInputConnector';
|
import IndexerFlagsSelectInputConnector from './IndexerFlagsSelectInputConnector';
|
||||||
import KeyValueListInput from './KeyValueListInput';
|
import KeyValueListInput from './KeyValueListInput';
|
||||||
|
import LanguageSelectInputConnector from './LanguageSelectInputConnector';
|
||||||
import MovieMonitoredSelectInput from './MovieMonitoredSelectInput';
|
import MovieMonitoredSelectInput from './MovieMonitoredSelectInput';
|
||||||
import NumberInput from './NumberInput';
|
import NumberInput from './NumberInput';
|
||||||
import OAuthInputConnector from './OAuthInputConnector';
|
import OAuthInputConnector from './OAuthInputConnector';
|
||||||
@@ -24,6 +26,7 @@ import TagSelectInputConnector from './TagSelectInputConnector';
|
|||||||
import TextArea from './TextArea';
|
import TextArea from './TextArea';
|
||||||
import TextInput from './TextInput';
|
import TextInput from './TextInput';
|
||||||
import TextTagInputConnector from './TextTagInputConnector';
|
import TextTagInputConnector from './TextTagInputConnector';
|
||||||
|
import UMaskInput from './UMaskInput';
|
||||||
import styles from './FormInputGroup.css';
|
import styles from './FormInputGroup.css';
|
||||||
|
|
||||||
function getComponent(type) {
|
function getComponent(type) {
|
||||||
@@ -70,9 +73,14 @@ function getComponent(type) {
|
|||||||
case inputTypes.INDEXER_FLAGS_SELECT:
|
case inputTypes.INDEXER_FLAGS_SELECT:
|
||||||
return IndexerFlagsSelectInputConnector;
|
return IndexerFlagsSelectInputConnector;
|
||||||
|
|
||||||
|
case inputTypes.LANGUAGE_SELECT:
|
||||||
|
return LanguageSelectInputConnector;
|
||||||
|
|
||||||
case inputTypes.SELECT:
|
case inputTypes.SELECT:
|
||||||
return EnhancedSelectInput;
|
return EnhancedSelectInput;
|
||||||
|
|
||||||
|
case inputTypes.DYNAMIC_SELECT:
|
||||||
|
return EnhancedSelectInputConnector;
|
||||||
case inputTypes.TAG:
|
case inputTypes.TAG:
|
||||||
return TagInputConnector;
|
return TagInputConnector;
|
||||||
|
|
||||||
@@ -85,6 +93,9 @@ function getComponent(type) {
|
|||||||
case inputTypes.TAG_SELECT:
|
case inputTypes.TAG_SELECT:
|
||||||
return TagSelectInputConnector;
|
return TagSelectInputConnector;
|
||||||
|
|
||||||
|
case inputTypes.UMASK:
|
||||||
|
return UMaskInput;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return TextInput;
|
return TextInput;
|
||||||
}
|
}
|
||||||
@@ -192,7 +203,7 @@ function FormInputGroup(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!checkInput && helpTextWarning &&
|
(!checkInput || helpText) && helpTextWarning &&
|
||||||
<FormInputHelpText
|
<FormInputHelpText
|
||||||
text={helpTextWarning}
|
text={helpTextWarning}
|
||||||
isWarning={true}
|
isWarning={true}
|
||||||
|
|||||||
@@ -21,3 +21,8 @@
|
|||||||
color: $darkGray;
|
color: $darkGray;
|
||||||
font-size: $smallFontSize;
|
font-size: $smallFontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid $lightGray;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,38 +9,49 @@ function HintedSelectInputOption(props) {
|
|||||||
id,
|
id,
|
||||||
value,
|
value,
|
||||||
hint,
|
hint,
|
||||||
|
depth,
|
||||||
isSelected,
|
isSelected,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
|
dividerAfter,
|
||||||
isMultiSelect,
|
isMultiSelect,
|
||||||
isMobile,
|
isMobile,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EnhancedSelectInputOption
|
<div>
|
||||||
id={id}
|
<EnhancedSelectInputOption
|
||||||
isSelected={isSelected}
|
id={id}
|
||||||
isDisabled={isDisabled}
|
depth={depth}
|
||||||
isHidden={isDisabled}
|
isSelected={isSelected}
|
||||||
isMultiSelect={isMultiSelect}
|
isDisabled={isDisabled}
|
||||||
isMobile={isMobile}
|
isHidden={isDisabled}
|
||||||
{...otherProps}
|
isMultiSelect={isMultiSelect}
|
||||||
>
|
isMobile={isMobile}
|
||||||
<div className={classNames(
|
{...otherProps}
|
||||||
styles.optionText,
|
|
||||||
isMobile && styles.isMobile
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<div>{value}</div>
|
<div className={classNames(
|
||||||
|
styles.optionText,
|
||||||
|
isMobile && styles.isMobile
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>{value}</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
hint != null &&
|
hint != null &&
|
||||||
<div className={styles.hintText}>
|
<div className={styles.hintText}>
|
||||||
{hint}
|
{hint}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</EnhancedSelectInputOption>
|
</EnhancedSelectInputOption>
|
||||||
|
|
||||||
|
{
|
||||||
|
dividerAfter ?
|
||||||
|
<div className={styles.divider} /> :
|
||||||
|
null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,14 +59,18 @@ HintedSelectInputOption.propTypes = {
|
|||||||
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
hint: PropTypes.node,
|
hint: PropTypes.node,
|
||||||
|
name: PropTypes.string,
|
||||||
|
depth: PropTypes.number,
|
||||||
isSelected: PropTypes.bool.isRequired,
|
isSelected: PropTypes.bool.isRequired,
|
||||||
isDisabled: PropTypes.bool.isRequired,
|
isDisabled: PropTypes.bool.isRequired,
|
||||||
|
dividerAfter: PropTypes.bool.isRequired,
|
||||||
isMultiSelect: PropTypes.bool.isRequired,
|
isMultiSelect: PropTypes.bool.isRequired,
|
||||||
isMobile: PropTypes.bool.isRequired
|
isMobile: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
HintedSelectInputOption.defaultProps = {
|
HintedSelectInputOption.defaultProps = {
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
|
dividerAfter: false,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
isMultiSelect: false
|
isMultiSelect: false
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -98,7 +98,9 @@ class KeyValueListInput extends Component {
|
|||||||
className,
|
className,
|
||||||
value,
|
value,
|
||||||
keyPlaceholder,
|
keyPlaceholder,
|
||||||
valuePlaceholder
|
valuePlaceholder,
|
||||||
|
hasError,
|
||||||
|
hasWarning
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { isFocused } = this.state;
|
const { isFocused } = this.state;
|
||||||
@@ -106,7 +108,9 @@ class KeyValueListInput extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className={classNames(
|
<div className={classNames(
|
||||||
className,
|
className,
|
||||||
isFocused && styles.isFocused
|
isFocused && styles.isFocused,
|
||||||
|
hasError && styles.hasError,
|
||||||
|
hasWarning && styles.hasWarning
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
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, { values }) => values,
|
||||||
|
( languages ) => {
|
||||||
|
|
||||||
|
const minId = languages.reduce((min, v) => (v.key < 1 ? v.key : min), languages[0].key);
|
||||||
|
|
||||||
|
const values = languages.map(({ key, value }) => {
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
dividerAfter: minId < 1 ? key === minId : false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LanguageSelectInputConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EnhancedSelectInput
|
||||||
|
{...this.props}
|
||||||
|
onChange={this.props.onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LanguageSelectInputConnector.propTypes = {
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]).isRequired,
|
||||||
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps)(LanguageSelectInputConnector);
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
import SelectInput from './SelectInput';
|
import SelectInput from './SelectInput';
|
||||||
|
|
||||||
const monitorTypesOptions = [
|
const monitorTypesOptions = [
|
||||||
{ key: 'true', value: 'True' },
|
{ key: 'true', value: translate('Yes') },
|
||||||
{ key: 'false', value: 'False' }
|
{ key: 'false', value: translate('No') }
|
||||||
];
|
];
|
||||||
|
|
||||||
function MovieMonitoredSelectInput(props) {
|
function MovieMonitoredSelectInput(props) {
|
||||||
|
|||||||
@@ -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, selectOptionsProviderAction }) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'captcha':
|
case 'captcha':
|
||||||
return inputTypes.CAPTCHA;
|
return inputTypes.CAPTCHA;
|
||||||
@@ -23,6 +23,9 @@ function getType(type, value) {
|
|||||||
case 'filePath':
|
case 'filePath':
|
||||||
return inputTypes.PATH;
|
return inputTypes.PATH;
|
||||||
case 'select':
|
case 'select':
|
||||||
|
if (selectOptionsProviderAction) {
|
||||||
|
return inputTypes.DYNAMIC_SELECT;
|
||||||
|
}
|
||||||
return inputTypes.SELECT;
|
return inputTypes.SELECT;
|
||||||
case 'tag':
|
case 'tag':
|
||||||
return inputTypes.TEXT_TAG;
|
return inputTypes.TEXT_TAG;
|
||||||
@@ -46,6 +49,7 @@ function getSelectValues(selectOptions) {
|
|||||||
result.push({
|
result.push({
|
||||||
key: option.value,
|
key: option.value,
|
||||||
value: option.name,
|
value: option.name,
|
||||||
|
dividerAfter: option.dividerAfter,
|
||||||
hint: option.hint
|
hint: option.hint
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -63,7 +67,6 @@ function ProviderFieldFormGroup(props) {
|
|||||||
value,
|
value,
|
||||||
type,
|
type,
|
||||||
advanced,
|
advanced,
|
||||||
requestAction,
|
|
||||||
hidden,
|
hidden,
|
||||||
pending,
|
pending,
|
||||||
errors,
|
errors,
|
||||||
@@ -88,7 +91,7 @@ function ProviderFieldFormGroup(props) {
|
|||||||
<FormLabel>{label}</FormLabel>
|
<FormLabel>{label}</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={getType(type, value)}
|
type={getType(props)}
|
||||||
name={name}
|
name={name}
|
||||||
label={label}
|
label={label}
|
||||||
helpText={helpText}
|
helpText={helpText}
|
||||||
@@ -100,7 +103,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>
|
||||||
@@ -109,7 +111,8 @@ function ProviderFieldFormGroup(props) {
|
|||||||
|
|
||||||
const selectOptionsShape = {
|
const selectOptionsShape = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
value: PropTypes.number.isRequired
|
value: PropTypes.number.isRequired,
|
||||||
|
hint: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
ProviderFieldFormGroup.propTypes = {
|
ProviderFieldFormGroup.propTypes = {
|
||||||
@@ -121,12 +124,12 @@ 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,
|
||||||
warnings: PropTypes.arrayOf(PropTypes.object).isRequired,
|
warnings: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
selectOptions: PropTypes.arrayOf(PropTypes.shape(selectOptionsShape)),
|
selectOptions: PropTypes.arrayOf(PropTypes.shape(selectOptionsShape)),
|
||||||
|
selectOptionsProviderAction: PropTypes.string,
|
||||||
onChange: PropTypes.func.isRequired
|
onChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,16 @@ const ADD_NEW_KEY = 'addNew';
|
|||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.rootFolders,
|
(state) => state.rootFolders,
|
||||||
|
(state, { value }) => value,
|
||||||
|
(state, { includeMissingValue }) => includeMissingValue,
|
||||||
(state, { includeNoChange }) => includeNoChange,
|
(state, { includeNoChange }) => includeNoChange,
|
||||||
(rootFolders, includeNoChange) => {
|
(rootFolders, value, includeMissingValue, includeNoChange) => {
|
||||||
const values = rootFolders.items.map((rootFolder) => {
|
const values = rootFolders.items.map((rootFolder) => {
|
||||||
return {
|
return {
|
||||||
key: rootFolder.path,
|
key: rootFolder.path,
|
||||||
value: rootFolder.path,
|
value: rootFolder.path,
|
||||||
freeSpace: rootFolder.freeSpace
|
freeSpace: rootFolder.freeSpace,
|
||||||
|
isMissing: false
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -24,7 +27,8 @@ function createMapStateToProps() {
|
|||||||
values.unshift({
|
values.unshift({
|
||||||
key: 'noChange',
|
key: 'noChange',
|
||||||
value: 'No Change',
|
value: 'No Change',
|
||||||
isDisabled: true
|
isDisabled: true,
|
||||||
|
isMissing: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +41,15 @@ function createMapStateToProps() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includeMissingValue && !values.find((v) => v.key === value)) {
|
||||||
|
values.push({
|
||||||
|
key: value,
|
||||||
|
value,
|
||||||
|
isMissing: true,
|
||||||
|
isDisabled: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
values.push({
|
values.push({
|
||||||
key: ADD_NEW_KEY,
|
key: ADD_NEW_KEY,
|
||||||
value: 'Add a new path'
|
value: 'Add a new path'
|
||||||
@@ -151,7 +164,8 @@ RootFolderSelectInputConnector.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
RootFolderSelectInputConnector.defaultProps = {
|
RootFolderSelectInputConnector.defaultProps = {
|
||||||
includeNoChange: false
|
includeNoChange: false,
|
||||||
|
value: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(RootFolderSelectInputConnector);
|
export default connect(createMapStateToProps, createMapDispatchToProps)(RootFolderSelectInputConnector);
|
||||||
|
|||||||
@@ -29,3 +29,9 @@
|
|||||||
color: $darkGray;
|
color: $darkGray;
|
||||||
font-size: $smallFontSize;
|
font-size: $smallFontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.isMissing {
|
||||||
|
margin-left: 15px;
|
||||||
|
color: $dangerColor;
|
||||||
|
font-size: $smallFontSize;
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ function RootFolderSelectInputOption(props) {
|
|||||||
id,
|
id,
|
||||||
value,
|
value,
|
||||||
freeSpace,
|
freeSpace,
|
||||||
|
isMissing,
|
||||||
movieFolder,
|
movieFolder,
|
||||||
isMobile,
|
isMobile,
|
||||||
isWindows,
|
isWindows,
|
||||||
@@ -43,11 +44,20 @@ function RootFolderSelectInputOption(props) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
freeSpace != null &&
|
freeSpace == null ?
|
||||||
|
null :
|
||||||
<div className={styles.freeSpace}>
|
<div className={styles.freeSpace}>
|
||||||
{formatBytes(freeSpace)} Free
|
{formatBytes(freeSpace)} Free
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isMissing ?
|
||||||
|
<div className={styles.isMissing}>
|
||||||
|
Missing
|
||||||
|
</div> :
|
||||||
|
null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</EnhancedSelectInputOption>
|
</EnhancedSelectInputOption>
|
||||||
);
|
);
|
||||||
@@ -58,6 +68,7 @@ RootFolderSelectInputOption.propTypes = {
|
|||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
freeSpace: PropTypes.number,
|
freeSpace: PropTypes.number,
|
||||||
movieFolder: PropTypes.string,
|
movieFolder: PropTypes.string,
|
||||||
|
isMissing: PropTypes.boolean,
|
||||||
isMobile: PropTypes.bool.isRequired,
|
isMobile: PropTypes.bool.isRequired,
|
||||||
isWindows: PropTypes.bool
|
isWindows: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
.inputWrapper {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputFolder {
|
||||||
|
composes: input from '~Components/Form/Input.css';
|
||||||
|
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputUnitWrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputUnit {
|
||||||
|
composes: inputUnit from '~Components/Form/FormInputGroup.css';
|
||||||
|
|
||||||
|
right: 40px;
|
||||||
|
font-family: $monoSpaceFontFamily;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-family: $monoSpaceFontFamily;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-left: 17px;
|
||||||
|
line-height: 20px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
label {
|
||||||
|
flex: 0 0 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
width: 50px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
width: 90px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.readOnly {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/* eslint-disable no-bitwise */
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||||
|
import styles from './UMaskInput.css';
|
||||||
|
|
||||||
|
const umaskOptions = [
|
||||||
|
{
|
||||||
|
key: '755',
|
||||||
|
value: '755 - Owner write, Everyone else read',
|
||||||
|
hint: 'drwxr-xr-x'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '775',
|
||||||
|
value: '775 - Owner & Group write, Other read',
|
||||||
|
hint: 'drwxrwxr-x'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '770',
|
||||||
|
value: '770 - Owner & Group write',
|
||||||
|
hint: 'drwxrwx---'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '750',
|
||||||
|
value: '750 - Owner write, Group read',
|
||||||
|
hint: 'drwxr-x---'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '777',
|
||||||
|
value: '777 - Everyone write',
|
||||||
|
hint: 'drwxrwxrwx'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function formatPermissions(permissions) {
|
||||||
|
|
||||||
|
const hasSticky = permissions & 0o1000;
|
||||||
|
const hasSetGID = permissions & 0o2000;
|
||||||
|
const hasSetUID = permissions & 0o4000;
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < 9; i++) {
|
||||||
|
const bit = (permissions & (1 << i)) !== 0;
|
||||||
|
let digit = bit ? 'xwr'[i % 3] : '-';
|
||||||
|
if (i === 6 && hasSetUID) {
|
||||||
|
digit = bit ? 's' : 'S';
|
||||||
|
} else if (i === 3 && hasSetGID) {
|
||||||
|
digit = bit ? 's' : 'S';
|
||||||
|
} else if (i === 0 && hasSticky) {
|
||||||
|
digit = bit ? 't' : 'T';
|
||||||
|
}
|
||||||
|
result = digit + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UMaskInput extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
onChange
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const valueNum = parseInt(value, 8);
|
||||||
|
const umaskNum = 0o777 & ~valueNum;
|
||||||
|
const umask = umaskNum.toString(8).padStart(4, '0');
|
||||||
|
const folderNum = 0o777 & ~umaskNum;
|
||||||
|
const folder = folderNum.toString(8).padStart(3, '0');
|
||||||
|
const fileNum = 0o666 & ~umaskNum;
|
||||||
|
const file = fileNum.toString(8).padStart(3, '0');
|
||||||
|
|
||||||
|
const unit = formatPermissions(folderNum);
|
||||||
|
|
||||||
|
const values = umaskOptions.map((v) => {
|
||||||
|
return { ...v, hint: <span className={styles.unit}>{v.hint}</span> };
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={styles.inputWrapper}>
|
||||||
|
<div className={styles.inputUnitWrapper}>
|
||||||
|
<EnhancedSelectInput
|
||||||
|
name={name}
|
||||||
|
value={value}
|
||||||
|
values={values}
|
||||||
|
isEditable={true}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={styles.inputUnit}>
|
||||||
|
d{unit}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.details}>
|
||||||
|
<div>
|
||||||
|
<label>UMask</label>
|
||||||
|
<div className={styles.value}>{umask}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Folder</label>
|
||||||
|
<div className={styles.value}>{folder}</div>
|
||||||
|
<div className={styles.unit}>d{formatPermissions(folderNum)}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>File</label>
|
||||||
|
<div className={styles.value}>{file}</div>
|
||||||
|
<div className={styles.unit}>{formatPermissions(fileNum)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UMaskInput.propTypes = {
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.string.isRequired,
|
||||||
|
hasError: PropTypes.bool,
|
||||||
|
hasWarning: PropTypes.bool,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
onFocus: PropTypes.func,
|
||||||
|
onBlur: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UMaskInput;
|
||||||
@@ -19,6 +19,10 @@
|
|||||||
&.outline {
|
&.outline {
|
||||||
color: $dangerColor;
|
color: $dangerColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:global(.colorImpaired) {
|
||||||
|
background: repeating-linear-gradient(90deg, color($dangerColor shade(5%)), color($dangerColor shade(5%)) 5px, color($dangerColor shade(15%)) 5px, color($dangerColor shade(15%)) 10px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.default {
|
.default {
|
||||||
@@ -85,6 +89,10 @@
|
|||||||
&.outline {
|
&.outline {
|
||||||
color: $warningColor;
|
color: $warningColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:global(.colorImpaired) {
|
||||||
|
background: repeating-linear-gradient(45deg, $warningColor, $warningColor 5px, color($warningColor tint(15%)) 5px, color($warningColor tint(15%)) 10px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.queue {
|
.queue {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ function Label(props) {
|
|||||||
size,
|
size,
|
||||||
outline,
|
outline,
|
||||||
children,
|
children,
|
||||||
|
colorImpairedMode,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -20,7 +21,8 @@ function Label(props) {
|
|||||||
className,
|
className,
|
||||||
styles[kind],
|
styles[kind],
|
||||||
styles[size],
|
styles[size],
|
||||||
outline && styles.outline
|
outline && styles.outline,
|
||||||
|
colorImpairedMode && 'colorImpaired'
|
||||||
)}
|
)}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
@@ -34,14 +36,16 @@ Label.propTypes = {
|
|||||||
kind: PropTypes.oneOf(kinds.all).isRequired,
|
kind: PropTypes.oneOf(kinds.all).isRequired,
|
||||||
size: PropTypes.oneOf(sizes.all).isRequired,
|
size: PropTypes.oneOf(sizes.all).isRequired,
|
||||||
outline: PropTypes.bool.isRequired,
|
outline: PropTypes.bool.isRequired,
|
||||||
children: PropTypes.node.isRequired
|
children: PropTypes.node.isRequired,
|
||||||
|
colorImpairedMode: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
Label.defaultProps = {
|
Label.defaultProps = {
|
||||||
className: styles.label,
|
className: styles.label,
|
||||||
kind: kinds.DEFAULT,
|
kind: kinds.DEFAULT,
|
||||||
size: sizes.SMALL,
|
size: sizes.SMALL,
|
||||||
outline: false
|
outline: false,
|
||||||
|
colorImpairedMode: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Label;
|
export default Label;
|
||||||
|
|||||||
@@ -38,24 +38,33 @@ class Link extends Component {
|
|||||||
const linkProps = { target };
|
const linkProps = { target };
|
||||||
let el = component;
|
let el = component;
|
||||||
|
|
||||||
if (to) {
|
if (to && typeof to === 'string') {
|
||||||
if ((/\w+?:\/\//).test(to)) {
|
if ((/\w+?:\/\//).test(to)) {
|
||||||
el = 'a';
|
el = 'a';
|
||||||
linkProps.href = to;
|
linkProps.href = to;
|
||||||
linkProps.target = target || '_blank';
|
linkProps.target = target || '_blank';
|
||||||
|
linkProps.rel = 'noreferrer';
|
||||||
} else if (noRouter) {
|
} else if (noRouter) {
|
||||||
el = 'a';
|
el = 'a';
|
||||||
linkProps.href = to;
|
linkProps.href = to;
|
||||||
linkProps.target = target || '_self';
|
linkProps.target = target || '_self';
|
||||||
} else if (to.startsWith(`${window.Radarr.urlBase}/`)) {
|
|
||||||
el = RouterLink;
|
|
||||||
linkProps.to = to;
|
|
||||||
linkProps.target = target;
|
|
||||||
} else {
|
} else {
|
||||||
el = RouterLink;
|
el = RouterLink;
|
||||||
linkProps.to = `${window.Radarr.urlBase}/${to.replace(/^\//, '')}`;
|
linkProps.to = `${window.Radarr.urlBase}/${to.replace(/^\//, '')}`;
|
||||||
linkProps.target = target;
|
linkProps.target = target;
|
||||||
}
|
}
|
||||||
|
} else if (to && typeof to === 'object') {
|
||||||
|
el = RouterLink;
|
||||||
|
linkProps.target = target;
|
||||||
|
if (to.pathname.startsWith(`${window.Radarr.urlBase}/`)) {
|
||||||
|
linkProps.to = to;
|
||||||
|
} else {
|
||||||
|
const pathname = `${window.Radarr.urlBase}/${to.pathname.replace(/^\//, '')}`;
|
||||||
|
linkProps.to = {
|
||||||
|
...to,
|
||||||
|
pathname
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el === 'button' || el === 'input') {
|
if (el === 'button' || el === 'input') {
|
||||||
@@ -86,7 +95,7 @@ class Link extends Component {
|
|||||||
Link.propTypes = {
|
Link.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||||
to: PropTypes.string,
|
to: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||||
target: PropTypes.string,
|
target: PropTypes.string,
|
||||||
isDisabled: PropTypes.bool,
|
isDisabled: PropTypes.bool,
|
||||||
noRouter: PropTypes.bool,
|
noRouter: PropTypes.bool,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.loadingMessage {
|
.loadingMessage {
|
||||||
margin: 50px 10px 0;
|
margin: 10px 10px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const messages = [
|
|||||||
'Hum something loud while others stare',
|
'Hum something loud while others stare',
|
||||||
'Loading humorous message... Please Wait',
|
'Loading humorous message... Please Wait',
|
||||||
'I could\'ve been faster in Python',
|
'I could\'ve been faster in Python',
|
||||||
'Don\'t forget to rewind your tracks',
|
'Don\'t forget to rewind your movies',
|
||||||
'Congratulations! you are the 1000th visitor.',
|
'Congratulations! you are the 1000th visitor.',
|
||||||
'HELP! I\'m being held hostage and forced to write these stupid lines!',
|
'HELP! I\'m being held hostage and forced to write these stupid lines!',
|
||||||
'RE-calibrating the internet...',
|
'RE-calibrating the internet...',
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ function ConfirmModal(props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
bindShortcut('enter', onConfirm);
|
bindShortcut('enter', onConfirm);
|
||||||
} else {
|
|
||||||
unbindShortcut('enter', onConfirm);
|
return () => unbindShortcut('enter', onConfirm);
|
||||||
}
|
}
|
||||||
}, [onConfirm]);
|
}, [isOpen, onConfirm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@@ -53,7 +53,13 @@ class PageHeader extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className={styles.logoContainer}>
|
<div className={styles.logoContainer}>
|
||||||
<Link to={`${window.Radarr.urlBase}/`}>
|
<Link
|
||||||
|
className={styles.logoLink}
|
||||||
|
to={{
|
||||||
|
pathname: '/',
|
||||||
|
state: { restoreScrollPosition: true }
|
||||||
|
}}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
className={isSmallScreen ? styles.logo : styles.logoFull}
|
className={isSmallScreen ? styles.logo : styles.logoFull}
|
||||||
src={isSmallScreen ? `${window.Radarr.urlBase}/Content/Images/logo.png` : `${window.Radarr.urlBase}/Content/Images/logo-full.png`}
|
src={isSmallScreen ? `${window.Radarr.urlBase}/Content/Images/logo.png` : `${window.Radarr.urlBase}/Content/Images/logo-full.png`}
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
.page {
|
.page {
|
||||||
composes: page from '~./Page.css';
|
composes: page from '~./Page.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logoFull {
|
||||||
|
margin-top: 50px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
width: 120px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user