Merge branch 'details-in-card-and-add-dialog-title-change'

This commit is contained in:
Daniel Shleifman
2021-06-17 16:45:24 +03:00
35 changed files with 712 additions and 191 deletions

View File

@@ -8,7 +8,8 @@
> `http://192.168.78.4:8090` - correct > `http://192.168.78.4:8090` - correct
> >
> `http://192.168.78.4:8090/` - wrong > `http://192.168.78.4:8090/` - wrong
3. `yarn start` 3. in `.env` file add TMDB api key
4. `yarn start`
### Eslint ### Eslint
> Prettier will fix the code every time the code is saved > Prettier will fix the code every time the code is saved

View File

@@ -7,7 +7,6 @@
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"axios": "^0.21.1", "axios": "^0.21.1",
"clsx": "^1.1.1", "clsx": "^1.1.1",
"fontsource-roboto": "^4.0.0",
"i18next": "^20.3.1", "i18next": "^20.3.1",
"i18next-browser-languagedetector": "^6.1.1", "i18next-browser-languagedetector": "^6.1.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
web/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,265 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="1200.000000pt" height="1200.000000pt" viewBox="0 0 1200.000000 1200.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,1200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M5719 11396 c-2 -2 -58 -7 -124 -10 -66 -4 -127 -9 -135 -11 -8 -2
-42 -7 -75 -10 -703 -70 -1491 -334 -2125 -710 -473 -281 -931 -656 -1281
-1051 -95 -106 -113 -127 -175 -204 -565 -701 -934 -1492 -1110 -2385 -4 -19
-31 -182 -39 -235 -10 -60 -35 -293 -42 -385 -16 -198 -8 -823 12 -923 2 -11
7 -53 10 -94 4 -40 8 -75 10 -78 2 -3 6 -36 10 -75 3 -38 8 -72 10 -75 1 -3 6
-27 9 -54 4 -28 14 -82 22 -120 9 -39 18 -84 21 -101 2 -16 25 -111 50 -210
146 -582 397 -1146 736 -1655 78 -117 230 -326 287 -393 10 -12 44 -53 76 -92
130 -156 353 -388 494 -515 265 -237 517 -428 803 -605 107 -67 154 -101 145
-107 -7 -4 -9 -8 -4 -8 5 0 -1 -11 -14 -25 -13 -14 -19 -25 -14 -25 5 0 1 -5
-9 -11 -9 -6 -15 -14 -12 -19 3 -4 -2 -11 -11 -14 -9 -4 -14 -13 -10 -21 3 -9
0 -15 -9 -15 -8 0 -14 -3 -13 -8 3 -11 -23 -55 -43 -73 -10 -9 -15 -19 -11
-23 4 -4 -1 -12 -11 -17 -9 -6 -15 -14 -12 -19 3 -4 -2 -11 -11 -14 -8 -3 -13
-10 -9 -15 3 -6 -3 -14 -12 -20 -10 -6 -13 -11 -8 -11 6 0 3 -5 -5 -10 -8 -5
-15 -15 -15 -22 -1 -7 -6 -12 -12 -11 -6 1 -9 -6 -5 -19 3 -14 1 -19 -6 -14
-8 5 -12 -2 -12 -18 0 -14 3 -26 8 -26 12 1 72 59 72 70 0 6 4 9 9 6 5 -3 19
10 32 28 13 19 27 32 31 30 4 -3 8 -1 8 4 0 15 208 221 216 214 4 -4 4 1 1 11
-4 11 -3 16 3 12 6 -3 10 -1 10 4 0 21 34 10 168 -57 580 -289 1207 -469 1869
-537 154 -15 252 -19 518 -20 297 0 430 6 595 26 47 5 103 12 125 15 194 22
496 81 690 135 39 11 86 24 105 29 346 96 782 278 1127 471 1188 665 2073
1735 2502 3027 97 292 181 648 216 912 3 25 8 56 10 70 4 23 13 104 21 190 14
145 19 283 19 530 0 334 -8 458 -46 730 -25 183 -28 204 -46 285 -8 39 -17 84
-20 100 -2 17 -14 66 -25 110 -11 44 -22 90 -24 103 -3 13 -24 92 -49 175
-181 624 -470 1200 -869 1732 -49 66 -92 122 -95 125 -4 3 -22 25 -41 50 -19
25 -49 61 -66 80 -17 19 -55 62 -84 95 -293 335 -743 718 -1130 962 -91 57
-305 183 -362 212 -21 11 -25 18 -18 31 5 10 7 20 3 23 -3 4 2 9 11 13 9 3 14
10 11 14 -3 4 2 11 11 14 8 3 13 10 10 15 -3 5 1 12 10 15 8 3 12 10 9 16 -3
5 -1 10 4 10 6 0 10 3 9 8 -4 10 68 123 75 116 3 -3 5 1 4 8 -1 32 4 49 13 43
5 -3 12 6 16 20 3 14 10 25 15 25 5 0 8 6 7 13 -2 6 2 11 8 10 7 -1 10 6 7 17
-3 11 0 20 6 20 6 0 11 9 11 19 0 14 -6 19 -20 18 -11 -1 -20 -5 -20 -10 0 -4
-4 -6 -9 -3 -5 3 -8 -2 -7 -12 0 -9 -5 -16 -11 -14 -7 1 -13 -3 -13 -10 0 -11
-24 -38 -152 -169 -32 -33 -58 -64 -58 -69 0 -6 -4 -10 -8 -10 -5 0 -17 -7
-28 -16 -10 -8 -13 -12 -5 -8 10 5 12 3 7 -5 -5 -7 -15 -10 -23 -7 -8 3 -11 2
-8 -4 3 -5 -2 -18 -11 -28 -16 -19 -19 -18 -148 39 -72 33 -140 64 -151 69
-144 67 -624 228 -757 254 -13 2 -70 16 -128 30 -198 47 -471 91 -690 112 -36
3 -85 8 -110 11 -56 6 -686 14 -691 9z m602 -1203 c13 -16 12 -17 -3 -4 -10 7
-18 15 -18 17 0 8 8 3 21 -13z m844 -102 c3 -5 2 -12 -3 -15 -5 -3 -9 1 -9 9
0 17 3 19 12 6z m-644 -98 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3
21 -13z m-1670 -70 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z
m-201 -3 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z
m320 0 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11
-10z m50 -74 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m425 -75 c3 -5 2 -12 -3 -15 -5 -3 -9 1 -9 9 0 17 3 19 12 6z m125 5 c0 -2 -8
-10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-520 -18 c0 -4 -4 -8
-9 -8 -6 0 -12 4 -15 8 -3 5 1 9 9 9 8 0 15 -4 15 -9z m3157 -3 c0 -8 -4 -12
-9 -9 -5 3 -6 10 -3 15 9 13 12 11 12 -6z m-2526 -42 c13 -16 12 -17 -3 -4
-10 7 -18 15 -18 17 0 8 8 3 21 -13z m-551 -153 c0 -5 -5 -10 -11 -10 -5 0 -7
5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m330 6 c0 -2 -8 -10 -17 -17 -16 -13
-17 -12 -4 4 13 16 21 21 21 13z m2385 -14 c-3 -3 -11 0 -18 7 -9 10 -8 11 6
5 10 -3 15 -9 12 -12z m481 -27 c-1 -8 -5 -17 -8 -21 -5 -4 -4 16 1 34 3 9 9
-1 7 -13z m-3202 -131 c3 -8 2 -12 -4 -9 -6 3 -10 10 -10 16 0 14 7 11 14 -7z
m606 -258 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m-1769 -23 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m219 11
c0 -8 -19 -13 -24 -6 -3 5 1 9 9 9 8 0 15 -2 15 -3z m1341 -51 c13 -16 12 -17
-3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m2880 0 c13 -16 12 -17 -3 -4 -10
7 -18 15 -18 17 0 8 8 3 21 -13z m-4760 -40 c13 -16 12 -17 -3 -4 -10 7 -18
15 -18 17 0 8 8 3 21 -13z m530 -30 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17
0 8 8 3 21 -13z m2079 -43 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4
10 6 0 11 -4 11 -10z m1671 -47 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8
8 3 21 -13z m-3791 -17 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21
21 21 13z m730 -10 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21
13z m-2089 -163 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z
m1840 0 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m160 0 c13
-16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m160 0 c13 -16 12 -17
-3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m160 0 c13 -16 12 -17 -3 -4 -10 7
-18 15 -18 17 0 8 8 3 21 -13z m3480 -40 c13 -16 12 -17 -3 -4 -10 7 -18 15
-18 17 0 8 8 3 21 -13z m-81 -37 c0 -3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6
5 11 10 11 6 0 10 -2 10 -4z m475 -134 c-3 -3 -11 0 -18 7 -9 10 -8 11 6 5 10
-3 15 -9 12 -12z m46 -29 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3
21 -13z m-1637 -1 c3 -5 -1 -9 -9 -9 -8 0 -12 4 -9 9 3 4 7 8 9 8 2 0 6 -4 9
-8z m-1443 -39 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z
m3289 3 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m240 0
c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-2139 -53 c13
-16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m1169 -73 c0 -5 -2 -10
-4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m320 0 c0 -5 -2
-10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m640 0 c0 -5
-5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m-89 -47 c13
-16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-387 -71 c3 -5 -1 -9
-9 -9 -8 0 -12 4 -9 9 3 4 7 8 9 8 2 0 6 -4 9 -8z m-5893 -55 c-10 -9 -11 -8
-5 6 3 10 9 15 12 12 3 -3 0 -11 -7 -18z m169 19 c0 -3 -4 -8 -10 -11 -5 -3
-10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2 10 -4z m311 -13 c13 -16 12 -17 -3 -4
-10 7 -18 15 -18 17 0 8 8 3 21 -13z m334 7 c-3 -5 -12 -10 -18 -10 -7 0 -6 4
3 10 19 12 23 12 15 0z m3685 -74 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4
13 16 21 21 21 13z m748 -51 c-3 -3 -9 2 -12 12 -6 14 -5 15 5 6 7 -7 10 -15
7 -18z m323 8 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z
m-641 -3 c0 -5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10
-10z m1641 -37 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z
m-201 -3 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z
m370 -74 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m-6930 -82 c0 -8 -19 -13 -24 -6 -3 5 1 9 9 9 8 0 15 -2 15 -3z m880 2 c0 -2
-7 -7 -16 -10 -8 -3 -12 -2 -9 4 6 10 25 14 25 6z m325 -5 c-9 -8 -36 -10 -30
-2 2 4 11 8 20 8 8 0 13 -3 10 -6z m280 -1 c3 -6 -1 -7 -9 -4 -18 7 -21 14 -7
14 6 0 13 -4 16 -10z m4270 1 c3 -5 2 -12 -3 -15 -5 -3 -9 1 -9 9 0 17 3 19
12 6z m-6545 -35 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21
13z m1951 -43 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z
m4230 -10 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-4991
-17 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m80 -56 c0
-5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m320 0
c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m210 6
c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m110 -6 c0 -5
-5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m3130 6 c0 -2
-8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m1115 -5 c-9 -8 -36
-10 -30 -2 2 4 11 8 20 8 8 0 13 -3 10 -6z m-6124 -78 c13 -16 12 -17 -3 -4
-10 7 -18 15 -18 17 0 8 8 3 21 -13z m679 -27 c0 -2 -8 -10 -17 -17 -16 -13
-17 -12 -4 4 13 16 21 21 21 13z m561 -13 c13 -16 12 -17 -3 -4 -10 7 -18 15
-18 17 0 8 8 3 21 -13z m5399 -27 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4
13 16 21 21 21 13z m-120 -12 c0 -8 -19 -13 -24 -6 -3 5 1 9 9 9 8 0 15 -2 15
-3z m400 -2 c0 -5 -7 -9 -15 -9 -8 0 -12 4 -9 9 3 4 9 8 15 8 5 0 9 -4 9 -8z
m325 -1 c-9 -8 -36 -10 -30 -2 2 4 11 8 20 8 8 0 13 -3 10 -6z m-5915 -35 c0
-2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m5421 -13 c13 -16
12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m130 -30 c13 -16 12 -17 -3
-4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-5006 -83 c3 -5 1 -10 -4 -10 -6 0
-11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m5925 -120 c0 -5 -2 -10 -4 -10
-3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m-7683 -35 c0 -8 -4 -12
-9 -9 -5 3 -6 10 -3 15 9 13 12 11 12 -6z m7008 -5 c3 -6 -1 -7 -9 -4 -18 7
-21 14 -7 14 6 0 13 -4 16 -10z m510 -160 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11
10 0 6 2 10 4 10 3 0 8 -4 11 -10z m320 -8 c-3 -3 -11 0 -18 7 -9 10 -8 11 6
5 10 -3 15 -9 12 -12z m-7844 -39 c13 -16 12 -17 -3 -4 -17 13 -22 21 -14 21
2 0 10 -8 17 -17z m5680 0 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3
21 -13z m-186 -113 c-3 -5 -12 -10 -18 -10 -7 0 -6 4 3 10 19 12 23 12 15 0z
m285 -34 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m-5970 -30 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m1481 -53 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-1446
-43 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z
m116 -7 c13 -16 12 -17 -3 -4 -17 13 -22 21 -14 21 2 0 10 -8 17 -17z m209 7
c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m5551
-87 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-4511 -17 c0
-2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-839 -53 c13 -16
12 -17 -3 -4 -17 13 -22 21 -14 21 2 0 10 -8 17 -17z m6676 2 c0 -8 -4 -12 -9
-9 -5 3 -6 10 -3 15 9 13 12 11 12 -6z m-5762 -4 c3 -5 2 -12 -3 -15 -5 -3 -9
1 -9 9 0 17 3 19 12 6z m-705 -35 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4
13 16 21 21 21 13z m6560 0 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16
21 21 21 13z m-4376 -55 c-2 -3 -12 3 -22 13 -16 17 -16 18 5 5 12 -7 20 -15
17 -18z m4886 25 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21
13z m-5440 -90 c0 -3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10
-2 10 -4z m4290 -40 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21
21 13z m541 -13 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z
m650 -70 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-5566
-51 c-3 -3 -11 0 -18 7 -9 10 -8 11 6 5 10 -3 15 -9 12 -12z m3245 6 c0 -4 -4
-8 -9 -8 -6 0 -12 4 -15 8 -3 5 1 9 9 9 8 0 15 -4 15 -9z m-5889 -85 c13 -16
12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m5329 11 c0 -8 -19 -13 -24
-6 -3 5 1 9 9 9 8 0 15 -2 15 -3z m1040 -28 c0 -2 -8 -10 -17 -17 -16 -13 -17
-12 -4 4 13 16 21 21 21 13z m-2960 -16 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10
3 6 8 10 11 10 2 0 4 -4 4 -10z m3040 -40 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11
10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m-4329 -53 c-10 -9 -11 -8 -5 6 3 10 9
15 12 12 3 -3 0 -11 -7 -18z m4330 -64 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18
17 0 8 8 3 21 -13z m-5928 -78 c-3 -9 -8 -14 -10 -11 -3 3 -2 9 2 15 9 16 15
13 8 -4z m-482 -12 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z
m5924 7 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z
m1685 -64 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m-7605 -56 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11
-10z m965 0 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4
-10z m6720 0 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4
11 -10z m320 0 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4
4 -10z m320 6 c0 -3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10
-2 10 -4z m-8230 -40 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21
21 13z m1760 0 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m-1694 -52 c1 -9 0 -13 -4 -10 -3 4 -7 14 -8 22 -1 9 0 13 4 10 3 -4 7 -14 8
-22z m2560 11 c-1 -8 -5 -17 -8 -21 -5 -4 -4 16 1 34 3 9 9 -1 7 -13z m-2476
-29 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m7441 -13
c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-6730 -90 c13 -16
12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m1779 13 c0 -2 -8 -10 -17
-17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-2520 -40 c0 -2 -8 -10 -17 -17
-16 -13 -17 -12 -4 4 13 16 21 21 21 13z m745 -86 c3 -5 1 -10 -4 -10 -6 0
-11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m-804 -47 c13 -16 12 -17 -3 -4
-10 7 -18 15 -18 17 0 8 8 3 21 -13z m899 13 c0 -2 -8 -10 -17 -17 -16 -13
-17 -12 -4 4 13 16 21 21 21 13z m4541 -19 c-10 -9 -11 -8 -5 6 3 10 9 15 12
12 3 -3 0 -11 -7 -18z m-3711 -11 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4
13 16 21 21 21 13z m791 -23 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8
3 21 -13z m2894 7 c3 -6 -1 -7 -9 -4 -18 7 -21 14 -7 14 6 0 13 -4 16 -10z
m146 -7 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m99 13 c0
-2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m1030 -86 c0 -5
-2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m1320 6 c0
-3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2 10 -4z m320 0
c0 -3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2 10 -4z
m-1679 -123 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m639
13 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-720 -50
c0 -3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2 10 -4z
m-639 -83 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m553 -9
c3 -8 2 -12 -4 -9 -6 3 -10 10 -10 16 0 14 7 11 14 -7z m1446 -18 c0 -2 -8
-10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-2350 -50 c0 -2 -8 -10
-17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m1750 -70 c0 -2 -8 -10 -17
-17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-559 -93 c13 -16 12 -17 -3 -4
-10 7 -18 15 -18 17 0 8 8 3 21 -13z m-597 -1 c3 -5 -1 -9 -9 -9 -8 0 -12 4
-9 9 3 4 7 8 9 8 2 0 6 -4 9 -8z m396 -2 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4
10 3 6 8 10 11 10 2 0 4 -4 4 -10z m320 0 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11
10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m50 -74 c0 -2 -8 -10 -17 -17 -16 -13
-17 -12 -4 4 13 16 21 21 21 13z m-1690 -50 c0 -2 -8 -10 -17 -17 -16 -13 -17
-12 -4 4 13 16 21 21 21 13z m-3640 -32 c0 -8 -19 -13 -24 -6 -3 5 1 9 9 9 8
0 15 -2 15 -3z m634 -42 c3 -5 -1 -9 -9 -9 -8 0 -12 4 -9 9 3 4 7 8 9 8 2 0 6
-4 9 -8z m4326 -122 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0
4 -4 4 -10z m-6160 -104 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21
21 21 13z m1120 0 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21
13z m-230 -90 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m3231 -43 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m1020
-160 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-4310 -10
c13 -16 12 -17 -3 -4 -17 13 -22 21 -14 21 2 0 10 -8 17 -17z m3086 -68 c0 -8
-4 -12 -9 -9 -5 3 -6 10 -3 15 9 13 12 11 12 -6z m3040 0 c0 -8 -4 -12 -9 -9
-5 3 -6 10 -3 15 9 13 12 11 12 -6z m-323 -3 c3 -5 -1 -9 -9 -9 -8 0 -12 4 -9
9 3 4 7 8 9 8 2 0 6 -4 9 -8z m-1273 -119 c13 -16 12 -17 -3 -4 -10 7 -18 15
-18 17 0 8 8 3 21 -13z m1590 -90 c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0
8 8 3 21 -13z m-6911 -17 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21
21 21 13z m7235 -85 c3 -5 2 -12 -3 -15 -5 -3 -9 1 -9 9 0 17 3 19 12 6z
m-310 -11 c-3 -5 -12 -10 -18 -10 -7 0 -6 4 3 10 19 12 23 12 15 0z m-6555
-34 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m4581 -53
c13 -16 12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-4106 -33 c-3 -5
-12 -10 -18 -10 -7 0 -6 4 3 10 19 12 23 12 15 0z m-485 -40 c0 -5 -5 -10 -11
-10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m320 0 c0 -5 -2 -10 -4
-10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m-409 -47 c13 -16 12
-17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m4739 13 c0 -2 -8 -10 -17 -17
-16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-3610 -70 c0 -2 -8 -10 -17 -17 -16
-13 -17 -12 -4 4 13 16 21 21 21 13z m-245 -55 c3 -5 2 -12 -3 -15 -5 -3 -9 1
-9 9 0 17 3 19 12 6z m4165 5 c0 -2 -7 -7 -16 -10 -8 -3 -12 -2 -9 4 6 10 25
14 25 6z m-3609 -53 c13 -16 12 -17 -3 -4 -17 13 -22 21 -14 21 2 0 10 -8 17
-17z m4975 -115 c3 -4 -1 -5 -10 -4 -8 1 -18 5 -22 8 -3 4 1 5 10 4 8 -1 18
-5 22 -8z m-5215 -51 c-10 -9 -11 -8 -5 6 3 10 9 15 12 12 3 -3 0 -11 -7 -18z
m339 19 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m4155
-6 c-3 -5 -12 -10 -18 -10 -7 0 -6 4 3 10 19 12 23 12 15 0z m635 4 c0 -8 -19
-13 -24 -6 -3 5 1 9 9 9 8 0 15 -2 15 -3z m-645 -113 c3 -5 2 -12 -3 -15 -5
-3 -9 1 -9 9 0 17 3 19 12 6z m-4804 -24 c-10 -9 -11 -8 -5 6 3 10 9 15 12 12
3 -3 0 -11 -7 -18z m654 13 c3 -6 -1 -7 -9 -4 -18 7 -21 14 -7 14 6 0 13 -4
16 -10z m1835 -104 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21
13z m680 -120 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m-1560 -10 c0 -3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2
10 -4z m3120 -30 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21
13z m-1842 -61 c-3 -3 -9 2 -12 12 -6 14 -5 15 5 6 7 -7 10 -15 7 -18z m-2998
5 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z
m120 4 c0 -8 -19 -13 -24 -6 -3 5 1 9 9 9 8 0 15 -2 15 -3z m2991 -11 c13 -16
12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m1603 1 c3 -8 2 -12 -4 -9
-6 3 -10 10 -10 16 0 14 7 11 14 -7z m-2994 -18 c0 -2 -8 -10 -17 -17 -16 -13
-17 -12 -4 4 13 16 21 21 21 13z m-1763 -51 c0 -8 -4 -15 -9 -15 -10 0 -11 14
-1 23 9 10 10 9 10 -8z m640 -160 c0 -8 -4 -12 -9 -9 -5 3 -6 10 -3 15 9 13
12 11 12 -6z m723 1 c0 -2 -7 -7 -16 -10 -8 -3 -12 -2 -9 4 6 10 25 14 25 6z
m1676 -92 c1 -9 0 -13 -4 -10 -3 4 -7 14 -8 22 -1 9 0 13 4 10 3 -4 7 -14 8
-22z m-2561 7 c3 -5 2 -12 -3 -15 -5 -3 -9 1 -9 9 0 17 3 19 12 6z m95 -35 c0
-2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m4080 0 c0 -2 -8
-10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-650 -70 c0 -2 -8 -10
-17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z m-3520 -10 c0 -3 -4 -8 -10
-11 -5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2 10 -4z m4160 -46 c0 -5 -5
-10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m-49 -87 c13 -16
12 -17 -3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-510 -30 c13 -16 12 -17
-3 -4 -10 7 -18 15 -18 17 0 8 8 3 21 -13z m-2841 -27 c0 -2 -8 -10 -17 -17
-16 -13 -17 -12 -4 4 13 16 21 21 21 13z m561 -13 c13 -16 12 -17 -3 -4 -17
13 -22 21 -14 21 2 0 10 -8 17 -17z m1120 0 c13 -16 12 -17 -3 -4 -17 13 -22
21 -14 21 2 0 10 -8 17 -17z m-207 -1 c3 -5 -1 -9 -9 -9 -8 0 -12 4 -9 9 3 4
7 8 9 8 2 0 6 -4 9 -8z m320 -8 c3 -8 2 -12 -4 -9 -6 3 -10 10 -10 16 0 14 7
11 14 -7z m-1024 -28 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21
21 13z m2160 0 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m-1400 -40 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m1394 -84 c3 -5 -1 -9 -9 -9 -8 0 -12 4 -9 9 3 4 7 8 9 8 2 0 6 -4 9 -8z
m-714 -36 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21 21 21 13z
m-530 -126 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4
11 -10z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import Button from '@material-ui/core/Button' import Button from '@material-ui/core/Button'
import Dialog from '@material-ui/core/Dialog' import Dialog from '@material-ui/core/Dialog'
import { torrentsHost, torrentUploadHost } from 'utils/Hosts' import { torrentsHost, torrentUploadHost } from 'utils/Hosts'
@@ -9,6 +9,9 @@ import useChangeLanguage from 'utils/useChangeLanguage'
import { useMediaQuery } from '@material-ui/core' import { useMediaQuery } from '@material-ui/core'
import CircularProgress from '@material-ui/core/CircularProgress' import CircularProgress from '@material-ui/core/CircularProgress'
import usePreviousState from 'utils/usePreviousState' import usePreviousState from 'utils/usePreviousState'
import { useQuery } from 'react-query'
import { getTorrents } from 'utils/Utils'
import parseTorrent from 'parse-torrent'
import { checkImageURL, getMoviePosters, chechTorrentSource, parseTorrentTitle } from './helpers' import { checkImageURL, getMoviePosters, chechTorrentSource, parseTorrentTitle } from './helpers'
import { ButtonWrapper, Content, Header } from './style' import { ButtonWrapper, Content, Header } from './style'
@@ -23,31 +26,75 @@ export default function AddDialog({
poster: originalPoster, poster: originalPoster,
}) { }) {
const { t } = useTranslation() const { t } = useTranslation()
const isEditMode = !!originalHash
const [torrentSource, setTorrentSource] = useState(originalHash || '') const [torrentSource, setTorrentSource] = useState(originalHash || '')
const [title, setTitle] = useState(originalTitle || '') const [title, setTitle] = useState(originalTitle || '')
const [originalTorrentTitle, setOriginalTorrentTitle] = useState('')
const [parsedTitle, setParsedTitle] = useState('')
const [posterUrl, setPosterUrl] = useState(originalPoster || '') const [posterUrl, setPosterUrl] = useState(originalPoster || '')
const [isPosterUrlCorrect, setIsPosterUrlCorrect] = useState(false) const [isPosterUrlCorrect, setIsPosterUrlCorrect] = useState(false)
const [isTorrentSourceCorrect, setIsTorrentSourceCorrect] = useState(false) const [isTorrentSourceCorrect, setIsTorrentSourceCorrect] = useState(false)
const [isHashAlreadyExists, setIsHashAlreadyExists] = useState(false)
const [posterList, setPosterList] = useState() const [posterList, setPosterList] = useState()
const [isUserInteractedWithPoster, setIsUserInteractedWithPoster] = useState(false) const [isUserInteractedWithPoster, setIsUserInteractedWithPoster] = useState(isEditMode)
const [currentLang] = useChangeLanguage() const [currentLang] = useChangeLanguage()
const [selectedFile, setSelectedFile] = useState() const [selectedFile, setSelectedFile] = useState()
const [posterSearchLanguage, setPosterSearchLanguage] = useState(currentLang === 'ru' ? 'ru' : 'en') const [posterSearchLanguage, setPosterSearchLanguage] = useState(currentLang === 'ru' ? 'ru' : 'en')
const [isLoadingButton, setIsLoadingButton] = useState(false) const [isLoadingButton, setIsLoadingButton] = useState(false)
const [skipDebounce, setSkipDebounce] = useState(false) const [skipDebounce, setSkipDebounce] = useState(false)
const [isEditMode, setIsEditMode] = useState(false) const [isCustomTitleEnabled, setIsCustomTitleEnabled] = useState(false)
const { data: torrents } = useQuery('torrents', getTorrents, {
retry: 1,
refetchInterval: 1000,
})
useEffect(() => {
const allHashes = torrents.map(({ hash }) => hash)
parseTorrent.remote(selectedFile || torrentSource, (err, { infoHash } = {}) => {
setIsHashAlreadyExists(allHashes.includes(infoHash))
})
}, [selectedFile, torrentSource, torrents])
const fullScreen = useMediaQuery('@media (max-width:930px)') const fullScreen = useMediaQuery('@media (max-width:930px)')
const updateTitleFromSource = useCallback(() => {
parseTorrentTitle(selectedFile || torrentSource, ({ parsedTitle, originalName }) => {
if (!originalName) return
setSkipDebounce(true)
setTitle('')
setIsCustomTitleEnabled(false)
setOriginalTorrentTitle(originalName)
setParsedTitle(parsedTitle)
})
}, [selectedFile, torrentSource])
useEffect(() => {
if (!selectedFile && !torrentSource) {
setTitle('')
setOriginalTorrentTitle('')
setParsedTitle('')
setIsCustomTitleEnabled(false)
setPosterList()
removePoster()
setIsUserInteractedWithPoster(false)
}
}, [selectedFile, torrentSource])
const removePoster = () => {
setIsPosterUrlCorrect(false)
setPosterUrl('')
}
useEffect(() => { useEffect(() => {
if (originalHash) { if (originalHash) {
setIsEditMode(true)
checkImageURL(posterUrl).then(correctImage => { checkImageURL(posterUrl).then(correctImage => {
correctImage ? setIsPosterUrlCorrect(true) : removePoster() correctImage ? setIsPosterUrlCorrect(true) : removePoster()
}) })
} }
// This is needed only on mount // This is needed only on mount. Do not remove line below
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
@@ -85,54 +132,51 @@ export default function AddDialog({
const delayedPosterSearch = useMemo(() => debounce(posterSearch, 700), [posterSearch]) const delayedPosterSearch = useMemo(() => debounce(posterSearch, 700), [posterSearch])
const prevTitleState = usePreviousState(title)
const prevTorrentSourceState = usePreviousState(torrentSource) const prevTorrentSourceState = usePreviousState(torrentSource)
useEffect(() => { useEffect(() => {
// if torrentSource is updated then we are checking that source is valid and getting title from the source
const torrentSourceChanged = torrentSource !== prevTorrentSourceState
const isCorrectSource = chechTorrentSource(torrentSource) const isCorrectSource = chechTorrentSource(torrentSource)
if (!isCorrectSource) return setIsTorrentSourceCorrect(false) if (!isCorrectSource) return setIsTorrentSourceCorrect(false)
setIsTorrentSourceCorrect(true) setIsTorrentSourceCorrect(true)
if (torrentSourceChanged) { // if torrentSource is updated then we are getting title from the source
parseTorrentTitle(selectedFile || torrentSource, newTitle => { const torrentSourceChanged = torrentSource !== prevTorrentSourceState
if (!newTitle) return if (!torrentSourceChanged) return
setSkipDebounce(true) updateTitleFromSource()
setTitle(newTitle) }, [prevTorrentSourceState, selectedFile, torrentSource, updateTitleFromSource])
})
} const prevTitleState = usePreviousState(title)
}, [prevTorrentSourceState, selectedFile, torrentSource])
useEffect(() => { useEffect(() => {
// if title exists and title was changed then search poster. // if title exists and title was changed then search poster.
const titleChanged = title !== prevTitleState const titleChanged = title !== prevTitleState
if (!titleChanged) return if (!titleChanged && !parsedTitle) return
if (skipDebounce) { if (skipDebounce) {
posterSearch(title, posterSearchLanguage) posterSearch(title || parsedTitle, posterSearchLanguage)
setSkipDebounce(false) setSkipDebounce(false)
} else if (!title) {
if (parsedTitle) {
posterSearch(parsedTitle, posterSearchLanguage)
} else { } else {
title === '' ? removePoster() : delayedPosterSearch(title, posterSearchLanguage) delayedPosterSearch.cancel()
!isUserInteractedWithPoster && removePoster()
} }
}, [title, prevTitleState, delayedPosterSearch, posterSearch, posterSearchLanguage, skipDebounce]) } else {
delayedPosterSearch(title, posterSearchLanguage)
const removePoster = () => {
setIsPosterUrlCorrect(false)
setPosterUrl('')
} }
}, [
useEffect(() => { title,
if (!selectedFile && !torrentSource) { parsedTitle,
setTitle('') prevTitleState,
setPosterList() delayedPosterSearch,
removePoster() posterSearch,
setIsUserInteractedWithPoster(false) posterSearchLanguage,
} skipDebounce,
}, [selectedFile, torrentSource]) isUserInteractedWithPoster,
])
const handleSave = () => { const handleSave = () => {
setIsLoadingButton(true) setIsLoadingButton(true)
@@ -142,7 +186,7 @@ export default function AddDialog({
.post(torrentsHost(), { .post(torrentsHost(), {
action: 'set', action: 'set',
hash: originalHash, hash: originalHash,
title: title === '' ? originalName : title, title: title || originalName,
poster: posterUrl, poster: posterUrl,
}) })
.finally(handleClose) .finally(handleClose)
@@ -185,13 +229,16 @@ export default function AddDialog({
)} )}
<RightSideComponent <RightSideComponent
originalTorrentTitle={originalTorrentTitle}
setTitle={setTitle} setTitle={setTitle}
setPosterUrl={setPosterUrl} setPosterUrl={setPosterUrl}
setIsPosterUrlCorrect={setIsPosterUrlCorrect} setIsPosterUrlCorrect={setIsPosterUrlCorrect}
setIsUserInteractedWithPoster={setIsUserInteractedWithPoster} setIsUserInteractedWithPoster={setIsUserInteractedWithPoster}
setPosterList={setPosterList} setPosterList={setPosterList}
isTorrentSourceCorrect={isTorrentSourceCorrect} isTorrentSourceCorrect={isTorrentSourceCorrect}
isHashAlreadyExists={isHashAlreadyExists}
title={title} title={title}
parsedTitle={parsedTitle}
posterUrl={posterUrl} posterUrl={posterUrl}
isPosterUrlCorrect={isPosterUrlCorrect} isPosterUrlCorrect={isPosterUrlCorrect}
posterList={posterList} posterList={posterList}
@@ -200,7 +247,11 @@ export default function AddDialog({
setPosterSearchLanguage={setPosterSearchLanguage} setPosterSearchLanguage={setPosterSearchLanguage}
posterSearch={posterSearch} posterSearch={posterSearch}
removePoster={removePoster} removePoster={removePoster}
updateTitleFromSource={updateTitleFromSource}
torrentSource={torrentSource} torrentSource={torrentSource}
isCustomTitleEnabled={isCustomTitleEnabled}
setIsCustomTitleEnabled={setIsCustomTitleEnabled}
isEditMode={isEditMode}
/> />
</Content> </Content>
@@ -212,7 +263,7 @@ export default function AddDialog({
<Button <Button
variant='contained' variant='contained'
style={{ minWidth: '110px' }} style={{ minWidth: '110px' }}
disabled={!torrentSource} disabled={!torrentSource || (isHashAlreadyExists && !isEditMode) || !isTorrentSourceCorrect}
onClick={handleSave} onClick={handleSave}
color='primary' color='primary'
> >

View File

@@ -50,8 +50,8 @@ export default function LeftSideComponent({
onChange={handleTorrentSourceChange} onChange={handleTorrentSourceChange}
value={torrentSource} value={torrentSource}
margin='dense' margin='dense'
label={t('TorrentSourceLink')} label={t('AddDialog.TorrentSourceLink')}
helperText={t('TorrentSourceOptions')} helperText={t('AddDialog.TorrentSourceOptions')}
type='text' type='text'
fullWidth fullWidth
onFocus={() => setIsTorrentSourceActive(true)} onFocus={() => setIsTorrentSourceActive(true)}
@@ -74,11 +74,11 @@ export default function LeftSideComponent({
) : ( ) : (
<LeftSideBottomSectionNoFile isDragActive={isDragActive} {...getRootProps()}> <LeftSideBottomSectionNoFile isDragActive={isDragActive} {...getRootProps()}>
<input {...getInputProps()} /> <input {...getInputProps()} />
<div>{t('AppendFile.Or')}</div> <div>{t('AddDialog.AppendFile.Or')}</div>
<IconWrapper> <IconWrapper>
<AddItemIcon color='primary' /> <AddItemIcon color='primary' />
<div>{t('AppendFile.ClickOrDrag')}</div> <div>{t('AddDialog.AppendFile.ClickOrDrag')}</div>
</IconWrapper> </IconWrapper>
</LeftSideBottomSectionNoFile> </LeftSideBottomSectionNoFile>
)} )}

View File

@@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { NoImageIcon } from 'icons' import { NoImageIcon } from 'icons'
import { TextField } from '@material-ui/core' import { IconButton, InputAdornment, TextField } from '@material-ui/core'
import { CheckBox as CheckBoxIcon } from '@material-ui/icons'
import { import {
ClearPosterButton, ClearPosterButton,
@@ -21,7 +22,9 @@ export default function RightSideComponent({
setIsUserInteractedWithPoster, setIsUserInteractedWithPoster,
setPosterList, setPosterList,
isTorrentSourceCorrect, isTorrentSourceCorrect,
isHashAlreadyExists,
title, title,
parsedTitle,
posterUrl, posterUrl,
isPosterUrlCorrect, isPosterUrlCorrect,
posterList, posterList,
@@ -31,6 +34,11 @@ export default function RightSideComponent({
posterSearch, posterSearch,
removePoster, removePoster,
torrentSource, torrentSource,
originalTorrentTitle,
updateTitleFromSource,
isCustomTitleEnabled,
setIsCustomTitleEnabled,
isEditMode,
}) { }) {
const { t } = useTranslation() const { t } = useTranslation()
@@ -49,13 +57,61 @@ export default function RightSideComponent({
return ( return (
<RightSide> <RightSide>
<RightSideContainer isHidden={!isTorrentSourceCorrect}> <RightSideContainer isHidden={!isTorrentSourceCorrect || (isHashAlreadyExists && !isEditMode)}>
<TextField onChange={handleTitleChange} value={title} margin='dense' label={t('Title')} type='text' fullWidth /> {originalTorrentTitle ? (
<>
<TextField
value={originalTorrentTitle}
margin='dense'
label={t('AddDialog.OriginalTorrentTitle')}
type='text'
fullWidth
disabled={isCustomTitleEnabled}
InputProps={{ readOnly: true }}
/>
<TextField
onChange={handleTitleChange}
onFocus={() => setIsCustomTitleEnabled(true)}
onBlur={({ target: { value } }) => !value && setIsCustomTitleEnabled(false)}
value={title}
margin='dense'
label={t('AddDialog.CustomTorrentTitle')}
type='text'
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position='end'>
<IconButton
style={{ padding: '0 0 0 7px' }}
onClick={() => {
setTitle('')
setIsCustomTitleEnabled(!isCustomTitleEnabled)
updateTitleFromSource()
setIsUserInteractedWithPoster(false)
}}
>
<CheckBoxIcon style={{ color: isCustomTitleEnabled ? 'green' : 'gray' }} />
</IconButton>
</InputAdornment>
),
}}
/>
</>
) : (
<TextField
onChange={handleTitleChange}
value={title}
margin='dense'
label={t('AddDialog.TitleBlank')}
type='text'
fullWidth
/>
)}
<TextField <TextField
onChange={handlePosterUrlChange} onChange={handlePosterUrlChange}
value={posterUrl} value={posterUrl}
margin='dense' margin='dense'
label={t('AddPosterLinkInput')} label={t('AddDialog.AddPosterLinkInput')}
type='url' type='url'
fullWidth fullWidth
/> />
@@ -81,7 +137,9 @@ export default function RightSideComponent({
onClick={() => { onClick={() => {
const newLanguage = posterSearchLanguage === 'en' ? 'ru' : 'en' const newLanguage = posterSearchLanguage === 'en' ? 'ru' : 'en'
setPosterSearchLanguage(newLanguage) setPosterSearchLanguage(newLanguage)
posterSearch(title, newLanguage, { shouldRefreshMainPoster: true }) posterSearch(isCustomTitleEnabled ? title : originalTorrentTitle ? parsedTitle : title, newLanguage, {
shouldRefreshMainPoster: true,
})
}} }}
showbutton={+isPosterUrlCorrect} showbutton={+isPosterUrlCorrect}
color='primary' color='primary'
@@ -108,11 +166,15 @@ export default function RightSideComponent({
</RightSideContainer> </RightSideContainer>
<RightSideContainer <RightSideContainer
isError={torrentSource && !isTorrentSourceCorrect} isError={torrentSource && (!isTorrentSourceCorrect || isHashAlreadyExists)}
notificationMessage={ notificationMessage={
!torrentSource ? t('AddTorrentSourceNotification') : !isTorrentSourceCorrect && t('WrongTorrentSource') !torrentSource
? t('AddDialog.AddTorrentSourceNotification')
: !isTorrentSourceCorrect
? t('AddDialog.WrongTorrentSource')
: isHashAlreadyExists && t('AddDialog.HashExists')
} }
isHidden={isTorrentSourceCorrect} isHidden={isEditMode || (isTorrentSourceCorrect && !isHashAlreadyExists)}
/> />
</RightSide> </RightSide>
) )

View File

@@ -32,14 +32,14 @@ export const checkImageURL = async url => {
} }
const magnetRegex = /^magnet:\?xt=urn:[a-z0-9].*/i const magnetRegex = /^magnet:\?xt=urn:[a-z0-9].*/i
const hashRegex = /^\b[0-9a-f]{32}\b$|^\b[0-9a-f]{40}\b$|^\b[0-9a-f]{64}\b$/i export const hashRegex = /^\b[0-9a-f]{32}\b$|^\b[0-9a-f]{40}\b$|^\b[0-9a-f]{64}\b$/i
const torrentRegex = /^.*\.(torrent)$/i const torrentRegex = /^.*\.(torrent)$/i
export const chechTorrentSource = source => export const chechTorrentSource = source =>
source.match(hashRegex) !== null || source.match(magnetRegex) !== null || source.match(torrentRegex) !== null source.match(hashRegex) !== null || source.match(magnetRegex) !== null || source.match(torrentRegex) !== null
export const parseTorrentTitle = (parsingSource, callback) => { export const parseTorrentTitle = (parsingSource, callback) => {
parseTorrent.remote(parsingSource, (err, { name, files } = {}) => { parseTorrent.remote(parsingSource, (err, { name, files } = {}) => {
if (!name || err) return callback(null) if (!name || err) return callback({ parsedTitle: null, originalName: null })
const torrentName = ptt.parse(name).title const torrentName = ptt.parse(name).title
const nameOfFileInsideTorrent = files ? ptt.parse(files[0].name).title : null const nameOfFileInsideTorrent = files ? ptt.parse(files[0].name).title : null
@@ -50,6 +50,6 @@ export const parseTorrentTitle = (parsingSource, callback) => {
newTitle = torrentName.length < nameOfFileInsideTorrent.length ? torrentName : nameOfFileInsideTorrent newTitle = torrentName.length < nameOfFileInsideTorrent.length ? torrentName : nameOfFileInsideTorrent
} }
callback(newTitle) callback({ parsedTitle: newTitle, originalName: name })
}) })
} }

View File

@@ -6,7 +6,7 @@ export const Header = styled.div`
color: rgba(0, 0, 0, 0.87); color: rgba(0, 0, 0, 0.87);
font-size: 20px; font-size: 20px;
color: #fff; color: #fff;
font-weight: 500; font-weight: 600;
box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
padding: 15px 24px; padding: 15px 24px;
position: relative; position: relative;
@@ -14,6 +14,7 @@ export const Header = styled.div`
export const Content = styled.div` export const Content = styled.div`
${({ isEditMode }) => css` ${({ isEditMode }) => css`
height: 550px;
background: linear-gradient(145deg, #e4f6ed, #b5dec9); background: linear-gradient(145deg, #e4f6ed, #b5dec9);
flex: 1; flex: 1;
display: grid; display: grid;
@@ -29,6 +30,10 @@ export const Content = styled.div`
@media (max-width: 930px) { @media (max-width: 930px) {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
@media (max-width: 500px) {
align-content: start;
}
`} `}
` `
@@ -38,7 +43,7 @@ export const RightSide = styled.div`
export const RightSideContainer = styled.div` export const RightSideContainer = styled.div`
${({ isHidden, notificationMessage, isError }) => css` ${({ isHidden, notificationMessage, isError }) => css`
height: 455px; height: 530px;
${notificationMessage && ${notificationMessage &&
css` css`
@@ -54,7 +59,7 @@ export const RightSideContainer = styled.div`
background: ${isError ? '#cda184' : '#84cda7'}; background: ${isError ? '#cda184' : '#84cda7'};
padding: 10px 15px; padding: 10px 15px;
position: absolute; position: absolute;
top: 50%; top: 52%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
border-radius: 5px; border-radius: 5px;
@@ -65,6 +70,10 @@ export const RightSideContainer = styled.div`
css` css`
display: none; display: none;
`}; `};
@media (max-width: 500px) {
height: 170px;
}
`} `}
` `
export const LeftSide = styled.div` export const LeftSide = styled.div`
@@ -88,7 +97,7 @@ export const LeftSideBottomSectionNoFile = styled.div`
${({ isDragActive }) => isDragActive && `border: 4px dashed green`}; ${({ isDragActive }) => isDragActive && `border: 4px dashed green`};
justify-items: center; justify-items: center;
grid-template-rows: 100px 1fr; grid-template-rows: 130px 1fr;
cursor: pointer; cursor: pointer;
:hover { :hover {
@@ -104,6 +113,15 @@ export const LeftSideBottomSectionNoFile = styled.div`
place-items: center; place-items: center;
grid-template-rows: 40% 1fr; grid-template-rows: 40% 1fr;
} }
@media (max-width: 500px) {
height: 170px;
grid-template-rows: 1fr;
> div:first-of-type {
display: none;
}
}
` `
export const LeftSideBottomSectionFileSelected = styled.div` export const LeftSideBottomSectionFileSelected = styled.div`
@@ -113,6 +131,10 @@ export const LeftSideBottomSectionFileSelected = styled.div`
@media (max-width: 930px) { @media (max-width: 930px) {
height: 400px; height: 400px;
} }
@media (max-width: 500px) {
height: 170px;
}
` `
export const TorrentIconWrapper = styled.div` export const TorrentIconWrapper = styled.div`
@@ -281,7 +303,7 @@ export const PosterLanguageSwitch = styled.div`
display: grid; display: grid;
place-items: center; place-items: center;
color: #e1f4eb; color: #e1f4eb;
font-weight: 500; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;

View File

@@ -1,12 +1,10 @@
import { playlistAllHost } from 'utils/Hosts'
import Divider from '@material-ui/core/Divider' import Divider from '@material-ui/core/Divider'
import ListItem from '@material-ui/core/ListItem' import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon' import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText' import ListItemText from '@material-ui/core/ListItemText'
import { CreditCard as CreditCardIcon, List as ListIcon, Language as LanguageIcon } from '@material-ui/icons' import { CreditCard as CreditCardIcon } from '@material-ui/icons'
import List from '@material-ui/core/List' import List from '@material-ui/core/List'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import useChangeLanguage from 'utils/useChangeLanguage'
import AddDialogButton from 'components/Add' import AddDialogButton from 'components/Add'
import SettingsDialog from 'components/Settings' import SettingsDialog from 'components/Settings'
import RemoveAll from 'components/RemoveAll' import RemoveAll from 'components/RemoveAll'
@@ -16,7 +14,6 @@ import CloseServer from 'components/CloseServer'
import { AppSidebarStyle } from './style' import { AppSidebarStyle } from './style'
export default function Sidebar({ isDrawerOpen, setIsDonationDialogOpen }) { export default function Sidebar({ isDrawerOpen, setIsDonationDialogOpen }) {
const [currentLang, changeLang] = useChangeLanguage()
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
@@ -24,12 +21,6 @@ export default function Sidebar({ isDrawerOpen, setIsDonationDialogOpen }) {
<List> <List>
<AddDialogButton /> <AddDialogButton />
<RemoveAll /> <RemoveAll />
<ListItem button component='a' target='_blank' href={playlistAllHost()}>
<ListItemIcon>
<ListIcon />
</ListItemIcon>
<ListItemText primary={t('PlaylistAll')} />
</ListItem>
</List> </List>
<Divider /> <Divider />
@@ -37,20 +28,14 @@ export default function Sidebar({ isDrawerOpen, setIsDonationDialogOpen }) {
<List> <List>
<SettingsDialog /> <SettingsDialog />
<ListItem button onClick={() => (currentLang === 'en' ? changeLang('ru') : changeLang('en'))}>
<ListItemIcon>
<LanguageIcon />
</ListItemIcon>
<ListItemText primary={t('ChooseLanguage')} />
</ListItem>
<AboutDialog />
<CloseServer /> <CloseServer />
</List> </List>
<Divider /> <Divider />
<List> <List>
<AboutDialog />
<ListItem button onClick={() => setIsDonationDialogOpen(true)}> <ListItem button onClick={() => setIsDonationDialogOpen(true)}>
<ListItemIcon> <ListItemIcon>
<CreditCardIcon /> <CreditCardIcon />

View File

@@ -10,19 +10,22 @@ import axios from 'axios'
import TorrentList from 'components/TorrentList' import TorrentList from 'components/TorrentList'
import DonateSnackbar from 'components/Donate' import DonateSnackbar from 'components/Donate'
import DonateDialog from 'components/Donate/DonateDialog' import DonateDialog from 'components/Donate/DonateDialog'
import useChangeLanguage from 'utils/useChangeLanguage'
import { AppWrapper, AppHeader } from './style' import { AppWrapper, AppHeader, LanguageSwitch } from './style'
import Sidebar from './Sidebar' import Sidebar from './Sidebar'
const baseTheme = createMuiTheme({ const baseTheme = createMuiTheme({
overrides: { MuiCssBaseline: { '@global': { html: { WebkitFontSmoothing: 'auto' } } } }, overrides: { MuiCssBaseline: { '@global': { html: { WebkitFontSmoothing: 'auto' } } } },
palette: { primary: { main: '#00a572' }, secondary: { main: '#ffa724' }, tonalOffset: 0.2 }, palette: { primary: { main: '#00a572' }, secondary: { main: '#ffa724' }, tonalOffset: 0.2 },
typography: { fontFamily: 'Open Sans, sans-serif' },
}) })
export default function App() { export default function App() {
const [isDrawerOpen, setIsDrawerOpen] = useState(false) const [isDrawerOpen, setIsDrawerOpen] = useState(false)
const [isDonationDialogOpen, setIsDonationDialogOpen] = useState(false) const [isDonationDialogOpen, setIsDonationDialogOpen] = useState(false)
const [torrServerVersion, setTorrServerVersion] = useState('') const [torrServerVersion, setTorrServerVersion] = useState('')
const [currentLang, changeLang] = useChangeLanguage()
useEffect(() => { useEffect(() => {
axios.get(echoHost()).then(({ data }) => setTorrServerVersion(data)) axios.get(echoHost()).then(({ data }) => setTorrServerVersion(data))
@@ -48,6 +51,12 @@ export default function App() {
<Typography variant='h6' noWrap> <Typography variant='h6' noWrap>
TorrServer {torrServerVersion} TorrServer {torrServerVersion}
</Typography> </Typography>
<div style={{ justifySelf: 'end' }}>
<LanguageSwitch onClick={() => (currentLang === 'en' ? changeLang('ru') : changeLang('en'))}>
{currentLang === 'en' ? 'RU' : 'EN'}
</LanguageSwitch>
</div>
</AppHeader> </AppHeader>
<Sidebar isDrawerOpen={isDrawerOpen} setIsDonationDialogOpen={setIsDonationDialogOpen} /> <Sidebar isDrawerOpen={isDrawerOpen} setIsDonationDialogOpen={setIsDonationDialogOpen} />

View File

@@ -21,8 +21,10 @@ export const AppHeader = styled.div`
background: #00a572; background: #00a572;
color: rgba(0, 0, 0, 0.87); color: rgba(0, 0, 0, 0.87);
grid-area: head; grid-area: head;
display: flex; display: grid;
grid-auto-flow: column;
align-items: center; align-items: center;
grid-template-columns: repeat(2, max-content) 1fr;
box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%); box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
padding: 0 24px; padding: 0 24px;
z-index: 3; z-index: 3;
@@ -63,3 +65,26 @@ export const TorrentListWrapper = styled.div`
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
` `
export const LanguageSwitch = styled.div`
cursor: pointer;
border-radius: 50%;
background: #56b887;
height: 35px;
width: 35px;
transition: all 0.2s;
font-weight: 600;
display: grid;
place-items: center;
color: #44795e;
:hover {
background: #7ec9a3;
}
@media (max-width: 700px) {
height: 28px;
width: 28px;
font-size: 12px;
}
`

View File

@@ -4,9 +4,9 @@ export const pieceSizeForMiniMap = 23
export const gapBetweenPieces = 3 export const gapBetweenPieces = 3
export const miniCacheMaxHeight = 340 export const miniCacheMaxHeight = 340
export const defaultBorderColor = '#eef2f4' export const defaultBorderColor = '#dbf2e8'
export const defaultBackgroundColor = '#fff' export const defaultBackgroundColor = '#fff'
export const completeColor = '#00a572' export const completeColor = '#00a572'
export const progressColor = '#ffa724' export const progressColor = '#ffa724'
export const activeColor = '#000' export const activeColor = '#000'
export const rangeColor = '#9a9aff' export const rangeColor = '#ffa724'

View File

@@ -62,5 +62,5 @@ export const SnakeWrapper = styled.div`
export const PercentagePiece = styled.div` export const PercentagePiece = styled.div`
background: ${completeColor}; background: ${completeColor};
height: ${({ percentage }) => (percentage / 100) * 12}px; height: ${({ percentage }) => percentage}%;
` `

View File

@@ -1,5 +1,5 @@
import { NoImageIcon } from 'icons' import { NoImageIcon } from 'icons'
import { humanizeSize, shortenText } from 'utils/Utils' import { humanizeSize, removeRedundantCharacters } from 'utils/Utils'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Button, ButtonGroup } from '@material-ui/core' import { Button, ButtonGroup } from '@material-ui/core'
import ptt from 'parse-torrent-title' import ptt from 'parse-torrent-title'
@@ -102,18 +102,27 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
const bufferSize = settings?.PreloadBuffer ? Capacity : 33554432 // Default is 32mb if PreloadBuffer is false const bufferSize = settings?.PreloadBuffer ? Capacity : 33554432 // Default is 32mb if PreloadBuffer is false
const getParsedTitle = () => { const getParsedTitle = () => {
const newNameStrings = [] const newNameStringArr = []
const torrentParsedName = name && ptt.parse(name) const torrentParsedName = name && ptt.parse(name)
if (title !== name) { if (title !== name) {
newNameStrings.push(title) newNameStringArr.push(removeRedundantCharacters(title))
} else if (torrentParsedName?.title) newNameStrings.push(torrentParsedName?.title) } else if (torrentParsedName?.title) newNameStringArr.push(removeRedundantCharacters(torrentParsedName?.title))
if (torrentParsedName?.year) newNameStrings.push(torrentParsedName?.year) // These 2 checks are needed to get year and resolution from torrent name if title does not have this info
if (torrentParsedName?.resolution) newNameStrings.push(torrentParsedName?.resolution) if (torrentParsedName?.year && !newNameStringArr[0].includes(torrentParsedName?.year))
newNameStringArr.push(torrentParsedName?.year)
if (torrentParsedName?.resolution && !newNameStringArr[0].includes(torrentParsedName?.resolution))
newNameStringArr.push(torrentParsedName?.resolution)
return newNameStrings.join('. ') const newNameString = newNameStringArr.join('. ')
// removeRedundantCharacters is returning ".." if it was "..."
const lastDotShouldBeAdded =
newNameString[newNameString.length - 1] === '.' && newNameString[newNameString.length - 2] === '.'
return lastDotShouldBeAdded ? `${newNameString}.` : newNameString
} }
return ( return (
@@ -145,12 +154,19 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
<div> <div>
{title && name !== title ? ( {title && name !== title ? (
getParsedTitle().length > 90 ? (
<> <>
<SectionTitle>{shortenText(getParsedTitle(), 55)}</SectionTitle> <SectionTitle>{ptt.parse(name).title}</SectionTitle>
<SectionSubName mb={20}>{shortenText(ptt.parse(name).title, 110)}</SectionSubName> <SectionSubName mb={20}>{getParsedTitle()}</SectionSubName>
</> </>
) : ( ) : (
<SectionTitle mb={20}>{shortenText(getParsedTitle(), 55)}</SectionTitle> <>
<SectionTitle>{getParsedTitle()}</SectionTitle>
<SectionSubName mb={20}>{ptt.parse(name).title}</SectionSubName>
</>
)
) : (
<SectionTitle mb={20}>{getParsedTitle()}</SectionTitle>
)} )}
<WidgetWrapper> <WidgetWrapper>

View File

@@ -113,7 +113,7 @@ export const SectionTitle = styled.div`
${({ mb }) => css` ${({ mb }) => css`
${mb && `margin-bottom: ${mb}px`}; ${mb && `margin-bottom: ${mb}px`};
font-size: 35px; font-size: 35px;
font-weight: 200; font-weight: 300;
line-height: 1; line-height: 1;
word-break: break-word; word-break: break-word;
@@ -187,7 +187,7 @@ export const WidgetFieldTitle = styled.div`
text-transform: uppercase; text-transform: uppercase;
font-size: 11px; font-size: 11px;
margin-bottom: 2px; margin-bottom: 2px;
font-weight: 500; font-weight: 600;
` `
export const WidgetFieldIcon = styled.div` export const WidgetFieldIcon = styled.div`

View File

@@ -1,4 +1,3 @@
import 'fontsource-roboto'
import { forwardRef, memo, useState } from 'react' import { forwardRef, memo, useState } from 'react'
import { import {
UnfoldMore as UnfoldMoreIcon, UnfoldMore as UnfoldMoreIcon,
@@ -6,7 +5,7 @@ import {
Close as CloseIcon, Close as CloseIcon,
Delete as DeleteIcon, Delete as DeleteIcon,
} from '@material-ui/icons' } from '@material-ui/icons'
import { getPeerString, humanizeSize, shortenText } from 'utils/Utils' import { getPeerString, humanizeSize, removeRedundantCharacters } from 'utils/Utils'
import { torrentsHost } from 'utils/Hosts' import { torrentsHost } from 'utils/Hosts'
import { NoImageIcon } from 'icons' import { NoImageIcon } from 'icons'
import DialogTorrentDetailsContent from 'components/DialogTorrentDetailsContent' import DialogTorrentDetailsContent from 'components/DialogTorrentDetailsContent'
@@ -40,7 +39,21 @@ const Torrent = ({ torrent }) => {
const dropTorrent = () => axios.post(torrentsHost(), { action: 'drop', hash }) const dropTorrent = () => axios.post(torrentsHost(), { action: 'drop', hash })
const deleteTorrent = () => axios.post(torrentsHost(), { action: 'rem', hash }) const deleteTorrent = () => axios.post(torrentsHost(), { action: 'rem', hash })
const parsedTitle = (title || name) && ptt.parse(title || name).title const getParsedTitle = () => {
const parse = key => ptt.parse(title || '')?.[key] || ptt.parse(name || '')?.[key]
const titleStrings = []
let parsedTitle = removeRedundantCharacters(parse('title'))
const parsedYear = parse('year')
const parsedResolution = parse('resolution')
if (parsedTitle) titleStrings.push(parsedTitle)
if (parsedYear) titleStrings.push(`(${parsedYear})`)
if (parsedResolution) titleStrings.push(`[${parsedResolution}]`)
parsedTitle = titleStrings.join(' ')
return { parsedTitle }
}
const { parsedTitle } = getParsedTitle()
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
const handleClickOpenEditDialog = () => setIsEditDialogOpen(true) const handleClickOpenEditDialog = () => setIsEditDialogOpen(true)
@@ -78,7 +91,7 @@ const Torrent = ({ torrent }) => {
<TorrentCardDescription> <TorrentCardDescription>
<div className='description-title-wrapper'> <div className='description-title-wrapper'>
<div className='description-section-name'>{t('Name')}</div> <div className='description-section-name'>{t('Name')}</div>
<div className='description-torrent-title'>{shortenText(parsedTitle, 100)}</div> <div className='description-torrent-title'>{parsedTitle}</div>
</div> </div>
<div className='description-statistics-wrapper'> <div className='description-statistics-wrapper'>

View File

@@ -87,10 +87,6 @@ export const TorrentCardDescription = styled.div`
gap: 3px; gap: 3px;
} }
@media (max-width: 770px) {
grid-template-rows: 56% 1fr;
}
.description-title-wrapper { .description-title-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -99,7 +95,7 @@ export const TorrentCardDescription = styled.div`
.description-section-name { .description-section-name {
text-transform: uppercase; text-transform: uppercase;
font-size: 10px; font-size: 10px;
font-weight: 500; font-weight: 600;
letter-spacing: 0.4px; letter-spacing: 0.4px;
color: #216e47; color: #216e47;
@@ -170,7 +166,6 @@ export const StyledButton = styled.button`
background: #268757; background: #268757;
color: #fff; color: #fff;
font-size: 0.9rem; font-size: 0.9rem;
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
letter-spacing: 0.009em; letter-spacing: 0.009em;
padding: 0 12px; padding: 0 12px;
svg { svg {

View File

@@ -1,21 +1,11 @@
import { useState } from 'react' import { useState } from 'react'
import { Typography } from '@material-ui/core' import { Typography } from '@material-ui/core'
import { torrentsHost } from 'utils/Hosts'
import TorrentCard from 'components/TorrentCard' import TorrentCard from 'components/TorrentCard'
import axios from 'axios'
import CircularProgress from '@material-ui/core/CircularProgress' import CircularProgress from '@material-ui/core/CircularProgress'
import { TorrentListWrapper, CenteredGrid } from 'components/App/style' import { TorrentListWrapper, CenteredGrid } from 'components/App/style'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useQuery } from 'react-query' import { useQuery } from 'react-query'
import { getTorrents } from 'utils/Utils'
const getTorrents = async () => {
try {
const { data } = await axios.post(torrentsHost(), { action: 'list' })
return data
} catch (error) {
throw new Error(null)
}
}
export default function TorrentList() { export default function TorrentList() {
const { t } = useTranslation() const { t } = useTranslation()

View File

@@ -1,13 +1,20 @@
body { *,
*::before,
*::after {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', padding: 0;
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', box-sizing: inherit;
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
} }
code { body {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: "Open Sans", sans-serif;
monospace; box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
letter-spacing: -0.1px;
}
button {
font-family: "Open Sans", sans-serif;
letter-spacing: -0.1px;
} }

View File

@@ -2,21 +2,29 @@
"About": "About", "About": "About",
"Actions": "Actions", "Actions": "Actions",
"Add": "Add", "Add": "Add",
"AddFromLink": "Add from Link", "AddDialog": {
"AddNewTorrent": "Add new torrent",
"AddPosterLinkInput": "Poster link", "AddPosterLinkInput": "Poster link",
"AddRetrackers": "Add retrackers",
"AddTorrentSourceNotification": "First add your torrent source", "AddTorrentSourceNotification": "First add your torrent source",
"AppendFile": { "AppendFile": {
"Or": "OR", "Or": "OR",
"ClickOrDrag": "CLICK / DRAG & DROP (.torrent)" "ClickOrDrag": "CLICK / DRAG & DROP (.torrent)"
}, },
"CustomTorrentTitle": "Custom title (optional)",
"HashExists": "This torrent is already in database",
"OriginalTorrentTitle": "Original torrent title",
"TitleBlank": "Title (blank for orig. torrent title)",
"TorrentSourceLink": "Torrent source link",
"TorrentSourceOptions": "magnet / hash / .torrent file link",
"WrongTorrentSource": "Wrong torrent source"
},
"AddFromLink": "Add from Link",
"AddNewTorrent": "Add new torrent",
"AddRetrackers": "Add retrackers",
"Buffer": "Preload Buffer / Cache", "Buffer": "Preload Buffer / Cache",
"BufferNote": "Enable “Preload Buffer” in settings to see cache loading progress", "BufferNote": "Enable “Preload Buffer” in settings to see cache loading progress",
"Cache": "Cache", "Cache": "Cache",
"CacheSize": "Cache Size (Megabytes)", "CacheSize": "Cache Size (Megabytes)",
"Cancel": "Cancel", "Cancel": "Cancel",
"ChooseLanguage": "Russian",
"Clear": "Clear", "Clear": "Clear",
"Close": "Close", "Close": "Close",
"CloseServer?": "Do you want to turn off server?", "CloseServer?": "Do you want to turn off server?",
@@ -63,7 +71,6 @@
"PEX": "PEX (Peer Exchange)", "PEX": "PEX (Peer Exchange)",
"PiecesCount": "Pieces count", "PiecesCount": "Pieces count",
"PiecesLength": "Pieces length", "PiecesLength": "Pieces length",
"PlaylistAll": "Playlist All",
"Preload": "Preload", "Preload": "Preload",
"PreloadBuffer": "Preload Buffer", "PreloadBuffer": "Preload Buffer",
"ReaderReadAHead": "Reader Read Ahead (5-100%)", "ReaderReadAHead": "Reader Read Ahead (5-100%)",
@@ -86,7 +93,6 @@
"Support": "Support", "Support": "Support",
"TCP": "TCP (Transmission Control Protocol)", "TCP": "TCP (Transmission Control Protocol)",
"ThanksToEveryone": "Thanks to everyone who tested and helped.", "ThanksToEveryone": "Thanks to everyone who tested and helped.",
"Title": "Title",
"TorrentAdded": "Added", "TorrentAdded": "Added",
"TorrentClosed": "Сlosed", "TorrentClosed": "Сlosed",
"TorrentContent": "Torrent Content", "TorrentContent": "Torrent Content",
@@ -96,8 +102,6 @@
"TorrentInDb": "In DB", "TorrentInDb": "In DB",
"TorrentPreload": "Preload", "TorrentPreload": "Preload",
"TorrentSize": "Torrent size", "TorrentSize": "Torrent size",
"TorrentSourceLink": "Torrent source link",
"TorrentSourceOptions": "magnet / hash / .torrent file link",
"TorrentsSavePath": "Torrents Save Path", "TorrentsSavePath": "Torrents Save Path",
"TorrentState": "Torrent State", "TorrentState": "Torrent State",
"TorrentStatus": "Torrent Status", "TorrentStatus": "Torrent Status",
@@ -111,6 +115,5 @@
"UseDisk": "Use Disk for Cache", "UseDisk": "Use Disk for Cache",
"UseDiskDesc": "Better use external media on flash-based devices", "UseDiskDesc": "Better use external media on flash-based devices",
"UTP": "μTP (Micro Transport Protocol)", "UTP": "μTP (Micro Transport Protocol)",
"Viewed": "Viewed", "Viewed": "Viewed"
"WrongTorrentSource": "Wrong torrent source"
} }

View File

@@ -2,21 +2,29 @@
"About": "О сервере", "About": "О сервере",
"Actions": "Действия", "Actions": "Действия",
"Add": "Добавить", "Add": "Добавить",
"AddFromLink": "Добавить", "AddDialog": {
"AddNewTorrent": "Добавить новый торрент",
"AddPosterLinkInput": "Ссылка на постер", "AddPosterLinkInput": "Ссылка на постер",
"AddRetrackers": "Добавлять",
"AddTorrentSourceNotification": "Сначала добавьте torrent-источник", "AddTorrentSourceNotification": "Сначала добавьте torrent-источник",
"AppendFile": { "AppendFile": {
"Or": "ИЛИ", "Or": "ИЛИ",
"ClickOrDrag": "НАЖМИТЕ / ПЕРЕТАЩИТЕ ФАЙЛ (.torrent)" "ClickOrDrag": "НАЖМИТЕ / ПЕРЕТАЩИТЕ ФАЙЛ (.torrent)"
}, },
"CustomTorrentTitle": "Cвое имя (не обязательно)",
"HashExists": "Этот торрент уже есть в базе данных",
"OriginalTorrentTitle": "Оригинальное имя торрента",
"TitleBlank": "Имя (пустое - ориг. имя торрента)",
"TorrentSourceLink": "Ссылка на источник торрента",
"TorrentSourceOptions": "magnet-ссылка / хеш / ссылка на .torrent файл",
"WrongTorrentSource": "Неправильный torrent-источник"
},
"AddFromLink": "Добавить",
"AddNewTorrent": "Добавить новый торрент",
"AddRetrackers": "Добавлять",
"Buffer": "Предзагрузка / Кеш", "Buffer": "Предзагрузка / Кеш",
"BufferNote": "Включите «Наполнять кеш перед началом воспроизведения» в настройках для показа заполнения кеша", "BufferNote": "Включите «Наполнять кеш перед началом воспроизведения» в настройках для показа заполнения кеша",
"Cache": "Кеш", "Cache": "Кеш",
"CacheSize": "Размер кеша (Мегабайты)", "CacheSize": "Размер кеша (Мегабайты)",
"Cancel": "Отмена", "Cancel": "Отмена",
"ChooseLanguage": "Английский",
"Clear": "Очистить", "Clear": "Очистить",
"Close": "Закрыть", "Close": "Закрыть",
"CloseServer?": "Хотите выключить сервер?", "CloseServer?": "Хотите выключить сервер?",
@@ -58,12 +66,11 @@
"Offline": "Сервер не доступен", "Offline": "Сервер не доступен",
"OK": "OK", "OK": "OK",
"OpenLink": "Открыть", "OpenLink": "Открыть",
"Peers": одкл./Пиры", "Peers": "Пиры",
"PeersListenPort": "Порт для входящих подключений", "PeersListenPort": "Порт для входящих подключений",
"PEX": "PEX (Peer Exchange)", "PEX": "PEX (Peer Exchange)",
"PiecesCount": "Кол-во блоков", "PiecesCount": "Кол-во блоков",
"PiecesLength": "Размер блока", "PiecesLength": "Размер блока",
"PlaylistAll": "Плейлист всех",
"Preload": "Предзагр.", "Preload": "Предзагр.",
"PreloadBuffer": "Наполнять кеш перед началом воспроизведения", "PreloadBuffer": "Наполнять кеш перед началом воспроизведения",
"ReaderReadAHead": "Кеш предзагрузки (5-100%, рек. 95%)", "ReaderReadAHead": "Кеш предзагрузки (5-100%, рек. 95%)",
@@ -86,7 +93,6 @@
"Support": "Поддержать", "Support": "Поддержать",
"TCP": "TCP (Transmission Control Protocol)", "TCP": "TCP (Transmission Control Protocol)",
"ThanksToEveryone": "Спасибо всем, кто тестировал и помогал!", "ThanksToEveryone": "Спасибо всем, кто тестировал и помогал!",
"Title": "Название",
"TorrentAdded": "Добавлен", "TorrentAdded": "Добавлен",
"TorrentClosed": "Закрыт", "TorrentClosed": "Закрыт",
"TorrentContent": "Содержимое торрента", "TorrentContent": "Содержимое торрента",
@@ -96,8 +102,6 @@
"TorrentInDb": "Торрент в БД", "TorrentInDb": "Торрент в БД",
"TorrentPreload": "Предзагрузка", "TorrentPreload": "Предзагрузка",
"TorrentSize": "Размер торрента", "TorrentSize": "Размер торрента",
"TorrentSourceLink": "Ссылка на источник торрента",
"TorrentSourceOptions": "magnet-ссылка / хеш / ссылка на .torrent файл",
"TorrentsSavePath": "Путь хранения кеша", "TorrentsSavePath": "Путь хранения кеша",
"TorrentState": "Данные торрента", "TorrentState": "Данные торрента",
"TorrentStatus": "Состояние торрента", "TorrentStatus": "Состояние торрента",
@@ -111,6 +115,5 @@
"UseDisk": "Использовать диск для кеша", "UseDisk": "Использовать диск для кеша",
"UseDiskDesc": "Рекомендуется использовать внешние носители на устройствах с flash-памятью", "UseDiskDesc": "Рекомендуется использовать внешние носители на устройствах с flash-памятью",
"UTP": "μTP (Micro Transport Protocol)", "UTP": "μTP (Micro Transport Protocol)",
"Viewed": "Просм.", "Viewed": "Просм."
"WrongTorrentSource": "Неправильный torrent-источник"
} }

View File

@@ -10,7 +10,6 @@ export const settingsHost = () => `${torrserverHost}/settings`
export const streamHost = () => `${torrserverHost}/stream` export const streamHost = () => `${torrserverHost}/stream`
export const shutdownHost = () => `${torrserverHost}/shutdown` export const shutdownHost = () => `${torrserverHost}/shutdown`
export const echoHost = () => `${torrserverHost}/echo` export const echoHost = () => `${torrserverHost}/echo`
export const playlistAllHost = () => `${torrserverHost}/playlistall/all.m3u`
export const playlistTorrHost = () => `${torrserverHost}/stream` export const playlistTorrHost = () => `${torrserverHost}/stream`
export const getTorrServerHost = () => torrserverHost export const getTorrServerHost = () => torrserverHost

View File

@@ -1,7 +1,11 @@
import axios from 'axios'
import { torrentsHost } from './Hosts'
export function humanizeSize(size) { export function humanizeSize(size) {
if (!size) return '' if (!size) return ''
const i = Math.floor(Math.log(size) / Math.log(1024)) const i = Math.floor(Math.log(size) / Math.log(1024))
return `${(size / Math.pow(1024, i)).toFixed(2) * 1} ${['B', 'kB', 'MB', 'GB', 'TB'][i]}` return `${(size / Math.pow(1024, i)).toFixed(2) * 1} ${['B', 'KB', 'MB', 'GB', 'TB'][i]}`
} }
export function getPeerString(torrent) { export function getPeerString(torrent) {
@@ -10,4 +14,43 @@ export function getPeerString(torrent) {
} }
export const shortenText = (text, sympolAmount) => export const shortenText = (text, sympolAmount) =>
text ? text.slice(0, sympolAmount) + (text.length > sympolAmount ? '...' : '') : '' text ? text.slice(0, sympolAmount) + (text.length > sympolAmount ? '' : '') : ''
export const removeRedundantCharacters = string => {
let newString = string
const brackets = [
['(', ')'],
['[', ']'],
['{', '}'],
]
brackets.forEach(el => {
const leftBracketRegexFormula = `\\${el[0]}`
const leftBracketRegex = new RegExp(leftBracketRegexFormula, 'g')
const leftBracketAmount = [...newString.matchAll(leftBracketRegex)].length
const rightBracketRegexFormula = `\\${el[1]}`
const rightBracketRegex = new RegExp(rightBracketRegexFormula, 'g')
const rightBracketAmount = [...newString.matchAll(rightBracketRegex)].length
if (leftBracketAmount !== rightBracketAmount) {
const removeFormula = `(\\${el[0]})(?!.*\\1).*`
const removeRegex = new RegExp(removeFormula, 'g')
newString = newString.replace(removeRegex, '')
}
})
const hasThreeDotsAtTheEnd = !!newString.match(/\.{3}$/g)
const trimmedString = newString.replace(/[\\.| ]+$/g, '').trim()
return hasThreeDotsAtTheEnd ? `${trimmedString}..` : trimmedString
}
export const getTorrents = async () => {
try {
const { data } = await axios.post(torrentsHost(), { action: 'list' })
return data
} catch (error) {
throw new Error(null)
}
}

View File

@@ -5808,11 +5808,6 @@ follow-redirects@^1.0.0, follow-redirects@^1.10.0:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
fontsource-roboto@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fontsource-roboto/-/fontsource-roboto-4.0.0.tgz#35eacd4fb8d90199053c0eec9b34a57fb79cd820"
integrity sha512-zD6L8nvdWRcwSgp4ojxFchG+MPj8kXXQKDEAH9bfhbxy+lkpvpC1WgAK0lCa4dwobv+hvAe0uyHaawcgH7WH/g==
for-each@^0.3.3: for-each@^0.3.3:
version "0.3.3" version "0.3.3"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"