Compare commits
602 commits
auth-refac
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
d14c9141db | ||
|
06c350c4db | ||
|
c3a9d9d002 | ||
|
aeada6dc74 | ||
|
6ba985fd28 | ||
|
2a0bc85738 | ||
|
523ea284a2 | ||
|
a66d7534c2 | ||
|
89ec07622f | ||
|
7e6ad31392 | ||
|
4ab23f03fc | ||
|
29cea5cc07 | ||
|
8dd7150d67 | ||
|
f1b1523017 | ||
|
88ac250fee | ||
|
0a5e9e87ed | ||
|
20413d79b6 | ||
|
ff9011e6ac | ||
|
802139d0a4 | ||
|
68d307fa50 | ||
|
d3900e39b6 | ||
|
35769dede6 | ||
|
ae1e20d131 | ||
|
051af8b6ad | ||
|
5d77b115f9 | ||
|
7dbeeac112 | ||
|
de062c4aee | ||
|
18a3336714 | ||
|
ebf35a1df8 | ||
|
28e168d5e5 | ||
|
7ad66b99e7 | ||
|
7eb7c1a5ff | ||
|
b88e704e6b | ||
|
8d85af8064 | ||
|
f9d7340729 | ||
|
d57300f785 | ||
|
09baf1d9b9 | ||
|
ce692d38ea | ||
|
b1ca3b0183 | ||
|
a4c34d89e2 | ||
|
d7b9ca3391 | ||
|
55a5c7b051 | ||
|
0e2a9a1033 | ||
|
3fd38be789 | ||
|
329d434c83 | ||
|
6a2939d9fc | ||
|
65781e33f7 | ||
|
608721c7ac | ||
|
2846dd926b | ||
|
7a8a16cd9c | ||
|
1c17ff5dd9 | ||
|
b9be8d9f3a | ||
|
6b1069f02a | ||
|
b92fb03856 | ||
|
1a9743e639 | ||
|
2773cbbe6e | ||
|
e11fb5d225 | ||
|
38200b9912 | ||
|
c69826a887 | ||
|
ce4fadbdf9 | ||
|
2895e93323 | ||
|
3859124c05 | ||
|
da5ec6edc1 | ||
|
9825bccf4a | ||
|
f065218ff4 | ||
|
f79c622edf | ||
|
c7ab47f54d | ||
|
8c10617259 | ||
|
2e565fd95b | ||
|
68718f32b2 | ||
|
f70bde0639 | ||
|
2be96a25b1 | ||
|
30cbc3f5c5 | ||
|
8d8c1fd58c | ||
|
d8600e286f | ||
|
7d08800836 | ||
|
27ede86996 | ||
|
60e5471f5e | ||
|
a1e52eea4a | ||
|
5a99d9777f | ||
|
fab69450c0 | ||
|
5609b43fc7 | ||
|
cc9f2e62de | ||
|
dd6a156d7c | ||
|
7b0d38eca7 | ||
|
168ae17eb6 | ||
|
0067d5a411 | ||
|
99ceaadf8b | ||
|
743c75df16 | ||
|
c5b018afc3 | ||
|
c7511fc803 | ||
|
17bd0eec30 | ||
|
5c6f7a391b | ||
|
d841835c9d | ||
|
3c3635977e | ||
|
d69eeaa589 | ||
|
8a9af7d354 | ||
|
6108860063 | ||
|
63ce691b90 | ||
|
de825fd4dc | ||
|
3671e855cb | ||
|
efa682ef02 | ||
|
5319232918 | ||
|
c5b7cc5ac4 | ||
|
02e4b651af | ||
|
34ea712874 | ||
|
562e154675 | ||
|
5b4948891e | ||
|
5ed13de5d6 | ||
|
9e48d22d70 | ||
|
9f40680b64 | ||
|
addcd63794 | ||
|
c11235c70e | ||
|
d8bedba43d | ||
|
befcf9fd55 | ||
|
feb37a17a6 | ||
|
fcff90e78c | ||
|
6acdfc9623 | ||
|
15ad30d509 | ||
|
3980d0f51e | ||
|
43c45c1f62 | ||
|
673ab85bea | ||
|
3932ecf7c0 | ||
|
9386cc678f | ||
|
fe2af64b90 | ||
|
7c2e4eb3e3 | ||
|
0c990ba276 | ||
|
f6fd061dd6 | ||
|
150f280c33 | ||
|
3bd97984c0 | ||
|
c31e40089c | ||
|
465d204e69 | ||
|
6742bc373c | ||
|
75d2464443 | ||
|
6a26771339 | ||
|
5a2e4ae49a | ||
|
0d44213f78 | ||
|
0bb13331d5 | ||
|
6bffc8a993 | ||
|
811bb0cf30 | ||
|
d64da2a8b2 | ||
|
a99f6d4bf2 | ||
|
0101bc0533 | ||
|
83faa7bba0 | ||
|
a18aceed1f | ||
|
2eb0219ad1 | ||
|
c58b3c752e | ||
|
ee98531c00 | ||
|
ee754f0085 | ||
|
3854bf6fd4 | ||
|
3ca62704d5 | ||
|
34d9b05de0 | ||
|
87835451f3 | ||
|
3b7b9e69c7 | ||
|
febbb4f82e | ||
|
e29e982ffb | ||
|
d84746d395 | ||
|
c133d5b53a | ||
|
845f33b4f2 | ||
|
9b26a65be4 | ||
|
2193c61628 | ||
|
aef3eccfbd | ||
|
3034f4ce6c | ||
|
50ae6e2869 | ||
|
21204321c0 | ||
|
daab8a28ed | ||
|
79f05a831f | ||
|
243cd0dffd | ||
|
8e536e7020 | ||
|
b0ab2daf39 | ||
|
3fd3a548ec | ||
|
d1c82c9af0 | ||
|
5ef1baff3b | ||
|
da564452f7 | ||
|
c3d836bca7 | ||
|
745a3ca889 | ||
|
f5564086c4 | ||
|
0e6b2eae8b | ||
|
23d61df410 | ||
|
c28c5219c1 | ||
|
1db943518d | ||
|
9f4ba9041b | ||
|
b47bd7e8b6 | ||
|
ec600bb8c8 | ||
|
f055198b29 | ||
|
e1a66d389f | ||
|
1891107a80 | ||
|
cff40b338c | ||
|
945b4b3992 | ||
|
085929a92e | ||
|
7f83c68d82 | ||
|
e0c2f03c16 | ||
|
12b15a2549 | ||
|
4d75922c8b | ||
|
0de96358bd | ||
|
5a24d6c570 | ||
|
1369fbb064 | ||
|
2aeb419969 | ||
|
474115b398 | ||
|
d09e294797 | ||
|
1bfb843606 | ||
|
d302fbae09 | ||
|
52e15bbc96 | ||
|
e0dc359241 | ||
|
8275ff1228 | ||
|
31d896fb8d | ||
|
0c467ddc8d | ||
|
37dbfcb40a | ||
|
f5b4aba9da | ||
|
21af8f4f28 | ||
|
cde52f4d35 | ||
|
7da8be67fe | ||
|
538aa3b42f | ||
|
fd02bb00b3 | ||
|
d5c79495ab | ||
|
702a3f8eb8 | ||
|
35276d5879 | ||
|
a7e70e4d21 | ||
|
6560ab6c7b | ||
|
66bdd3fc87 | ||
|
c843991378 | ||
|
aa008d8a61 | ||
|
ca1d0e71b7 | ||
|
2ddf0a2cbd | ||
|
64e48c1eef | ||
|
10381ef06a | ||
|
cef9ade10e | ||
|
33e5318a29 | ||
|
8ecdaab6d4 | ||
|
9de2465e21 | ||
|
bef46257bc | ||
|
d410664d9a | ||
|
1f8c9fd24d | ||
|
64a2e908ae | ||
|
40e20dfc1b | ||
|
53425d8fe2 | ||
|
ca748fd16a | ||
|
ea7ed53bfe | ||
|
29c605de86 | ||
|
b56746475b | ||
|
94f64f8d25 | ||
|
04a6c735ac | ||
|
e9502410de | ||
|
bf87ac08db | ||
|
72310710af | ||
|
cce7cd43d8 | ||
|
080eee7d92 | ||
|
3058dbb4a6 | ||
|
b7d685b4ec | ||
|
e1ecf87df7 | ||
|
48c5f58a8e | ||
|
aa40a44ce3 | ||
|
8fb67d5980 | ||
|
67608dfc9c | ||
|
9fc600397c | ||
|
3d85cff0ef | ||
|
97fbc5e598 | ||
|
9375ada72a | ||
|
49c5180820 | ||
|
12097dbbba | ||
|
870992a6e4 | ||
|
eb1b412840 | ||
|
7c7c2aa053 | ||
|
f6961f91fe | ||
|
154b20c6c8 | ||
|
43af7ddc5f | ||
|
a574a5c1de | ||
|
c553ee46f6 | ||
|
71eedd20e1 | ||
|
3a4ff9d35d | ||
|
0106c1c361 | ||
|
830567b6ec | ||
|
8bd38114dd | ||
|
92ed44c0f2 | ||
|
bbea2887f2 | ||
|
ca2c6a6f8f | ||
|
ab87591501 | ||
|
24264a15b0 | ||
|
4f74ecfc47 | ||
|
703d1afc06 | ||
|
629b928c80 | ||
|
9a5f69f0eb | ||
|
fe72dcfc2c | ||
|
fe1a2eac33 | ||
|
2e048dc225 | ||
|
cbc3624664 | ||
|
6b37fd2eae | ||
|
d13397d4dd | ||
|
f370aa8db1 | ||
|
0e134a02d7 | ||
|
acbf262641 | ||
|
54b59dd946 | ||
|
eb9bbd4c2c | ||
|
18b4f09bab | ||
|
98852e7eb4 | ||
|
5fdac4898f | ||
|
618ab5e195 | ||
|
f095081c71 | ||
|
3f0cc0bf2e | ||
|
caf32736b5 | ||
|
0ab1aab4e7 | ||
|
0b41fc041a | ||
|
fe95db15b2 | ||
|
c1a54f9707 | ||
|
f06b3bd877 | ||
|
11eed5c9eb | ||
|
18c3bbe6e3 | ||
|
fc3ddf01b1 | ||
|
ca0cd2ca75 | ||
|
2f1fc941bb | ||
|
528a0f4d6e | ||
|
cff17deb5d | ||
|
bd3126a6b8 | ||
|
4f6befc0ce | ||
|
c7021a08ad | ||
|
ea072febae | ||
|
064d8738dd | ||
|
03f5358a8c | ||
|
4cfc201b20 | ||
|
e5c4a5a1d9 | ||
|
ebe253f814 | ||
|
3450d76295 | ||
|
33949e5dbf | ||
|
85899e7e38 | ||
|
22a302f528 | ||
|
8a7b88f073 | ||
|
78fb559fa2 | ||
|
220021964d | ||
|
34283f7be6 | ||
|
ca799ae4ec | ||
|
3812989c0a | ||
|
7a100ec022 | ||
|
ba2caf4eb2 | ||
|
cedfd3e32c | ||
|
e704f87557 | ||
|
0eab08e3b4 | ||
|
a7659c368b | ||
|
82b9640387 | ||
|
5a69c9f4e9 | ||
|
65ad23be4f | ||
|
a1f4a7f8ec | ||
|
6d04ff6e32 | ||
|
68ecfbb990 | ||
|
73600003b0 | ||
|
08c47a57f1 | ||
|
a3398843c2 | ||
|
4fc050fdad | ||
|
d9d7845d96 | ||
|
500ed82988 | ||
|
851a715025 | ||
|
ee520d89e1 | ||
|
fd8cf9b40d | ||
|
dbc980cab5 | ||
|
c00b9cd434 | ||
|
b2e2e84cc0 | ||
|
dfdd3fc248 | ||
|
2549f5b0ad | ||
|
0600646479 | ||
|
36890601a8 | ||
|
59a188044e | ||
|
ca15faef02 | ||
|
24c516acb0 | ||
|
cb7f2e87cf | ||
|
1b2a6f6651 | ||
|
44fd8349a7 | ||
|
18619cac20 | ||
|
27e8159db9 | ||
|
6312f1eee9 | ||
|
1929089fab | ||
|
ae682a4a33 | ||
|
000a750f19 | ||
|
5e3844390f | ||
|
4c17b3818e | ||
|
a0917908bb | ||
|
05d5e6c05d | ||
|
514bc0a273 | ||
|
f474c014f3 | ||
|
0cd1c6d535 | ||
|
e5072c8681 | ||
|
6fed123253 | ||
|
13a9f5035d | ||
|
474782eeb0 | ||
|
3458fa5e50 | ||
|
6e27100606 | ||
|
d22442a316 | ||
|
28383efbdf | ||
|
d9afaadb27 | ||
|
ec14cc8828 | ||
|
3519be4633 | ||
|
408eb9a347 | ||
|
6ba1fafaa0 | ||
|
5e09de5f94 | ||
|
faa21cb681 | ||
|
79dbcfdafa | ||
|
19b24d3f58 | ||
|
5be27a5e2c | ||
|
797c18fd15 | ||
|
5a1cafc7d3 | ||
|
64a31f1aad | ||
|
d89ef5b928 | ||
|
041127bbce | ||
|
0459148e30 | ||
|
4d01452447 | ||
|
e7572312a8 | ||
|
29b845c3fc | ||
|
26f89b3ec9 | ||
|
e262e44912 | ||
|
7c11f91630 | ||
|
b778c70837 | ||
|
60f2fd65fd | ||
|
0c1b681b44 | ||
|
5fed2d01c0 | ||
|
f79446b1e7 | ||
|
cfd876927f | ||
|
f2da969f72 | ||
|
fea6ca8635 | ||
|
f1de3b193e | ||
|
02ba41e759 | ||
|
d50a74327f | ||
|
7afe2c58b0 | ||
|
30b1562e64 | ||
|
1360e21016 | ||
|
c6322ddb24 | ||
|
ba07cdeaa2 | ||
|
01a0c0ec6f | ||
|
73c4791460 | ||
|
83ce35df1b | ||
|
1f381ffb9b | ||
|
ef2c53f678 | ||
|
e0b82528d2 | ||
|
eaf3826df8 | ||
|
88b9c9decd | ||
|
babf6eaff7 | ||
|
34feee3567 | ||
|
cb97e94c4d | ||
|
eb56f1b486 | ||
|
41edd8317c | ||
|
32988bb7ca | ||
|
5df736dc6b | ||
|
7a2adae09c | ||
|
4c8920339d | ||
|
d15423dc65 | ||
|
6718528847 | ||
|
772bf6fcca | ||
|
82895bbce8 | ||
|
118b411b51 | ||
|
ea14fb1a27 | ||
|
b75a0ae85b | ||
|
0864b9ad89 | ||
|
3556015201 | ||
|
1a0f0b4b16 | ||
|
3015018142 | ||
|
1295e4a1d2 | ||
|
06fe810e92 | ||
|
efef0da03b | ||
|
704f508292 | ||
|
5dabeb558b | ||
|
0ba3d78b27 | ||
|
1dfd5386a7 | ||
|
2c8cf1c51c | ||
|
7d5d7d3c55 | ||
|
b50779f1e5 | ||
|
3784ec9e21 | ||
|
e55cae9496 | ||
|
9cc8ccac4e | ||
|
d20e4ad0e7 | ||
|
c66cfb28b5 | ||
|
73214a94ec | ||
|
b6bb5f05ec | ||
|
32dd7ef952 | ||
|
ab9f70930d | ||
|
fca18c26d3 | ||
|
3a77c7507b | ||
|
9694242989 | ||
|
2d008899b6 | ||
|
34c0f8cd04 | ||
|
390bb6dfa6 | ||
|
9306971620 | ||
|
d405ff5ffb | ||
|
e17e355247 | ||
|
23d7dc7892 | ||
|
98df8b265f | ||
|
11a4524c4c | ||
|
6c3ed54d87 | ||
|
1981f16715 | ||
|
aa766dc8ee | ||
|
5088caef1e | ||
|
ee9f63a161 | ||
|
2c3c0e8f1d | ||
|
4aaa26794b | ||
|
3d7470b01d | ||
|
ffb3f1ac35 | ||
|
8d399d6a2c | ||
|
b8cc60697b | ||
|
620de2e6b7 | ||
|
cafefb2a33 | ||
|
f7cb39c496 | ||
|
8b2c7a2b21 | ||
|
9d48d9924d | ||
|
fc657d98d2 | ||
|
0a89a8a2fa | ||
|
eaa32e4df4 | ||
|
1bb491857e | ||
|
3077c778c3 | ||
|
181f17ce6b | ||
|
31333f5706 | ||
|
aff69888da | ||
|
b455e807bc | ||
|
6b5b2698ce | ||
|
88794761d0 | ||
|
1ddfb11870 | ||
|
e3791aefdc | ||
|
b44be39252 | ||
|
4bc4a965d9 | ||
|
3a644d7bfc | ||
|
401f7fec17 | ||
|
95654955b1 | ||
|
0be3154cbe | ||
|
a04c69f787 | ||
|
256ed6a106 | ||
|
fc2e08c882 | ||
|
905a52ea61 | ||
|
d6e14b84db | ||
|
2ba6023926 | ||
|
5ed9e3e3ae | ||
|
5757eb7036 | ||
|
f7b942fa42 | ||
|
d54976e461 | ||
|
aa0db24d0d | ||
|
1cb0461991 | ||
|
11c9a53872 | ||
|
4f07867f8f | ||
|
a5caf57116 | ||
|
76b16d676e | ||
|
37327185b1 | ||
|
3a511842c1 | ||
|
eb241b3504 | ||
|
8859f46e5c | ||
|
135982e9d0 | ||
|
cb6873843f | ||
|
8b3820fb28 | ||
|
4c72a563da | ||
|
56817d4e69 | ||
|
de6c6f9bfd | ||
|
d3be8726fc | ||
|
dac8d711d1 | ||
|
c71b90cecf | ||
|
2cebdc3113 | ||
|
dd7c56a324 | ||
|
2f4dedfba2 | ||
|
81abae875f | ||
|
819bdea0c6 | ||
|
26f80b0ec5 | ||
|
0984fe8370 | ||
|
87636fc887 | ||
|
fdd2d503d9 | ||
|
25b56ada48 | ||
|
b1d4f119a7 | ||
|
a8149fe9bb | ||
|
b796ab3207 | ||
|
ddc29ac853 | ||
|
3e8172af13 | ||
|
78d0ff9793 | ||
|
3c0750a2a0 | ||
|
085d7ad37a | ||
|
9031d2478b | ||
|
64effb4679 | ||
|
7515ed2704 | ||
|
14c4be87aa | ||
|
af7345b3cd | ||
|
78491e71f6 | ||
|
7f73b51cb9 | ||
|
11ac636b74 | ||
|
0e5f528183 | ||
|
8065f6fd5f | ||
|
db5db28d71 | ||
|
37b85b16c3 | ||
|
dfc631cc37 | ||
|
e624ed8c51 | ||
|
cc76a4a665 | ||
|
3b47edc3b9 | ||
|
b78899d62c | ||
|
f38a15ee0d | ||
|
0e391a3d78 | ||
|
65d7e478ac | ||
|
e23e9e1387 | ||
|
6658217865 | ||
|
8bc8c4bcae | ||
|
5c8878353f | ||
|
de6eb99d41 | ||
|
8a277e767a | ||
|
aefa889ee4 | ||
|
63fd867757 | ||
|
fcea4005eb | ||
|
31523d769a | ||
|
432c40fb0c | ||
|
7303abcda6 | ||
|
eca9b2fcbf | ||
|
0fc9cb9e73 | ||
|
a199432b5c | ||
|
6ca058c3a1 | ||
|
1c59913e7a |
916 changed files with 54597 additions and 50314 deletions
|
@ -16,7 +16,7 @@ COMMENT_SERVER_NAME=Odysee
|
||||||
SEARCH_SERVER_API=https://lighthouse.odysee.com/search
|
SEARCH_SERVER_API=https://lighthouse.odysee.com/search
|
||||||
SOCKETY_SERVER_API=wss://sockety.odysee.com/ws
|
SOCKETY_SERVER_API=wss://sockety.odysee.com/ws
|
||||||
THUMBNAIL_CDN_URL=https://image-processor.vanwanet.com/optimize/
|
THUMBNAIL_CDN_URL=https://image-processor.vanwanet.com/optimize/
|
||||||
WELCOME_VERSION=1.0
|
WELCOME_VERSION=1.2
|
||||||
|
|
||||||
# STRIPE
|
# STRIPE
|
||||||
# STRIPE_PUBLIC_KEY='pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
|
# STRIPE_PUBLIC_KEY='pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
|
||||||
|
@ -35,25 +35,19 @@ SITE_CANONICAL_URL=https://lbry.tv
|
||||||
## Custom Site info
|
## Custom Site info
|
||||||
DOMAIN=lbry.tv
|
DOMAIN=lbry.tv
|
||||||
URL=https://lbry.tv
|
URL=https://lbry.tv
|
||||||
SITE_TITLE=lbry.tv
|
SITE_TITLE=LBRY
|
||||||
SITE_NAME=lbry.tv
|
SITE_NAME=LBRY
|
||||||
SITE_DESCRIPTION=Meet LBRY, an open, free, and community-controlled content wonderland.
|
SITE_DESCRIPTION=Meet LBRY, an open, free, and community-controlled content wonderland.
|
||||||
SITE_HELP_EMAIL=help@lbry.com
|
SITE_HELP_EMAIL=help@lbry.com
|
||||||
LOGO_TITLE=lbry.tv
|
LOGO_TITLE=LBRY
|
||||||
|
CLOUD_CONNECT_SITE_NAME=Odysee
|
||||||
## Social media
|
## Social media
|
||||||
TWITTER_ACCOUNT=LBRYcom
|
TWITTER_ACCOUNT=LBRYcom
|
||||||
BRANDED_SITE=odysee
|
BRANDED_SITE=odysee
|
||||||
|
|
||||||
## IMAGE ASSETS
|
## OLD IMAGE ASSETS
|
||||||
YRBL_HAPPY_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a
|
#YRBL_HAPPY_IMG_URL=https://player.odysee.com/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a
|
||||||
YRBL_SAD_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
|
#YRBL_SAD_IMG_URL=https://player.odysee.com/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
|
||||||
#LOGIN_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/login/b671946e911c66c5fa7233afb35de2badd9eceb8/0e1d81
|
|
||||||
#LOGO=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
|
|
||||||
#LOGO_TEXT_LIGHT=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
|
|
||||||
#LOGO_TEXT_DARK=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
|
|
||||||
#AVATAR_DEFAULT=
|
|
||||||
#MISSING_THUMB_DEFAULT=
|
|
||||||
#FAVICON=
|
|
||||||
|
|
||||||
# LOCALE
|
# LOCALE
|
||||||
DEFAULT_LANGUAGE=en
|
DEFAULT_LANGUAGE=en
|
||||||
|
@ -78,8 +72,8 @@ SIMPLE_SITE=false
|
||||||
#BRANDED_SITE
|
#BRANDED_SITE
|
||||||
|
|
||||||
ENABLE_COMMENT_REACTIONS=true
|
ENABLE_COMMENT_REACTIONS=true
|
||||||
ENABLE_FILE_REACTIONS=false
|
ENABLE_FILE_REACTIONS=true
|
||||||
ENABLE_CREATOR_REACTIONS=false
|
ENABLE_CREATOR_REACTIONS=true
|
||||||
ENABLE_NO_SOURCE_CLAIMS=false
|
ENABLE_NO_SOURCE_CLAIMS=false
|
||||||
ENABLE_PREROLL_ADS=false
|
ENABLE_PREROLL_ADS=false
|
||||||
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS=4
|
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS=4
|
||||||
|
@ -88,7 +82,7 @@ WEB_PUBLISH_SIZE_LIMIT_GB=4
|
||||||
LOADING_BAR_COLOR=#2bbb90
|
LOADING_BAR_COLOR=#2bbb90
|
||||||
LIGHTHOUSE_DEFAULT_TYPES=audio,video,text,image,application
|
LIGHTHOUSE_DEFAULT_TYPES=audio,video,text,image,application
|
||||||
|
|
||||||
SHOW_ADS=true
|
SHOW_ADS=false
|
||||||
|
|
||||||
## SIMPLE_SITE REPLACEMENTS
|
## SIMPLE_SITE REPLACEMENTS
|
||||||
ENABLE_MATURE=true
|
ENABLE_MATURE=true
|
||||||
|
|
25
.flowconfig
25
.flowconfig
|
@ -2,18 +2,13 @@
|
||||||
.*\.typeface\.json
|
.*\.typeface\.json
|
||||||
.*/node_modules/findup/.*
|
.*/node_modules/findup/.*
|
||||||
.*/node_modules/react-plastic/.*
|
.*/node_modules/react-plastic/.*
|
||||||
|
.*/node_modules/raf-schd/.*
|
||||||
|
.*/node_modules/react-beautiful-dnd/.*
|
||||||
|
.*/node_modules/resolve/test/.*
|
||||||
|
|
||||||
[include]
|
[include]
|
||||||
|
|
||||||
[libs]
|
[libs]
|
||||||
./flow-typed
|
|
||||||
node_modules/lbry-redux/flow-typed/
|
|
||||||
node_modules/lbryinc/flow-typed/
|
|
||||||
|
|
||||||
[untyped]
|
|
||||||
.*/node_modules/lbry-redux
|
|
||||||
.*/node_modules/lbryinc
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|
||||||
|
@ -31,7 +26,7 @@ module.name_mapper='^modal\(.*\)$' -> '<PROJECT_ROOT>/ui/modal\1'
|
||||||
module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/ui/app\1'
|
module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/ui/app\1'
|
||||||
module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/ui/native\1'
|
module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/ui/native\1'
|
||||||
module.name_mapper='^analytics\(.*\)$' -> '<PROJECT_ROOT>/ui/analytics\1'
|
module.name_mapper='^analytics\(.*\)$' -> '<PROJECT_ROOT>/ui/analytics\1'
|
||||||
module.name_mapper='^recsys\(.*\)$' -> '<PROJECT_ROOT>/ui/recsys\1'
|
module.name_mapper='^recsys\(.*\)$' -> '<PROJECT_ROOT>/extras/recsys\1'
|
||||||
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/ui/rewards\1'
|
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/ui/rewards\1'
|
||||||
module.name_mapper='^i18n\(.*\)$' -> '<PROJECT_ROOT>/ui/i18n\1'
|
module.name_mapper='^i18n\(.*\)$' -> '<PROJECT_ROOT>/ui/i18n\1'
|
||||||
module.name_mapper='^effects\(.*\)$' -> '<PROJECT_ROOT>/ui/effects\1'
|
module.name_mapper='^effects\(.*\)$' -> '<PROJECT_ROOT>/ui/effects\1'
|
||||||
|
@ -41,5 +36,17 @@ module.name_mapper='^web\/component\(.*\)$' -> '<PROJECT_ROOT>/web/component\1'
|
||||||
module.name_mapper='^web\/effects\(.*\)$' -> '<PROJECT_ROOT>/web/effects\1'
|
module.name_mapper='^web\/effects\(.*\)$' -> '<PROJECT_ROOT>/web/effects\1'
|
||||||
module.name_mapper='^web\/page\(.*\)$' -> '<PROJECT_ROOT>/web/page\1'
|
module.name_mapper='^web\/page\(.*\)$' -> '<PROJECT_ROOT>/web/page\1'
|
||||||
module.name_mapper='^homepage\(.*\)$' -> '<PROJECT_ROOT>/ui/util/homepage\1'
|
module.name_mapper='^homepage\(.*\)$' -> '<PROJECT_ROOT>/ui/util/homepage\1'
|
||||||
|
module.name_mapper='^scss\/component\(.*\)$' -> '<PROJECT_ROOT>/ui/scss/component/\1'
|
||||||
|
|
||||||
|
esproposal.optional_chaining=enable
|
||||||
|
|
||||||
|
; Extensions
|
||||||
|
module.file_ext=.js
|
||||||
|
module.file_ext=.jsx
|
||||||
|
module.file_ext=.json
|
||||||
|
module.file_ext=.css
|
||||||
|
module.file_ext=.scss
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[strict]
|
[strict]
|
||||||
|
|
63
.github/workflows/deploy.yml
vendored
63
.github/workflows/deploy.yml
vendored
|
@ -11,8 +11,9 @@ jobs:
|
||||||
name: lint
|
name: lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: Borales/actions-yarn@v2.3.0
|
- run: corepack enable
|
||||||
|
- run: yarn
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
@ -20,23 +21,39 @@ jobs:
|
||||||
name: 'build'
|
name: 'build'
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [14.x]
|
node-version: [16.x]
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
- macos-latest
|
- macos-latest
|
||||||
- windows-latest
|
- windows-latest
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v2-beta
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- run: corepack enable
|
||||||
|
|
||||||
- uses: maxim-lobanov/setup-xcode@v1
|
- uses: maxim-lobanov/setup-xcode@v1
|
||||||
if: startsWith(runner.os, 'mac')
|
if: startsWith(runner.os, 'mac')
|
||||||
with:
|
with:
|
||||||
xcode-version: '10.3.0'
|
xcode-version: '13.1.0'
|
||||||
|
# This is gonna be hacky.
|
||||||
|
# Github made us upgrade xcode, which would force an upgrade of electron-builder to fix mac.
|
||||||
|
# But there were bugs with copyfiles / extraFiles that kept seeing duplicates erroring on ln.
|
||||||
|
# A flag USE_HARD_LINKS=false in electron-builder.json was suggested in comments, but that broke windows builds.
|
||||||
|
# So for now we'll install python2 on mac and make sure it can find it.
|
||||||
|
# Remove this after successfully upgrading electron-builder.
|
||||||
|
# HACK part 1
|
||||||
|
- uses: Homebrew/actions/setup-homebrew@master
|
||||||
|
if: startsWith(runner.os, 'mac')
|
||||||
|
# HACK part 2
|
||||||
|
- name: Install Python2
|
||||||
|
if: startsWith(runner.os, 'mac')
|
||||||
|
run: |
|
||||||
|
/bin/bash -c "$(curl -fsSL https://github.com/alfredapp/dependency-scripts/raw/main/scripts/install-python2.sh)"
|
||||||
|
echo "PYTHON_PATH=/usr/local/bin/python" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Download blockchain headers
|
- name: Download blockchain headers
|
||||||
run: |
|
run: |
|
||||||
|
@ -46,17 +63,17 @@ jobs:
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
yarn global add cross-env
|
yarn dlx cross-env
|
||||||
yarn
|
yarn --network-timeout 600000
|
||||||
yarn build
|
yarn build
|
||||||
node ./build/afterSignHook.js
|
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN_NEW }}
|
||||||
NOTARIZATION_USERNAME: ${{ secrets.NOTARIZATION_USERNAME }}
|
NOTARIZATION_USERNAME: ${{ secrets.NOTARIZATION_USERNAME }}
|
||||||
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
|
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
|
||||||
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
|
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||||
WIN_CSC_LINK: https://s3.amazonaws.com/files.lbry.io/cert/win-csc-2020-2021-08.p12
|
|
||||||
|
WIN_CSC_LINK: https://raw.githubusercontent.com/lbryio/lbry-desktop/master/build/cert2023.pfx
|
||||||
CSC_LINK: https://s3.amazonaws.com/files.lbry.io/cert/osx-csc-2021-2022.p12
|
CSC_LINK: https://s3.amazonaws.com/files.lbry.io/cert/osx-csc-2021-2022.p12
|
||||||
|
|
||||||
# UI
|
# UI
|
||||||
|
@ -69,8 +86,6 @@ jobs:
|
||||||
SITE_TITLE: lbry.tv
|
SITE_TITLE: lbry.tv
|
||||||
SITE_NAME: lbry.tv
|
SITE_NAME: lbry.tv
|
||||||
SHOW_ADS: false
|
SHOW_ADS: false
|
||||||
YRBL_HAPPY_IMG_URL: https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a
|
|
||||||
YRBL_SAD_IMG_URL: https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
|
|
||||||
ENABLE_COMMENT_REACTIONS: true
|
ENABLE_COMMENT_REACTIONS: true
|
||||||
ENABLE_NO_SOURCE_CLAIMS: false
|
ENABLE_NO_SOURCE_CLAIMS: false
|
||||||
|
|
||||||
|
@ -78,23 +93,35 @@ jobs:
|
||||||
KNOWN_APP_DOMAINS: lbry.tv,lbry.lat,odysee.com
|
KNOWN_APP_DOMAINS: lbry.tv,lbry.lat,odysee.com
|
||||||
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS: 0
|
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS: 0
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2.2.4
|
||||||
if: |
|
if: |
|
||||||
startsWith(runner.os, 'linux')
|
startsWith(runner.os, 'linux')
|
||||||
with:
|
with:
|
||||||
name: Linux
|
name: Linux
|
||||||
path: ./dist/electron/*.*
|
path: ./dist/electron/*.*
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2.2.4
|
||||||
if: |
|
if: |
|
||||||
startsWith(runner.os, 'mac')
|
startsWith(runner.os, 'mac')
|
||||||
with:
|
with:
|
||||||
name: macOS
|
name: macOS
|
||||||
path: ./dist/electron/*.*
|
path: ./dist/electron/*.*
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2.2.4
|
||||||
if: |
|
if: |
|
||||||
github.event.pull_request.head.repo.full_name == github.repository
|
startsWith(runner.os, 'windows')
|
||||||
with:
|
with:
|
||||||
name: Windows
|
name: Windows
|
||||||
path: ./dist/electron/*.*
|
path: ./dist/electron/*.*
|
||||||
|
- uses: jakejarvis/s3-sync-action@master
|
||||||
|
if: |
|
||||||
|
startsWith(runner.os, 'linux')
|
||||||
|
with:
|
||||||
|
args: --acl public-read --follow-symlinks --exclude '*' --include '*.deb' --include '*.AppImage' --include '*.dmg'
|
||||||
|
env:
|
||||||
|
AWS_S3_BUCKET: ${{ secrets.ARTIFACTS_BUCKET }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.ARTIFACTS_KEY }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTIFACTS_SECRET }}
|
||||||
|
AWS_REGION: 'us-east-1'
|
||||||
|
SOURCE_DIR: 'dist/electron'
|
||||||
|
DEST_DIR: 'app/release'
|
||||||
|
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -33,6 +33,12 @@ package-lock.json
|
||||||
!/custom/robots.disallowall
|
!/custom/robots.disallowall
|
||||||
!/custom/robots.allowall
|
!/custom/robots.allowall
|
||||||
.env
|
.env
|
||||||
.env.ody
|
!.env.ody
|
||||||
.env.desktop
|
.env.desktop
|
||||||
.env.lbrytv
|
.env.lbrytv
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
!.yarn/releases
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
{
|
{
|
||||||
"linters": {
|
"linters": {
|
||||||
"ui/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"],
|
"ui/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"],
|
||||||
"web/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"],
|
"ui/**/*.{js,jsx}": ["eslint", "flow focus-check --color always", "git add"]
|
||||||
"ui/**/*.{js,jsx}": ["eslint", "flow focus-check --color always", "git add"],
|
|
||||||
"web/**/*.{js,jsx}": ["eslint", "git add"]
|
|
||||||
},
|
},
|
||||||
"ignore": ["node_modules", "web/dist/**/*", "dist/**/*", "package-lock.json"]
|
"ignore": ["node_modules", "dist/**/*", "package-lock.json"]
|
||||||
}
|
}
|
||||||
|
|
550
.yarn/plugins/@yarnpkg/plugin-version.cjs
vendored
Normal file
550
.yarn/plugins/@yarnpkg/plugin-version.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Executable file
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
0
.yarn/versions/17d7e90d.yml
vendored
Normal file
0
.yarn/versions/17d7e90d.yml
vendored
Normal file
0
.yarn/versions/33178102.yml
vendored
Normal file
0
.yarn/versions/33178102.yml
vendored
Normal file
0
.yarn/versions/35f2125e.yml
vendored
Normal file
0
.yarn/versions/35f2125e.yml
vendored
Normal file
0
.yarn/versions/4f9fb046.yml
vendored
Normal file
0
.yarn/versions/4f9fb046.yml
vendored
Normal file
0
.yarn/versions/5bc94294.yml
vendored
Normal file
0
.yarn/versions/5bc94294.yml
vendored
Normal file
0
.yarn/versions/5f1212ad.yml
vendored
Normal file
0
.yarn/versions/5f1212ad.yml
vendored
Normal file
0
.yarn/versions/5f4cac99.yml
vendored
Normal file
0
.yarn/versions/5f4cac99.yml
vendored
Normal file
0
.yarn/versions/6b35c994.yml
vendored
Normal file
0
.yarn/versions/6b35c994.yml
vendored
Normal file
0
.yarn/versions/6be5ab70.yml
vendored
Normal file
0
.yarn/versions/6be5ab70.yml
vendored
Normal file
0
.yarn/versions/86ac1afd.yml
vendored
Normal file
0
.yarn/versions/86ac1afd.yml
vendored
Normal file
0
.yarn/versions/8e384637.yml
vendored
Normal file
0
.yarn/versions/8e384637.yml
vendored
Normal file
0
.yarn/versions/909c3734.yml
vendored
Normal file
0
.yarn/versions/909c3734.yml
vendored
Normal file
0
.yarn/versions/951a8d12.yml
vendored
Normal file
0
.yarn/versions/951a8d12.yml
vendored
Normal file
0
.yarn/versions/97e7141a.yml
vendored
Normal file
0
.yarn/versions/97e7141a.yml
vendored
Normal file
0
.yarn/versions/ac69bc5f.yml
vendored
Normal file
0
.yarn/versions/ac69bc5f.yml
vendored
Normal file
0
.yarn/versions/c6e2b914.yml
vendored
Normal file
0
.yarn/versions/c6e2b914.yml
vendored
Normal file
0
.yarn/versions/d1a18cef.yml
vendored
Normal file
0
.yarn/versions/d1a18cef.yml
vendored
Normal file
0
.yarn/versions/ec3a9ddf.yml
vendored
Normal file
0
.yarn/versions/ec3a9ddf.yml
vendored
Normal file
0
.yarn/versions/fc1fde84.yml
vendored
Normal file
0
.yarn/versions/fc1fde84.yml
vendored
Normal file
0
.yarn/versions/fc597c00.yml
vendored
Normal file
0
.yarn/versions/fc597c00.yml
vendored
Normal file
7
.yarnrc.yml
Normal file
7
.yarnrc.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||||
|
spec: "@yarnpkg/plugin-version"
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-3.2.0.cjs
|
241
CHANGELOG.md
241
CHANGELOG.md
|
@ -1,38 +1,262 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased for Desktop]
|
## [0.53.9] - [2023-2-8]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated lbrynet to [0.113.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.113.0)
|
||||||
|
|
||||||
|
## [0.53.8] - [2022-11-17]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Selecting a large file in publish no longer crashes ([#7736](https://github.com/lbryio/lbry-desktop/pull/7736))
|
||||||
|
- Unfollowing unpublished channels ([#7737](https://github.com/lbryio/lbry-desktop/pull/7737))
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated xcode to 13.1 and hacked a fix for release ([#7736](https://github.com/lbryio/lbry-desktop/pull/7736))
|
||||||
|
|
||||||
|
## [0.53.7] - [2022-11-10]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Added direct replying to notifications _community pr!_ ([#6935](https://github.com/lbryio/lbry-desktop/pull/6935))
|
- 'Collections' to txo filter _community pr!_ ([#7711](https://github.com/lbryio/lbry-desktop/pull/7711))
|
||||||
|
- Swap comment servers _community pr!_ ([#7670](https://github.com/lbryio/lbry-desktop/pull/7670))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Thumbnails no longer disable publish ([#7714](https://github.com/lbryio/lbry-desktop/pull/7714))
|
||||||
|
- Publishing posts were empty ([#7715](https://github.com/lbryio/lbry-desktop/pull/7715))
|
||||||
|
- Minor layout fixes _community pr!_ ([#7709](https://github.com/lbryio/lbry-desktop/pull/7709))
|
||||||
|
- Comment section buttons layout ([#7716](https://github.com/lbryio/lbry-desktop/pull/7716))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Removed watchman and its errors ([#7710](https://github.com/lbryio/lbry-desktop/pull/7710))
|
||||||
|
- Updated lbrynet to [0.112.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.112.0)
|
||||||
|
|
||||||
|
## [0.53.6] - [2022-10-21]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Make thumbnails optional ([#7690](https://github.com/lbryio/lbry-desktop/pull/7690))
|
||||||
|
- Show downloads newest first ([#7684](https://github.com/lbryio/lbry-desktop/pull/7684))
|
||||||
|
- Only allow images in image uploader ([#7672](https://github.com/lbryio/lbry-desktop/pull/7672))
|
||||||
|
- Fixed bug with csv exports ([#7697](https://github.com/lbryio/lbry-desktop/pull/7697))
|
||||||
|
- Fixed various upload bugs including transcoding ([#7688](https://github.com/lbryio/lbry-desktop/pull/7688))
|
||||||
|
- Fallback for files with no extension ([#7704](https://github.com/lbryio/lbry-desktop/pull/7704))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Upgraded Electron to v17.2.0 ([#7703](https://github.com/lbryio/lbry-desktop/pull/7703))
|
||||||
|
- Upgraded Electron to v17.0.0 ([#7691](https://github.com/lbryio/lbry-desktop/pull/7691))
|
||||||
|
- Updated lbrynet to [0.111.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.111.0)
|
||||||
|
|
||||||
|
## [0.53.5] - [2022-08-26]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Checkbox to disable background wallpaper ([#7630](https://github.com/lbryio/lbry-desktop/pull/7630))
|
||||||
|
- Handle content blocking from hub ([#7665](https://github.com/lbryio/lbry-desktop/pull/7665))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Better handle decimals liquidating supports ([#7648](https://github.com/lbryio/lbry-desktop/pull/7648))
|
||||||
|
- Better handle cover uploads ([#7647](https://github.com/lbryio/lbry-desktop/pull/7647))
|
||||||
|
- Use default path when first choosing file on windows ([#7625](https://github.com/lbryio/lbry-desktop/pull/7625))
|
||||||
|
- Emoji button hover ([#7620](https://github.com/lbryio/lbry-desktop/pull/7620))
|
||||||
|
- Prevent infinite retries on thumbs ([#7618](https://github.com/lbryio/lbry-desktop/pull/7618))
|
||||||
|
- Double splash/error on app startup ([#7615](https://github.com/lbryio/lbry-desktop/pull/7615))
|
||||||
|
- App updates are now more coherent, also debs work. ([#7502](https://github.com/lbryio/lbry-desktop/pull/7502))
|
||||||
|
- Better handle many channels moderation calls at startup ([#7674](https://github.com/lbryio/lbry-desktop/pull/7674))
|
||||||
|
- Fix mobile floating viewer position ([#7677](https://github.com/lbryio/lbry-desktop/pull/7677))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Upgraded Electron to v15.5.5 ([#7614](https://github.com/lbryio/lbry-desktop/pull/7614))
|
||||||
|
- Upgraded to lbrynet v0.110.0 ([#7680](https://github.com/lbryio/lbry-desktop/pull/7680))
|
||||||
|
|
||||||
|
|
||||||
|
## [0.53.4] - [2022-06-10]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Add top in language category for non-english on homepage ([#7585](https://github.com/lbryio/lbry-desktop/pull/7585))
|
||||||
|
- Auto hosting in settings and hosting first run page ([#7598](https://github.com/lbryio/lbry-desktop/pull/7598))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated lbry-sdk to [0.107.2](https://github.com/lbryio/lbry-sdk/releases/tag/v0.107.2)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Better handle empty collections ([#7571](https://github.com/lbryio/lbry-desktop/pull/7571))
|
||||||
|
- Better handle thumbnails in uploads/collections ([#7574](https://github.com/lbryio/lbry-desktop/pull/7574))
|
||||||
|
- Work towards supporting collections of any claim type ([#7578](https://github.com/lbryio/lbry-desktop/pull/7578))
|
||||||
|
- Improve handling of downed custom servers on startup ([#7593](https://github.com/lbryio/lbry-desktop/pull/7593))
|
||||||
|
- Hide watch progress in related if being played ([#7606](https://github.com/lbryio/lbry-desktop/pull/7606))
|
||||||
|
- IPC disk space calls wait for daemon ready; refresh on vis. component load ([#7610](https://github.com/lbryio/lbry-desktop/pull/7610))
|
||||||
|
|
||||||
|
## [0.53.3] - [2022-04-27]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Reverted lbry.tv changes that broke production login ([#7569](https://github.com/lbryio/lbry-desktop/pull/7569))
|
||||||
|
- Reverted lbry.tv changes that broke login ([#7570](https://github.com/lbryio/lbry-desktop/pull/7570))
|
||||||
|
|
||||||
|
## [0.53.2] - [2022-04-26]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Upgraded Yarn to Berry branch ([#7530](https://github.com/lbryio/lbry-desktop/pull/7530))
|
||||||
|
- Removed some lbrytv references ([#7560](https://github.com/lbryio/lbry-desktop/pull/7560))
|
||||||
|
- Removed some lbrytv player references ([#7552](https://github.com/lbryio/lbry-desktop/pull/7552))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Repost style issues ([#7559](https://github.com/lbryio/lbry-desktop/pull/7559))
|
||||||
|
- Disappearing sidebar thumbs ([#7556](https://github.com/lbryio/lbry-desktop/pull/7556))
|
||||||
|
- Restore tags sidebar link ([#7555](https://github.com/lbryio/lbry-desktop/pull/7555))
|
||||||
|
- Playlist view link no longer crashes ([#7552](https://github.com/lbryio/lbry-desktop/pull/7552))
|
||||||
|
|
||||||
|
## [0.53.1] - [2022-04-22]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Uploads: show placeholder when loading page _community pr!_ ([#7531](https://github.com/lbryio/lbry-desktop/pull/7531))
|
||||||
|
- Sidebar channel search _styles pr_ ([#7542](https://github.com/lbryio/lbry-desktop/pull/7542))
|
||||||
|
- Viewed content progress indicator on thumbnail part 1 ([#7541](https://github.com/lbryio/lbry-desktop/pull/7541))
|
||||||
|
- Viewed content progress indicator on thumbnail part 2 ([#7547](https://github.com/lbryio/lbry-desktop/pull/7547))
|
||||||
|
- Ability to search through publishes ([#7535](https://github.com/lbryio/lbry-desktop/pull/7535))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Large styles revamp following odysee _styles pr_ ([#7542](https://github.com/lbryio/lbry-desktop/pull/7542))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix bad rerender on homepage _styles pr_ ([#7542](https://github.com/lbryio/lbry-desktop/pull/7542))
|
||||||
|
- Fix post-editor preview mode _community pr!_ ([#7532](https://github.com/lbryio/lbry-desktop/pull/7532))
|
||||||
|
- Fix send-tip default tab ([#7533](https://github.com/lbryio/lbry-desktop/pull/7533))
|
||||||
|
|
||||||
|
## [0.52.6] - [2022-04-04]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Discover page medium duration filter ([#7506](https://github.com/lbryio/lbry-desktop/pull/7506))
|
||||||
|
- Keep last used collection for Add To ([#7491](https://github.com/lbryio/lbry-desktop/pull/7491))
|
||||||
|
- Disk space functionality on mac / windows ([#7500](https://github.com/lbryio/lbry-desktop/pull/7500))
|
||||||
|
- Enable renaming private collections ([#7519](https://github.com/lbryio/lbry-desktop/pull/7519))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Some upgrade modal improvements ([#7488](https://github.com/lbryio/lbry-desktop/pull/7488))
|
||||||
|
- Updated lbry-sdk to [0.107.1](https://github.com/lbryio/lbry-sdk/releases/tag/v0.107.1)
|
||||||
|
- New YRBL!; facelift for first run ([#7527](https://github.com/lbryio/lbry-desktop/pull/7527))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Failed comment count increment ([#7510](https://github.com/lbryio/lbry-desktop/pull/7510))
|
||||||
|
- App crash playing media on older windows versions by updating electron ([#7509](https://github.com/lbryio/lbry-desktop/pull/7509))
|
||||||
|
- Local build failures on mac ([#7497](https://github.com/lbryio/lbry-desktop/pull/7497))
|
||||||
|
- Language change now rerenders whole app ([#7504](https://github.com/lbryio/lbry-desktop/pull/7504))
|
||||||
|
- Mac notarization ([#7518](https://github.com/lbryio/lbry-desktop/pull/7518))
|
||||||
|
- Prevent crash when deleting last comment reply ([#7526](https://github.com/lbryio/lbry-desktop/pull/7526))
|
||||||
|
|
||||||
|
## [0.52.5] - [2022-02-25]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- New data hosting ux ([#7493](https://github.com/lbryio/lbry-desktop/pull/7493))
|
||||||
|
- Fix markdown guide button ([#7485](https://github.com/lbryio/lbry-desktop/pull/7485))
|
||||||
|
|
||||||
|
## [0.52.4] - [2022-02-15]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed active channel ([#7481](https://github.com/lbryio/lbry-desktop/pull/7481))
|
||||||
|
- Remove extra search button in header ([#7482](https://github.com/lbryio/lbry-desktop/pull/7482))
|
||||||
|
|
||||||
|
## [0.52.3] - [2022-02-15]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed comment editing and pinning ([#7476](https://github.com/lbryio/lbry-desktop/pull/7476))
|
||||||
|
- Fixed mac header ([#7479](https://github.com/lbryio/lbry-desktop/pull/7479))
|
||||||
|
- Fixed markdown display and lbry url embedding ([#7474](https://github.com/lbryio/lbry-desktop/pull/7474))
|
||||||
|
|
||||||
|
## [0.52.2] - [2022-02-11]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Reenabled generating thumbs from video ([#7384](https://github.com/lbryio/lbry-desktop/pull/7409))
|
||||||
|
- Brought in playlist drag and drop playlist reordering _odysee team!_ ([#7442](https://github.com/lbryio/lbry-desktop/pull/7442))
|
||||||
|
- Added duration overlays to ClaimPreview component ([#7420](https://github.com/lbryio/lbry-desktop/pull/7420))
|
||||||
|
- Some Horizontal Scroll groundwork from _odysee team!_
|
||||||
|
- Comment Emotes and Stickers and Mentions refactors from _odysee team!_ ([#7435](https://github.com/lbryio/lbry-desktop/pull/7435))
|
||||||
|
- Seek forward and back from _odysee team!_ () ([#7460](https://github.com/lbryio/lbry-desktop/pull/7460))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Upgraded Electron to v15 ([#7384](https://github.com/lbryio/lbry-desktop/pull/7384))
|
||||||
|
- Performance improvements in some selectors ([#7370](https://github.com/lbryio/lbry-desktop/pull/7370))
|
||||||
|
- More Header refactoring from _odysee team!_ ([#7441](https://github.com/lbryio/lbry-desktop/pull/7441))
|
||||||
|
- Header refactoring from _odysee team!_ ([#7440](https://github.com/lbryio/lbry-desktop/pull/7440))
|
||||||
|
- Data hosting ui _incomplete_ ([#7438](https://github.com/lbryio/lbry-desktop/pull/7438))
|
||||||
|
- Updated c: control tags from _odysee team!_ ([#7433](https://github.com/lbryio/lbry-desktop/pull/7433))
|
||||||
|
- Nav keycodes (alt+left) no longer navigate while textarea is focused ([#7458](https://github.com/lbryio/lbry-desktop/pull/7458))
|
||||||
|
- Improved comment-server selection ui/ux ([#7455](https://github.com/lbryio/lbry-desktop/pull/7455))
|
||||||
|
- Improved Data Hosting settings ([#7563](https://github.com/lbryio/lbry-desktop/pull/7563))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Several fallout bugs from recent changes
|
||||||
|
|
||||||
|
## [0.52.1]
|
||||||
|
|
||||||
|
### Skipped patch version
|
||||||
|
|
||||||
|
## [0.52.0] - [2021-12-31]
|
||||||
|
|
||||||
|
### Compatibility
|
||||||
|
|
||||||
|
- Mac <= 10.13 (High Sierra) and Ubuntu <= 16 (Xenial) are no longer supported. If you upgrade, you will need to manually build and install your own lbrynet SDK
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Direct replying to notifications _community pr!_ ([#6935](https://github.com/lbryio/lbry-desktop/pull/6935))
|
||||||
- Added "Replay" option on autoplay countdown ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
- Added "Replay" option on autoplay countdown ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
||||||
- Added "Loop" option on Lists ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
- Added "Loop" option on Lists ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
||||||
- Added "Shuffle" option on Lists ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
- Added "Shuffle" option on Lists ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
||||||
- Added Play Next/Previous buttons (with shortcuts SHIFT+N/SHIFT+P) ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
- Added Play Next/Previous buttons (with shortcuts SHIFT+N/SHIFT+P) ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
||||||
- Added separate control for autoplay next on video player ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
- Separate control for autoplay next on video player ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
||||||
|
- Channel Mention selection ability while creating a comment ([#7151](https://github.com/lbryio/lbry-desktop/pull/7151))
|
||||||
|
- Disk space setting under Data Hosting ([#7266](https://github.com/lbryio/lbry-desktop/pull/7266))
|
||||||
|
- Paginated 'All Playlists' page ([#7268](https://github.com/lbryio/lbry-desktop/pull/7268))
|
||||||
|
- Expanded playlist ordering tools ([#7305](https://github.com/lbryio/lbry-desktop/pull/7305))
|
||||||
|
- Setting to upgrade to alpha prerelease builds ([#7353](https://github.com/lbryio/lbry-desktop/pull/7353))
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Changing the supported language from Filipino to Tagalog _community pr!_ ([#6951](https://github.com/lbryio/lbry-desktop/pull/6951))
|
- Changing the supported language from Filipino to Tagalog _community pr!_ ([#6951](https://github.com/lbryio/lbry-desktop/pull/6951))
|
||||||
- Don't show countdown to next item in list ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
- Don't show countdown to next item in list ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
||||||
- Changed "View List" popup option to link, so can be opened on a new tab ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
- Changed "View List" popup option to link, so can be opened on a new tab ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
||||||
|
- App reorganized to remove lbry-redux and lbryinc repository dependencies ([#7240](https://github.com/lbryio/lbry-desktop/pull/7240))
|
||||||
|
- Styling cleanup for file reactions ([#7251](https://github.com/lbryio/lbry-desktop/pull/7251))
|
||||||
|
- Change share url to odysee and allow custom share url in settings ([#7258](https://github.com/lbryio/lbry-desktop/pull/7258))
|
||||||
|
- Change Sign in/up to Cloud Connect for Odysee ([#7260](https://github.com/lbryio/lbry-desktop/pull/7260))
|
||||||
|
- Upgraded to lbrynet v0.106.0 ([#7315](https://github.com/lbryio/lbry-desktop/pull/7315))
|
||||||
|
- Upgraded Electron to v11.5.0 ([#7276](https://github.com/lbryio/lbry-desktop/pull/7276))
|
||||||
|
- Cleaner Discover page filters ([#7306](https://github.com/lbryio/lbry-desktop/pull/7306))
|
||||||
|
- Scroll bar styling ([#7314](https://github.com/lbryio/lbry-desktop/pull/7314))
|
||||||
|
- Remove pages for obsolete features like invites, rewards, swap ([#7330](https://github.com/lbryio/lbry-desktop/pull/7330))
|
||||||
|
- Change file repost to modal _community pr!_ ([#7341](https://github.com/lbryio/lbry-desktop/pull/7341))
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Clicking on the title of a floating player will take you back to the list ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
- Clicking on the title of a floating player will take you back to the list ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
|
||||||
- Fix floating player stopping on markdown or image files ([#7073](https://github.com/lbryio/lbry-desktop/pull/7073))
|
- Fix floating player stopping on markdown or image files ([#7073](https://github.com/lbryio/lbry-desktop/pull/7073))
|
||||||
- Fix list thumbnail upload ([#7074](https://github.com/lbryio/lbry-desktop/pull/7074))
|
- Fix list thumbnail upload ([#7074](https://github.com/lbryio/lbry-desktop/pull/7074))
|
||||||
|
- Stream Key is now hidden _community pr!_ ([#7127](https://github.com/lbryio/lbry-desktop/pull/7127))
|
||||||
|
- Fix playlist preview thumbnail ([#7178](https://github.com/lbryio/lbry-desktop/pull/7178)
|
||||||
|
- Fixed “Your Account” popup on mobile ([#7172](https://github.com/lbryio/lbry-desktop/pull/7172))
|
||||||
|
- Fix disable-support for comments ([#7245](https://github.com/lbryio/lbry-desktop/pull/7245))
|
||||||
|
- Fix Electron taking over .html files on linux ([#7291](https://github.com/lbryio/lbry-desktop/pull/7291))
|
||||||
|
- Fix floating player play/pause on drag _community pr!_ ([#7339](https://github.com/lbryio/lbry-desktop/pull/7339))
|
||||||
|
- Fix card dropdown menus triggering menu actions _community pr!_ ([#7335](https://github.com/lbryio/lbry-desktop/pull/7335))
|
||||||
|
|
||||||
## [0.51.2] - [2021-08-20]
|
## [0.51.2] - [2021-08-20]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Show currently active playing item on playlist _community pr!_ ([#6453](https://github.com/lbryio/lbry-desktop/pull/6453))
|
- Show currently active playing item on playlist _community pr!_ ([#6453](https://github.com/lbryio/lbry-desktop/pull/6453))
|
||||||
- Add watch later to hover action for last used playlist on popup _community pr!_ ([#6274](https://github.com/lbryio/lbry-desktop/pull/6274))
|
- Add watch later to hover action for last used playlist on popup _community pr!_ ([#6274](https://github.com/lbryio/lbry-desktop/pull/6274))
|
||||||
- Add confirmation on comment removal _community pr!_ ([#6563](https://github.com/lbryio/lbry-desktop/pull/6563))
|
- Add confirmation on comment removal _community pr!_ ([#6563](https://github.com/lbryio/lbry-desktop/pull/6563))
|
||||||
- Show on content page if a file is part of a playlist already _community pr!_([#6393](https://github.com/lbryio/lbry-desktop/pull/6393))
|
- Show on content page if a file is part of a playlist already _community pr!_([#6393](https://github.com/lbryio/lbry-desktop/pull/6393))
|
||||||
- Add filtering to playlists ([#6905](https://github.com/lbryio/lbry-desktop/pull/6905))
|
- Add filtering to playlists ([#6905](https://github.com/lbryio/lbry-desktop/pull/6905))
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Use Canonical Url for copy link ([#6500](https://github.com/lbryio/lbry-desktop/pull/6500))
|
- Use Canonical Url for copy link ([#6500](https://github.com/lbryio/lbry-desktop/pull/6500))
|
||||||
- Use better icon for copy link ([#6485](https://github.com/lbryio/lbry-desktop/pull/6485))
|
- Use better icon for copy link ([#6485](https://github.com/lbryio/lbry-desktop/pull/6485))
|
||||||
- Comments load paginated ([#6390](https://github.com/lbryio/lbry-desktop/pull/6390))
|
- Comments load paginated ([#6390](https://github.com/lbryio/lbry-desktop/pull/6390))
|
||||||
|
@ -43,6 +267,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Improved clickability of notification links _community pr!_ ([#6711](https://github.com/lbryio/lbry-desktop/pull/6711))
|
- Improved clickability of notification links _community pr!_ ([#6711](https://github.com/lbryio/lbry-desktop/pull/6711))
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- App now supports '#' and ':' for claimId separator ([#6496](https://github.com/lbryio/lbry-desktop/pull/6496))
|
- App now supports '#' and ':' for claimId separator ([#6496](https://github.com/lbryio/lbry-desktop/pull/6496))
|
||||||
- Fix "exact match" being applied to Recommended ([#6460](https://github.com/lbryio/lbry-desktop/pull/6460))
|
- Fix "exact match" being applied to Recommended ([#6460](https://github.com/lbryio/lbry-desktop/pull/6460))
|
||||||
- Fix upload button on creator analytics _community pr!_ ([#6458](https://github.com/lbryio/lbry-desktop/pull/6458))
|
- Fix upload button on creator analytics _community pr!_ ([#6458](https://github.com/lbryio/lbry-desktop/pull/6458))
|
||||||
|
@ -68,18 +293,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Enable sign up on desktop ([#6071](https://github.com/lbryio/lbry-desktop/issues/6071))
|
- Enable sign up on desktop ([#6071](https://github.com/lbryio/lbry-desktop/issues/6071))
|
||||||
|
|
||||||
## [0.51.0] - [2021-06-26]
|
## [0.51.0] - [2021-06-26]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Private and Publishable Playlists ([#6157](https://github.com/lbryio/lbry-desktop/pull/6157))
|
- Private and Publishable Playlists ([#6157](https://github.com/lbryio/lbry-desktop/pull/6157))
|
||||||
- Channel thumbnails in following side menu ([#6193](https://github.com/lbryio/lbry-desktop/pull/6193))
|
- Channel thumbnails in following side menu ([#6193](https://github.com/lbryio/lbry-desktop/pull/6193))
|
||||||
- Web is now PWA app ([#6120](https://github.com/lbryio/lbry-desktop/pull/6120))
|
- Web is now PWA app ([#6120](https://github.com/lbryio/lbry-desktop/pull/6120))
|
||||||
- Send a tip with your comment ([#5920](https://github.com/lbryio/lbry-desktop/issues/5920))
|
- Send a tip with your comment ([#5920](https://github.com/lbryio/lbry-desktop/issues/5920))
|
||||||
- Search for tags in search dropdown ([#5876](https://github.com/lbryio/lbry-desktop/issues/5876))
|
- Search for tags in search dropdown ([#5876](https://github.com/lbryio/lbry-desktop/issues/5876))
|
||||||
- Japanese, Afrikaans, Filipino, Thai and Vietnamese language support ([#5684](https://github.com/lbryio/lbry-desktop/issues/5684))
|
- Japanese, Afrikaans, Filipino, Thai and Vietnamese language support ([#5684](https://github.com/lbryio/lbry-desktop/issues/5684))
|
||||||
- Brazilian-Portuguese language support ([#5900](https://github.com/lbryio/lbry-desktop/issues/5900))
|
- Brazilian-Portuguese language support ([#5900](https://github.com/lbryio/lbry-desktop/issues/5900))
|
||||||
- Highlight comments made by content owner _community pr!_ ([#5744](https://github.com/lbryio/lbry-desktop/pull/5744))
|
- Highlight comments made by content owner _community pr!_ ([#5744](https://github.com/lbryio/lbry-desktop/pull/5744))
|
||||||
- Ability to report infringing content directly from the application ([#5808](https://github.com/lbryio/lbry-desktop/pull/5808))
|
- Ability to report infringing content directly from the application ([#5808](https://github.com/lbryio/lbry-desktop/pull/5808))
|
||||||
- Re-added ability to export wallet transactions ([#5899](https://github.com/lbryio/lbry-desktop/pull/5899))
|
- Re-added ability to export wallet transactions ([#5899](https://github.com/lbryio/lbry-desktop/pull/5899))
|
||||||
|
|
78
README.md
78
README.md
|
@ -1,8 +1,8 @@
|
||||||
<img width="40%" src="https://miro.medium.com/max/5198/1*bTVuL2THG_0mpwmE-n7Ezg.png" />
|
<img width="40%" src="https://miro.medium.com/max/5198/1*bTVuL2THG_0mpwmE-n7Ezg.png" />
|
||||||
|
|
||||||
# LBRY App - https://lbry.tv
|
# LBRY App
|
||||||
|
|
||||||
This repo contains the UI code that powers the official LBRY desktop app, as well as lbry.tv. The LBRY app is a graphical browser for the decentralized content marketplace provided by the
|
This repo contains the UI code that powers the official LBRY desktop app. The LBRY app is a graphical browser for the decentralized content marketplace provided by the
|
||||||
[LBRY](https://lbry.com) protocol. It is essentially the
|
[LBRY](https://lbry.com) protocol. It is essentially the
|
||||||
[lbry daemon](https://github.com/lbryio/lbry) bundled with a UI using
|
[lbry daemon](https://github.com/lbryio/lbry) bundled with a UI using
|
||||||
[Electron](https://electron.atom.io/).
|
[Electron](https://electron.atom.io/).
|
||||||
|
@ -65,26 +65,26 @@ _Note: If coming from a deb install, the directory structure is different and yo
|
||||||
|
|
||||||
| | Flatpak | Arch | Nixpkgs | ARM/ARM64 |
|
| | Flatpak | Arch | Nixpkgs | ARM/ARM64 |
|
||||||
| -------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------- |
|
| -------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------- |
|
||||||
| Latest Release | [FlatHub Page](https://flathub.org/apps/details/io.lbry.lbry-app) | [AUR Package](https://aur.archlinux.org/packages/lbry-app-bin/) | [Nixpkgs](https://search.nixos.org/packages?channel=unstable&show=lbry&query=lbry) | [Build Guide](https://lbry.tv/@LBRYarm:5) |
|
| Latest Release | [FlatHub Page](https://flathub.org/apps/details/io.lbry.lbry-app) | [AUR Package](https://aur.archlinux.org/packages/lbry-desktop-bin/) | [Nixpkgs](https://search.nixos.org/packages?channel=unstable&show=lbry&query=lbry) | [Build Guide](https://lbry.tv/@LBRYarm:5) |
|
||||||
| Maintainers | [@kcSeb](https://keybase.io/kcseb) | [@kcSeb](https://keybase.io/kcseb) | [@Enderger](https://github.com/enderger) | [@Madiator2011](https://github.com/kodxana) |
|
| Maintainers | N/A | [@RubenKelevra](https://github.com/RubenKelevra) | [@Enderger](https://github.com/enderger) | [@Madiator2011](https://github.com/kodxana) |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Double click the installed application to interact with the LBRY network.
|
Start the installed application to interact with the LBRY network.
|
||||||
|
|
||||||
## Running from Source
|
## Running from Source
|
||||||
|
|
||||||
You can run the web version (lbry.tv), the electron app, or both at the same time.
|
|
||||||
|
|
||||||
#### Prerequisites
|
#### Prerequisites
|
||||||
|
|
||||||
- [Git](https://git-scm.com/downloads)
|
- [Git](https://git-scm.com/downloads)
|
||||||
- [Node.js](https://nodejs.org/en/download/) (v14 required)
|
- [Node.js](https://nodejs.org/en/download/) (v16 required)
|
||||||
|
- [Corepack](https://nodejs.org/dist/latest-v17.x/docs/api/corepack.html) `npm i -g corepack` (Included in nodejs 14 LTS, 16 LTS and 17)
|
||||||
- [Yarn](https://yarnpkg.com/en/docs/install)
|
- [Yarn](https://yarnpkg.com/en/docs/install)
|
||||||
|
|
||||||
1. Clone (or [fork](https://help.github.com/articles/fork-a-repo/)) this repository: `git clone https://github.com/lbryio/lbry-desktop`
|
1. Clone (or [fork](https://help.github.com/articles/fork-a-repo/)) this repository: `git clone https://github.com/lbryio/lbry-desktop`
|
||||||
2. Change directory into the cloned repository: `cd lbry-desktop`
|
2. Change directory into the cloned repository: `cd lbry-desktop`
|
||||||
3. Install the dependencies: `yarn`
|
3. If corepack is not enabled, run `sudo corepack enable` (the sudo is necessary for system-wide installation, if you use container, nvm etc... you might not be forced to use it)
|
||||||
|
4. Install the dependencies: `yarn`
|
||||||
|
|
||||||
#### Run the electron app
|
#### Run the electron app
|
||||||
|
|
||||||
|
@ -92,66 +92,6 @@ You can run the web version (lbry.tv), the electron app, or both at the same tim
|
||||||
|
|
||||||
- If you want to build and launch the production app you can run `yarn build`. This will give you an executable inside the `/dist` folder. We use [electron-builder](https://github.com/electron-userland/electron-builder) to create distributable packages.
|
- If you want to build and launch the production app you can run `yarn build`. This will give you an executable inside the `/dist` folder. We use [electron-builder](https://github.com/electron-userland/electron-builder) to create distributable packages.
|
||||||
|
|
||||||
#### Run the web app for development
|
|
||||||
|
|
||||||
`yarn dev:web`
|
|
||||||
|
|
||||||
- This uses webpack-dev-server and includes hot-reloading. If you want to debug the [web server we use in production](https://github.com/lbryio/lbry-desktop/blob/master/web/index.js) you can run `yarn dev:web-server`. This starts a server at `localhost:1337` and does not include hot reloading.
|
|
||||||
|
|
||||||
#### Customize the web app
|
|
||||||
|
|
||||||
- In root directory, duplicate the .env.default file and rename it to .env then copy the code below and paste it anywhere in the .env file.
|
|
||||||
|
|
||||||
```
|
|
||||||
cp .env.defaults .env
|
|
||||||
nano .env
|
|
||||||
```
|
|
||||||
|
|
||||||
- To specify your own OG-IMAGE
|
|
||||||
You can either place a png named v2-og.png in the /custom folder or specify the OG_IMAGE_URL in .env
|
|
||||||
|
|
||||||
- To specify your own channels to be followed on first run
|
|
||||||
`AUTO_FOLLOW_URLS=lbry://@chan#123...a lbry://@chan2#456...a`
|
|
||||||
|
|
||||||
- If you want to customize the homepage content
|
|
||||||
|
|
||||||
1. add `CUSTOM_HOMEPAGE=true` to the '.env' file
|
|
||||||
2. copy `/custom/homepage.example.js` to `/custom/homepage.js` and make desired changes to `homepage.js`
|
|
||||||
|
|
||||||
- If you want up to two custom sidebar links
|
|
||||||
|
|
||||||
```
|
|
||||||
PINNED_URI_1=@someurl#2/someclaim#4
|
|
||||||
PINNED_LABEL_1=Linktext
|
|
||||||
|
|
||||||
PINNED_URI_2=$/discover?t=tag&[queryparams]
|
|
||||||
PINNED_LABEL_2=OtherLinkText
|
|
||||||
```
|
|
||||||
|
|
||||||
- Finally `NODE_ENV=production yarn compile:web` to rebuild
|
|
||||||
_Note: You don't need to edit the .env file in the /web folder - that is copied during compile._
|
|
||||||
|
|
||||||
#### Deploy the web app (_experimental_)
|
|
||||||
|
|
||||||
1. Create a server with a domain name and a reverse proxy https to port 1337.
|
|
||||||
2. Install pm2, node v10, yarn
|
|
||||||
3. Clone this repo
|
|
||||||
4. Make any customizations as above
|
|
||||||
5. Run `yarn` to install
|
|
||||||
6. Run `NODE_ENV=production yarn compile:web` to build
|
|
||||||
7. Set up pm2 to start ./web/index.js
|
|
||||||
|
|
||||||
#### Run both at the same time
|
|
||||||
|
|
||||||
Run the two commands above in separate terminal windows
|
|
||||||
|
|
||||||
```
|
|
||||||
yarn dev
|
|
||||||
|
|
||||||
// in another terminal window
|
|
||||||
yarn dev:web
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Resetting your Packages
|
#### Resetting your Packages
|
||||||
|
|
||||||
If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del /s /q node_modules && yarn` on Windows.
|
If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del /s /q node_modules && yarn` on Windows.
|
||||||
|
|
|
@ -7,6 +7,8 @@ module.exports = api => {
|
||||||
'import-glob',
|
'import-glob',
|
||||||
'@babel/plugin-transform-runtime',
|
'@babel/plugin-transform-runtime',
|
||||||
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
|
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
|
||||||
|
['@babel/plugin-proposal-private-methods', { 'loose': false }],
|
||||||
|
['@babel/plugin-proposal-private-property-in-object', { 'loose': false }],
|
||||||
'@babel/plugin-transform-flow-strip-types',
|
'@babel/plugin-transform-flow-strip-types',
|
||||||
'@babel/plugin-proposal-class-properties',
|
'@babel/plugin-proposal-class-properties',
|
||||||
'react-hot-loader/babel',
|
'react-hot-loader/babel',
|
||||||
|
|
BIN
build/cert-2021-2022.pfx
Normal file
BIN
build/cert-2021-2022.pfx
Normal file
Binary file not shown.
BIN
build/cert2023.pfx
Normal file
BIN
build/cert2023.pfx
Normal file
Binary file not shown.
14
build/entitlements.mac.plist
Normal file
14
build/entitlements.mac.plist
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.server</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -11,9 +11,10 @@ const config = {
|
||||||
LBRY_WEB_API: process.env.LBRY_WEB_API, //api.na-backend.odysee.com',
|
LBRY_WEB_API: process.env.LBRY_WEB_API, //api.na-backend.odysee.com',
|
||||||
LBRY_WEB_PUBLISH_API: process.env.LBRY_WEB_PUBLISH_API,
|
LBRY_WEB_PUBLISH_API: process.env.LBRY_WEB_PUBLISH_API,
|
||||||
LBRY_API_URL: process.env.LBRY_API_URL, //api.lbry.com',
|
LBRY_API_URL: process.env.LBRY_API_URL, //api.lbry.com',
|
||||||
LBRY_WEB_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //cdn.lbryplayer.xyz',
|
LBRY_WEB_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //player.odysee.com
|
||||||
LBRY_WEB_BUFFER_API: process.env.LBRY_WEB_BUFFER_API,
|
LBRY_WEB_BUFFER_API: process.env.LBRY_WEB_BUFFER_API,
|
||||||
SEARCH_SERVER_API: process.env.SEARCH_SERVER_API,
|
SEARCH_SERVER_API: process.env.SEARCH_SERVER_API,
|
||||||
|
CLOUD_CONNECT_SITE_NAME: process.env.CLOUD_CONNECT_SITE_NAME,
|
||||||
COMMENT_SERVER_API: process.env.COMMENT_SERVER_API,
|
COMMENT_SERVER_API: process.env.COMMENT_SERVER_API,
|
||||||
COMMENT_SERVER_NAME: process.env.COMMENT_SERVER_NAME,
|
COMMENT_SERVER_NAME: process.env.COMMENT_SERVER_NAME,
|
||||||
SOCKETY_SERVER_API: process.env.SOCKETY_SERVER_API,
|
SOCKETY_SERVER_API: process.env.SOCKETY_SERVER_API,
|
||||||
|
|
|
@ -20,11 +20,6 @@
|
||||||
"to": "static/daemon/",
|
"to": "static/daemon/",
|
||||||
"filter": ["**/*"]
|
"filter": ["**/*"]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"from": "./static/lbry-first/",
|
|
||||||
"to": "static/lbry-first/",
|
|
||||||
"filter": ["**/*"]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"from": "./static/img",
|
"from": "./static/img",
|
||||||
"to": "static/img",
|
"to": "static/img",
|
||||||
|
@ -34,6 +29,10 @@
|
||||||
"from": "./static/font",
|
"from": "./static/font",
|
||||||
"to": "static/font",
|
"to": "static/font",
|
||||||
"filter": ["**/*"]
|
"filter": ["**/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "./static/app-update.yml",
|
||||||
|
"to": "app-update.yml"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"publish": [
|
"publish": [
|
||||||
|
@ -42,7 +41,11 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mac": {
|
"mac": {
|
||||||
"category": "public.app-category.entertainment"
|
"category": "public.app-category.entertainment",
|
||||||
|
"entitlements": "build/entitlements.mac.plist",
|
||||||
|
"entitlementsInherit": "build/entitlements.mac.plist",
|
||||||
|
"hardenedRuntime" : true,
|
||||||
|
"gatekeeperAssess": false
|
||||||
},
|
},
|
||||||
"dmg": {
|
"dmg": {
|
||||||
"iconSize": 128,
|
"iconSize": 128,
|
||||||
|
@ -82,7 +85,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"deb": {
|
"deb": {
|
||||||
"depends": ["gconf2", "gconf-service", "libnotify4", "libappindicator1", "libxtst6", "libnss3"]
|
"depends": ["gconf2", "gconf-service", "libnotify4", "libxtst6", "libnss3"]
|
||||||
},
|
},
|
||||||
"nsis": {
|
"nsis": {
|
||||||
"perMachine": true,
|
"perMachine": true,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { spawn, execSync } from 'child_process';
|
import { spawn, execSync } from 'child_process';
|
||||||
import { Lbry } from 'lbry-redux';
|
import Lbry from 'lbry';
|
||||||
|
|
||||||
export default class Daemon {
|
export default class Daemon {
|
||||||
static lbrynetPath =
|
static lbrynetPath =
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { WEBPACK_ELECTRON_PORT } from 'config';
|
import { WEBPACK_ELECTRON_PORT } from 'config';
|
||||||
import { app, BrowserWindow, dialog, shell, screen, nativeImage } from 'electron';
|
import { app, BrowserWindow, dialog, screen, nativeImage } from 'electron';
|
||||||
import isDev from 'electron-is-dev';
|
import isDev from 'electron-is-dev';
|
||||||
import windowStateKeeper from 'electron-window-state';
|
import windowStateKeeper from 'electron-window-state';
|
||||||
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
||||||
|
@ -9,7 +9,8 @@ import { TO_TRAY_WHEN_CLOSED } from 'constants/settings';
|
||||||
|
|
||||||
import setupBarMenu from './menu/setupBarMenu';
|
import setupBarMenu from './menu/setupBarMenu';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
|
const remote = require('@electron/remote/main');
|
||||||
|
const shell = require('electron').shell;
|
||||||
function GetAppLangCode() {
|
function GetAppLangCode() {
|
||||||
// https://www.electronjs.org/docs/api/locales
|
// https://www.electronjs.org/docs/api/locales
|
||||||
// 1. Gets the user locale.
|
// 1. Gets the user locale.
|
||||||
|
@ -54,6 +55,8 @@ export default appState => {
|
||||||
webSecurity: !isDev,
|
webSecurity: !isDev,
|
||||||
plugins: true,
|
plugins: true,
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
enableRemoteModule: true, // see about removing this
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const lbryProto = 'lbry://';
|
const lbryProto = 'lbry://';
|
||||||
|
@ -61,6 +64,7 @@ export default appState => {
|
||||||
const rendererURL = isDev ? `http://localhost:${WEBPACK_ELECTRON_PORT}` : `file://${__dirname}/index.html`;
|
const rendererURL = isDev ? `http://localhost:${WEBPACK_ELECTRON_PORT}` : `file://${__dirname}/index.html`;
|
||||||
|
|
||||||
let window = new BrowserWindow(windowConfiguration);
|
let window = new BrowserWindow(windowConfiguration);
|
||||||
|
remote.enable(window.webContents);
|
||||||
|
|
||||||
// Let us register listeners on the window, so we can update the state
|
// Let us register listeners on the window, so we can update the state
|
||||||
// automatically (the listeners will be removed when the window is closed)
|
// automatically (the listeners will be removed when the window is closed)
|
||||||
|
@ -91,7 +95,7 @@ export default appState => {
|
||||||
|
|
||||||
// is it a lbry://? pointing to an app page
|
// is it a lbry://? pointing to an app page
|
||||||
if (deepLinkingURI.includes(lbryProtoQ)) {
|
if (deepLinkingURI.includes(lbryProtoQ)) {
|
||||||
let path = deepLinkingURI.substr(lbryProtoQ.length);
|
let path = deepLinkingURI.slice(lbryProtoQ.length);
|
||||||
let page = path.indexOf('?') >= 0 ? path.substring(0, path.indexOf('?')) : path;
|
let page = path.indexOf('?') >= 0 ? path.substring(0, path.indexOf('?')) : path;
|
||||||
if (Object.values(PAGES).includes(page)) {
|
if (Object.values(PAGES).includes(page)) {
|
||||||
deepLinkingURI = deepLinkingURI.replace(lbryProtoQ, '#/$/');
|
deepLinkingURI = deepLinkingURI.replace(lbryProtoQ, '#/$/');
|
||||||
|
@ -186,9 +190,13 @@ export default appState => {
|
||||||
window = null;
|
window = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
window.webContents.on('new-window', (event, url) => {
|
window.webContents.setWindowOpenHandler((details) => {
|
||||||
event.preventDefault();
|
// Only open http and https links to prevent
|
||||||
shell.openExternal(url);
|
// security issues.
|
||||||
|
if (['https:', 'http:'].includes(new URL(details.url).protocol)) {
|
||||||
|
shell.openExternal(details.url);
|
||||||
|
}
|
||||||
|
return { action: 'deny' };
|
||||||
});
|
});
|
||||||
|
|
||||||
window.webContents.on('update-target-url', (event, url) => {
|
window.webContents.on('update-target-url', (event, url) => {
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
import '@babel/polyfill';
|
import '@babel/polyfill';
|
||||||
import SemVer from 'semver';
|
import SemVer from 'semver';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
import { app, dialog, ipcMain, session, shell } from 'electron';
|
import { app, dialog, ipcMain, session, shell, BrowserWindow } from 'electron';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
import { Lbry } from 'lbry-redux';
|
import Lbry from 'lbry';
|
||||||
import LbryFirstInstance from './LbryFirstInstance';
|
import LbryFirstInstance from './LbryFirstInstance';
|
||||||
import Daemon from './Daemon';
|
import Daemon from './Daemon';
|
||||||
import isDev from 'electron-is-dev';
|
import isDev from 'electron-is-dev';
|
||||||
|
@ -17,6 +17,17 @@ import startSandbox from './startSandbox';
|
||||||
import installDevtools from './installDevtools';
|
import installDevtools from './installDevtools';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { diskSpaceLinux, diskSpaceWindows, diskSpaceMac } from '../ui/util/diskspace';
|
||||||
|
|
||||||
|
const { download } = require('electron-dl');
|
||||||
|
const mime = require('mime');
|
||||||
|
const remote = require('@electron/remote/main');
|
||||||
|
const os = require('os');
|
||||||
|
const sudo = require('sudo-prompt');
|
||||||
|
const probe = require('ffmpeg-probe');
|
||||||
|
const MAX_IPC_SEND_BUFFER_SIZE = 500000000; // large files crash when serialized for ipc message
|
||||||
|
|
||||||
|
remote.initialize();
|
||||||
const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled');
|
const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled');
|
||||||
let upgradeDisabled;
|
let upgradeDisabled;
|
||||||
try {
|
try {
|
||||||
|
@ -26,11 +37,18 @@ try {
|
||||||
upgradeDisabled = false;
|
upgradeDisabled = false;
|
||||||
}
|
}
|
||||||
autoUpdater.autoDownload = !upgradeDisabled;
|
autoUpdater.autoDownload = !upgradeDisabled;
|
||||||
|
autoUpdater.allowPrerelease = false;
|
||||||
|
|
||||||
// This is set to true if an auto update has been downloaded through the Electron
|
const UPDATE_STATE_INIT = 0;
|
||||||
// auto-update system and is ready to install. If the user declined an update earlier,
|
const UPDATE_STATE_CHECKING = 1;
|
||||||
// it will still install on shutdown.
|
const UPDATE_STATE_UPDATES_FOUND = 2;
|
||||||
let autoUpdateDownloaded = false;
|
const UPDATE_STATE_NO_UPDATES_FOUND = 3;
|
||||||
|
const UPDATE_STATE_DOWNLOADING = 4;
|
||||||
|
const UPDATE_STATE_DOWNLOADED = 5;
|
||||||
|
let updateState = UPDATE_STATE_INIT;
|
||||||
|
let updateDownloadItem;
|
||||||
|
|
||||||
|
const isAutoUpdateSupported = ['win32', 'darwin'].includes(process.platform) || !!process.env.APPIMAGE;
|
||||||
|
|
||||||
// This is used to keep track of whether we are showing the special dialog
|
// This is used to keep track of whether we are showing the special dialog
|
||||||
// that we show on Windows after you decline an upgrade and close the app later.
|
// that we show on Windows after you decline an upgrade and close the app later.
|
||||||
|
@ -52,7 +70,7 @@ if (isDev && process.platform === 'win32') {
|
||||||
app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [
|
app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [
|
||||||
path.resolve(process.argv[1]),
|
path.resolve(process.argv[1]),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else if (process.platform !== 'linux') {
|
||||||
app.setAsDefaultProtocolClient(PROTOCOL);
|
app.setAsDefaultProtocolClient(PROTOCOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +241,8 @@ app.on('activate', () => {
|
||||||
app.on('will-quit', event => {
|
app.on('will-quit', event => {
|
||||||
if (
|
if (
|
||||||
process.platform === 'win32' &&
|
process.platform === 'win32' &&
|
||||||
autoUpdateDownloaded &&
|
updateState === UPDATE_STATE_DOWNLOADED &&
|
||||||
|
isAutoUpdateSupported &&
|
||||||
!appState.autoUpdateAccepted &&
|
!appState.autoUpdateAccepted &&
|
||||||
!showingAutoUpdateCloseAlert
|
!showingAutoUpdateCloseAlert
|
||||||
) {
|
) {
|
||||||
|
@ -283,27 +302,118 @@ app.on('before-quit', () => {
|
||||||
appState.isQuitting = true;
|
appState.isQuitting = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('upgrade', (event, installerPath) => {
|
// Get the content of a file as a raw buffer of bytes.
|
||||||
app.on('quit', () => {
|
// Useful to convert a file path to a File instance.
|
||||||
console.log('Launching upgrade installer at', installerPath);
|
// Example:
|
||||||
// This gets triggered called after *all* other quit-related events, so
|
// const result = await ipcMain.invoke('get-file-from-path', 'path/to/file');
|
||||||
// we'll only get here if we're fully prepared and quitting for real.
|
// const file = new File([result.buffer], result.name);
|
||||||
shell.openPath(installerPath);
|
// NOTE: if path points to a folder, an empty
|
||||||
|
// file will be given.
|
||||||
|
ipcMain.handle('get-file-from-path', (event, path, readContents = true) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.stat(path, (error, stats) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Separate folders considering "\" and "/"
|
||||||
|
// as separators (cross platform)
|
||||||
|
const folders = path.split(/[\\/]/);
|
||||||
|
const name = folders[folders.length - 1];
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
resolve({
|
||||||
|
name,
|
||||||
|
mime: undefined,
|
||||||
|
path,
|
||||||
|
buffer: new ArrayBuffer(0),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!readContents) {
|
||||||
|
resolve({
|
||||||
|
name,
|
||||||
|
mime: mime.getType(name) || undefined,
|
||||||
|
path,
|
||||||
|
buffer: new ArrayBuffer(0),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Encoding null ensures data results in a Buffer.
|
||||||
|
fs.readFile(path, { encoding: null }, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
name,
|
||||||
|
mime: mime.getType(name) || undefined,
|
||||||
|
path,
|
||||||
|
buffer: data,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
// what to do if no shutdown in a long time?
|
|
||||||
console.log('Update downloaded to', installerPath);
|
|
||||||
console.log('The app will close and you will be prompted to install the latest version of LBRY.');
|
|
||||||
console.log('After the install is complete, please reopen the app.');
|
|
||||||
app.quit();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-downloaded', () => {
|
ipcMain.handle('get-file-details-from-path', async (event, path) => {
|
||||||
autoUpdateDownloaded = true;
|
const isFfMp4 = (ffprobeResults) => {
|
||||||
|
return ffprobeResults &&
|
||||||
|
ffprobeResults.format &&
|
||||||
|
ffprobeResults.format.format_name &&
|
||||||
|
ffprobeResults.format.format_name.includes('mp4');
|
||||||
|
};
|
||||||
|
const folders = path.split(/[\\/]/);
|
||||||
|
const name = folders[folders.length - 1];
|
||||||
|
let duration = 0, size = 0, mimeType;
|
||||||
|
try {
|
||||||
|
await fs.promises.stat(path);
|
||||||
|
let ffprobeResults;
|
||||||
|
try {
|
||||||
|
ffprobeResults = await probe(path);
|
||||||
|
duration = ffprobeResults.format.duration;
|
||||||
|
size = ffprobeResults.format.size;
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
let fileReadResult;
|
||||||
|
if (size < MAX_IPC_SEND_BUFFER_SIZE) {
|
||||||
|
try {
|
||||||
|
fileReadResult = await fs.promises.readFile(path);
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: use mmmagic to inspect file and get mime type
|
||||||
|
mimeType = isFfMp4(ffprobeResults) ? 'video/mp4' : mime.getType(name);
|
||||||
|
const fileData = {name, mime: mimeType || undefined, path, duration: duration, size, buffer: fileReadResult };
|
||||||
|
return fileData;
|
||||||
|
} catch (e) {
|
||||||
|
// no stat
|
||||||
|
return { error: 'no file' };
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('autoUpdateAccepted', () => {
|
ipcMain.on('get-disk-space', async (event) => {
|
||||||
appState.autoUpdateAccepted = true;
|
try {
|
||||||
autoUpdater.quitAndInstall();
|
const { data_dir } = await Lbry.settings_get();
|
||||||
|
let diskSpace;
|
||||||
|
switch (os.platform()) {
|
||||||
|
case 'linux':
|
||||||
|
diskSpace = await diskSpaceLinux(data_dir);
|
||||||
|
break;
|
||||||
|
case 'darwin':
|
||||||
|
diskSpace = await diskSpaceMac(data_dir);
|
||||||
|
break;
|
||||||
|
case 'win32':
|
||||||
|
diskSpace = await diskSpaceWindows(data_dir);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('unknown platform');
|
||||||
|
}
|
||||||
|
rendererWindow.webContents.send('send-disk-space', { diskSpace });
|
||||||
|
} catch (e) {
|
||||||
|
rendererWindow.webContents.send('send-disk-space', { error: e.message || e });
|
||||||
|
console.log('Failed to get disk space', e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('version-info-requested', () => {
|
ipcMain.on('version-info-requested', () => {
|
||||||
|
@ -398,3 +508,162 @@ process.on('uncaughtException', error => {
|
||||||
if (daemon) daemon.quit();
|
if (daemon) daemon.quit();
|
||||||
app.exit(1);
|
app.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Auto updater
|
||||||
|
autoUpdater.on('download-progress', () => {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-downloaded', () => {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADED;
|
||||||
|
|
||||||
|
// If this download was trigger by
|
||||||
|
// autoUpdateAccepted it means, the user
|
||||||
|
// wants to install the new update but
|
||||||
|
// needed to downloaded the files first.
|
||||||
|
if (appState.autoUpdateAccepted) {
|
||||||
|
autoUpdater.quitAndInstall();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-available', () => {
|
||||||
|
if (updateState === UPDATE_STATE_DOWNLOADING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateState = UPDATE_STATE_UPDATES_FOUND;
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-not-available', () => {
|
||||||
|
updateState = UPDATE_STATE_NO_UPDATES_FOUND;
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('error', () => {
|
||||||
|
if (updateState === UPDATE_STATE_DOWNLOADING) {
|
||||||
|
updateState = UPDATE_STATE_UPDATES_FOUND;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateState = UPDATE_STATE_INIT;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Manual (.deb) update
|
||||||
|
ipcMain.on('cancel-download-upgrade', () => {
|
||||||
|
if (updateDownloadItem) {
|
||||||
|
// Cancel the download and execute the onCancel
|
||||||
|
// callback set in the options.
|
||||||
|
updateDownloadItem.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('download-upgrade', (event, params) => {
|
||||||
|
if (updateState !== UPDATE_STATE_UPDATES_FOUND) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isAutoUpdateSupported) {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
autoUpdater.downloadUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { url, options } = params;
|
||||||
|
const dir = fs.mkdtempSync(app.getPath('temp') + path.sep);
|
||||||
|
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
|
||||||
|
// Grab the download item's handler to allow
|
||||||
|
// cancelling the operation if required.
|
||||||
|
options.onStarted = function(downloadItem) {
|
||||||
|
updateDownloadItem = downloadItem;
|
||||||
|
};
|
||||||
|
options.onCancel = function() {
|
||||||
|
updateState = UPDATE_STATE_UPDATES_FOUND;
|
||||||
|
updateDownloadItem = undefined;
|
||||||
|
};
|
||||||
|
options.onProgress = function(p) {
|
||||||
|
rendererWindow.webContents.send('download-progress-update', p);
|
||||||
|
};
|
||||||
|
options.onCompleted = function(c) {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADED;
|
||||||
|
updateDownloadItem = undefined;
|
||||||
|
rendererWindow.webContents.send('download-update-complete', c);
|
||||||
|
};
|
||||||
|
options.directory = dir;
|
||||||
|
const win = BrowserWindow.getFocusedWindow();
|
||||||
|
download(win, url, options).catch(e => {
|
||||||
|
updateState = UPDATE_STATE_UPDATES_FOUND;
|
||||||
|
console.log('e', e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update behavior
|
||||||
|
ipcMain.on('autoUpdateAccepted', () => {
|
||||||
|
appState.autoUpdateAccepted = true;
|
||||||
|
|
||||||
|
// quitAndInstall can only be called if the
|
||||||
|
// update has been downloaded. Since the user
|
||||||
|
// can disable auto updates, we have to make
|
||||||
|
// sure it has been downloaded first.
|
||||||
|
if (updateState === UPDATE_STATE_DOWNLOADED) {
|
||||||
|
autoUpdater.quitAndInstall();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateState !== UPDATE_STATE_UPDATES_FOUND) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the update hasn't been downloaded,
|
||||||
|
// start downloading it. After it's done, the
|
||||||
|
// event 'update-downloaded' will be triggered,
|
||||||
|
// where we will be able to resume the
|
||||||
|
// update installation.
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
autoUpdater.downloadUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('check-for-updates', (event, autoDownload) => {
|
||||||
|
if (![UPDATE_STATE_INIT, UPDATE_STATE_NO_UPDATES_FOUND].includes(updateState)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState = UPDATE_STATE_CHECKING;
|
||||||
|
|
||||||
|
// If autoDownload is true, checkForUpdates will begin the
|
||||||
|
// download automatically.
|
||||||
|
if (autoDownload) {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
}
|
||||||
|
|
||||||
|
autoUpdater.autoDownload = autoDownload;
|
||||||
|
autoUpdater.checkForUpdates();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('upgrade', (event, installerPath) => {
|
||||||
|
// what to do if no shutdown in a long time?
|
||||||
|
console.log('Update downloaded to', installerPath);
|
||||||
|
console.log('The app will close and you will be prompted to install the latest version of LBRY.');
|
||||||
|
console.log('After the install is complete, please reopen the app.');
|
||||||
|
|
||||||
|
// Prevent .deb package from opening with archive manager (Ubuntu >= 20)
|
||||||
|
if (process.platform === 'linux' && !process.env.APPIMAGE) {
|
||||||
|
sudo.exec(`dpkg -i ${installerPath}`, { name: app.name }, (err, stdout, stderr) => {
|
||||||
|
if (err || stderr) {
|
||||||
|
rendererWindow.webContents.send('upgrade-installing-error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-launch the application when the installation finishes.
|
||||||
|
app.relaunch();
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('quit', () => {
|
||||||
|
console.log('Launching upgrade installer at', installerPath);
|
||||||
|
// This gets triggered called after *all* other quit-related events, so
|
||||||
|
// we'll only get here if we're fully prepared and quitting for real.
|
||||||
|
shell.openPath(installerPath);
|
||||||
|
});
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import express from 'express';
|
// import express from 'express';
|
||||||
import unpackByOutpoint from './unpackByOutpoint';
|
|
||||||
|
|
||||||
// Polyfills and `lbry-redux`
|
// Polyfills and `lbry-redux`
|
||||||
global.fetch = require('node-fetch');
|
global.fetch = require('node-fetch');
|
||||||
|
@ -8,31 +7,31 @@ if (typeof global.fetch === 'object') {
|
||||||
global.fetch = global.fetch.default;
|
global.fetch = global.fetch.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Lbry } = require('lbry-redux');
|
// const Lbry = require('lbry');
|
||||||
|
|
||||||
delete global.window;
|
delete global.window;
|
||||||
|
|
||||||
export default async function startSandbox() {
|
export default async function startSandbox() {
|
||||||
const port = 5278;
|
// const port = 5278;
|
||||||
const sandbox = express();
|
// const sandbox = express();
|
||||||
|
|
||||||
sandbox.get('/set/:outpoint', async (req, res) => {
|
// sandbox.get('/set/:outpoint', async (req, res) => {
|
||||||
const { outpoint } = req.params;
|
// const { outpoint } = req.params;
|
||||||
|
//
|
||||||
const resolvedPath = await unpackByOutpoint(Lbry, outpoint);
|
// const resolvedPath = await unpackByOutpoint(Lbry, outpoint);
|
||||||
|
//
|
||||||
sandbox.use(`/sandbox/${outpoint}/`, express.static(resolvedPath));
|
// sandbox.use(`/sandbox/${outpoint}/`, express.static(resolvedPath));
|
||||||
|
//
|
||||||
res.send(`/sandbox/${outpoint}/`);
|
// res.send(`/sandbox/${outpoint}/`);
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
sandbox
|
// sandbox
|
||||||
.listen(port, 'localhost', () => console.log(`Sandbox listening on port ${port}.`))
|
// .listen(port, 'localhost', () => console.log(`Sandbox listening on port ${port}.`))
|
||||||
.on('error', err => {
|
// .on('error', err => {
|
||||||
if (err.code === 'EADDRINUSE') {
|
// if (err.code === 'EADDRINUSE') {
|
||||||
console.log(
|
// console.log(
|
||||||
`Server already listening at localhost:${port}. This is probably another LBRY app running. If not, games in the app will not work.`
|
// `Server already listening at localhost:${port}. This is probably another LBRY app running. If not, games in the app will not work.`
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import { unpackDirectory } from 'lbry-format';
|
|
||||||
|
|
||||||
async function unpackByOutpoint(lbry, outpoint) {
|
|
||||||
const { items: claimFiles } = await lbry.file_list({ outpoint, full_status: true, page: 1, page_size: 1 });
|
|
||||||
|
|
||||||
if (claimFiles && claimFiles.length) {
|
|
||||||
const claimFileInfo = claimFiles[0];
|
|
||||||
const packFilePath = path.resolve(claimFileInfo.download_path);
|
|
||||||
const unpackPath = path.normalize(path.join(claimFileInfo.download_directory, claimFileInfo.claim_name));
|
|
||||||
|
|
||||||
if (!fs.existsSync(unpackPath)) {
|
|
||||||
await unpackDirectory(unpackPath, {
|
|
||||||
fileName: packFilePath,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return unpackPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default unpackByOutpoint;
|
|
184
extras/lbry-first/lbry-first.js
Normal file
184
extras/lbry-first/lbry-first.js
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
// @flow
|
||||||
|
/*
|
||||||
|
LBRY FIRST does not work due to api changes
|
||||||
|
*/
|
||||||
|
import 'proxy-polyfill';
|
||||||
|
|
||||||
|
const CHECK_LBRYFIRST_STARTED_TRY_NUMBER = 200;
|
||||||
|
//
|
||||||
|
// Basic LBRYFIRST connection config
|
||||||
|
// Offers a proxy to call LBRYFIRST methods
|
||||||
|
|
||||||
|
//
|
||||||
|
const LbryFirst: LbryFirstTypes = {
|
||||||
|
isConnected: false,
|
||||||
|
connectPromise: null,
|
||||||
|
lbryFirstConnectionString: 'http://localhost:1337/rpc',
|
||||||
|
apiRequestHeaders: { 'Content-Type': 'application/json' },
|
||||||
|
|
||||||
|
// Allow overriding lbryFirst connection string (e.g. to `/api/proxy` for lbryweb)
|
||||||
|
setLbryFirstConnectionString: (value: string) => {
|
||||||
|
LbryFirst.lbryFirstConnectionString = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
setApiHeader: (key: string, value: string) => {
|
||||||
|
LbryFirst.apiRequestHeaders = Object.assign(LbryFirst.apiRequestHeaders, { [key]: value });
|
||||||
|
},
|
||||||
|
|
||||||
|
unsetApiHeader: key => {
|
||||||
|
Object.keys(LbryFirst.apiRequestHeaders).includes(key) &&
|
||||||
|
delete LbryFirst.apiRequestHeaders['key'];
|
||||||
|
},
|
||||||
|
// Allow overriding Lbry methods
|
||||||
|
overrides: {},
|
||||||
|
setOverride: (methodName, newMethod) => {
|
||||||
|
LbryFirst.overrides[methodName] = newMethod;
|
||||||
|
},
|
||||||
|
getApiRequestHeaders: () => LbryFirst.apiRequestHeaders,
|
||||||
|
|
||||||
|
// LbryFirst Methods
|
||||||
|
status: (params = {}) => lbryFirstCallWithResult('status', params),
|
||||||
|
stop: () => lbryFirstCallWithResult('stop', {}),
|
||||||
|
version: () => lbryFirstCallWithResult('version', {}),
|
||||||
|
|
||||||
|
// Upload to youtube
|
||||||
|
upload: (params: { title: string, description: string, file_path: ?string } = {}) => {
|
||||||
|
// Only upload when originally publishing for now
|
||||||
|
if (!params.file_path) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadParams: {
|
||||||
|
Title: string,
|
||||||
|
Description: string,
|
||||||
|
FilePath: string,
|
||||||
|
Category: string,
|
||||||
|
Keywords: string,
|
||||||
|
} = {
|
||||||
|
Title: params.title,
|
||||||
|
Description: params.description,
|
||||||
|
FilePath: params.file_path,
|
||||||
|
Category: '',
|
||||||
|
Keywords: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
return lbryFirstCallWithResult('youtube.Upload', uploadParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasYTAuth: (token: string) => {
|
||||||
|
const hasYTAuthParams = {};
|
||||||
|
hasYTAuthParams.AuthToken = token;
|
||||||
|
return lbryFirstCallWithResult('youtube.HasAuth', hasYTAuthParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
ytSignup: () => {
|
||||||
|
const emptyParams = {};
|
||||||
|
return lbryFirstCallWithResult('youtube.Signup', emptyParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: () => {
|
||||||
|
const emptyParams = {};
|
||||||
|
return lbryFirstCallWithResult('youtube.Remove', emptyParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Connect to lbry-first
|
||||||
|
connect: () => {
|
||||||
|
if (LbryFirst.connectPromise === null) {
|
||||||
|
LbryFirst.connectPromise = new Promise((resolve, reject) => {
|
||||||
|
let tryNum = 0;
|
||||||
|
// Check every half second to see if the lbryFirst is accepting connections
|
||||||
|
function checkLbryFirstStarted() {
|
||||||
|
tryNum += 1;
|
||||||
|
LbryFirst.status()
|
||||||
|
.then(resolve)
|
||||||
|
.catch(() => {
|
||||||
|
if (tryNum <= CHECK_LBRYFIRST_STARTED_TRY_NUMBER) {
|
||||||
|
setTimeout(checkLbryFirstStarted, tryNum < 50 ? 400 : 1000);
|
||||||
|
} else {
|
||||||
|
reject(new Error('Unable to connect to LBRY'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLbryFirstStarted();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flow thinks this could be empty, but it will always return a promise
|
||||||
|
// $FlowFixMe
|
||||||
|
return LbryFirst.connectPromise;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkAndParse(response) {
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
return response.json().then(json => {
|
||||||
|
let error;
|
||||||
|
if (json.error) {
|
||||||
|
const errorMessage = typeof json.error === 'object' ? json.error.message : json.error;
|
||||||
|
error = new Error(errorMessage);
|
||||||
|
} else {
|
||||||
|
error = new Error('Protocol error with unknown response signature');
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function apiCall(method: string, params: ?{}, resolve: Function, reject: Function) {
|
||||||
|
const counter = new Date().getTime();
|
||||||
|
const paramsArray = [params];
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: LbryFirst.apiRequestHeaders,
|
||||||
|
body: JSON.stringify({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
method,
|
||||||
|
params: paramsArray,
|
||||||
|
id: counter,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetch(LbryFirst.lbryFirstConnectionString, options)
|
||||||
|
.then(checkAndParse)
|
||||||
|
.then(response => {
|
||||||
|
const error = response.error || (response.result && response.result.error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
return resolve(response.result);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lbryFirstCallWithResult(name: string, params: ?{} = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
apiCall(
|
||||||
|
name,
|
||||||
|
params,
|
||||||
|
result => {
|
||||||
|
resolve(result);
|
||||||
|
},
|
||||||
|
reject
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is only for a fallback
|
||||||
|
// If there is a LbryFirst method that is being called by an app, it should be added to /flow-typed/LbryFirst.js
|
||||||
|
const lbryFirstProxy = new Proxy(LbryFirst, {
|
||||||
|
get(target: LbryFirstTypes, name: string) {
|
||||||
|
if (name in target) {
|
||||||
|
return target[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (params = {}) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
apiCall(name, params, resolve, reject);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default lbryFirstProxy;
|
97
extras/lbryinc/constants/action_types.js
Normal file
97
extras/lbryinc/constants/action_types.js
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// Claims
|
||||||
|
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
|
||||||
|
export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED';
|
||||||
|
export const FETCH_TRENDING_CONTENT_STARTED = 'FETCH_TRENDING_CONTENT_STARTED';
|
||||||
|
export const FETCH_TRENDING_CONTENT_COMPLETED = 'FETCH_TRENDING_CONTENT_COMPLETED';
|
||||||
|
export const RESOLVE_URIS_STARTED = 'RESOLVE_URIS_STARTED';
|
||||||
|
export const RESOLVE_URIS_COMPLETED = 'RESOLVE_URIS_COMPLETED';
|
||||||
|
export const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED';
|
||||||
|
export const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_COMPLETED';
|
||||||
|
export const FETCH_CHANNEL_CLAIM_COUNT_STARTED = 'FETCH_CHANNEL_CLAIM_COUNT_STARTED';
|
||||||
|
export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED = 'FETCH_CHANNEL_CLAIM_COUNT_COMPLETED';
|
||||||
|
export const FETCH_CLAIM_LIST_MINE_STARTED = 'FETCH_CLAIM_LIST_MINE_STARTED';
|
||||||
|
export const FETCH_CLAIM_LIST_MINE_COMPLETED = 'FETCH_CLAIM_LIST_MINE_COMPLETED';
|
||||||
|
export const ABANDON_CLAIM_STARTED = 'ABANDON_CLAIM_STARTED';
|
||||||
|
export const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
|
||||||
|
export const FETCH_CHANNEL_LIST_STARTED = 'FETCH_CHANNEL_LIST_STARTED';
|
||||||
|
export const FETCH_CHANNEL_LIST_COMPLETED = 'FETCH_CHANNEL_LIST_COMPLETED';
|
||||||
|
export const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
|
||||||
|
export const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
|
||||||
|
export const PUBLISH_STARTED = 'PUBLISH_STARTED';
|
||||||
|
export const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
|
||||||
|
export const PUBLISH_FAILED = 'PUBLISH_FAILED';
|
||||||
|
export const SET_PLAYING_URI = 'SET_PLAYING_URI';
|
||||||
|
export const SET_CONTENT_POSITION = 'SET_CONTENT_POSITION';
|
||||||
|
export const SET_CONTENT_LAST_VIEWED = 'SET_CONTENT_LAST_VIEWED';
|
||||||
|
export const CLEAR_CONTENT_HISTORY_URI = 'CLEAR_CONTENT_HISTORY_URI';
|
||||||
|
export const CLEAR_CONTENT_HISTORY_ALL = 'CLEAR_CONTENT_HISTORY_ALL';
|
||||||
|
|
||||||
|
// Subscriptions
|
||||||
|
export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
|
||||||
|
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
|
||||||
|
export const CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS =
|
||||||
|
'CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS';
|
||||||
|
export const CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS =
|
||||||
|
'CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS';
|
||||||
|
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
|
||||||
|
export const SET_SUBSCRIPTION_LATEST = 'SET_SUBSCRIPTION_LATEST';
|
||||||
|
export const UPDATE_SUBSCRIPTION_UNREADS = 'UPDATE_SUBSCRIPTION_UNREADS';
|
||||||
|
export const REMOVE_SUBSCRIPTION_UNREADS = 'REMOVE_SUBSCRIPTION_UNREADS';
|
||||||
|
export const CHECK_SUBSCRIPTION_STARTED = 'CHECK_SUBSCRIPTION_STARTED';
|
||||||
|
export const CHECK_SUBSCRIPTION_COMPLETED = 'CHECK_SUBSCRIPTION_COMPLETED';
|
||||||
|
export const CHECK_SUBSCRIPTIONS_SUBSCRIBE = 'CHECK_SUBSCRIPTIONS_SUBSCRIBE';
|
||||||
|
export const FETCH_SUBSCRIPTIONS_START = 'FETCH_SUBSCRIPTIONS_START';
|
||||||
|
export const FETCH_SUBSCRIPTIONS_FAIL = 'FETCH_SUBSCRIPTIONS_FAIL';
|
||||||
|
export const FETCH_SUBSCRIPTIONS_SUCCESS = 'FETCH_SUBSCRIPTIONS_SUCCESS';
|
||||||
|
export const SET_VIEW_MODE = 'SET_VIEW_MODE';
|
||||||
|
export const GET_SUGGESTED_SUBSCRIPTIONS_START = 'GET_SUGGESTED_SUBSCRIPTIONS_START';
|
||||||
|
export const GET_SUGGESTED_SUBSCRIPTIONS_SUCCESS = 'GET_SUGGESTED_SUBSCRIPTIONS_SUCCESS';
|
||||||
|
export const GET_SUGGESTED_SUBSCRIPTIONS_FAIL = 'GET_SUGGESTED_SUBSCRIPTIONS_FAIL';
|
||||||
|
export const SUBSCRIPTION_FIRST_RUN_COMPLETED = 'SUBSCRIPTION_FIRST_RUN_COMPLETED';
|
||||||
|
export const VIEW_SUGGESTED_SUBSCRIPTIONS = 'VIEW_SUGGESTED_SUBSCRIPTIONS';
|
||||||
|
|
||||||
|
// Blacklist
|
||||||
|
export const FETCH_BLACK_LISTED_CONTENT_STARTED = 'FETCH_BLACK_LISTED_CONTENT_STARTED';
|
||||||
|
export const FETCH_BLACK_LISTED_CONTENT_COMPLETED = 'FETCH_BLACK_LISTED_CONTENT_COMPLETED';
|
||||||
|
export const FETCH_BLACK_LISTED_CONTENT_FAILED = 'FETCH_BLACK_LISTED_CONTENT_FAILED';
|
||||||
|
export const BLACK_LISTED_CONTENT_SUBSCRIBE = 'BLACK_LISTED_CONTENT_SUBSCRIBE';
|
||||||
|
|
||||||
|
// Filtered list
|
||||||
|
export const FETCH_FILTERED_CONTENT_STARTED = 'FETCH_FILTERED_CONTENT_STARTED';
|
||||||
|
export const FETCH_FILTERED_CONTENT_COMPLETED = 'FETCH_FILTERED_CONTENT_COMPLETED';
|
||||||
|
export const FETCH_FILTERED_CONTENT_FAILED = 'FETCH_FILTERED_CONTENT_FAILED';
|
||||||
|
export const FILTERED_CONTENT_SUBSCRIBE = 'FILTERED_CONTENT_SUBSCRIBE';
|
||||||
|
|
||||||
|
// Cost Info
|
||||||
|
export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED';
|
||||||
|
export const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
export const FETCH_VIEW_COUNT_STARTED = 'FETCH_VIEW_COUNT_STARTED';
|
||||||
|
export const FETCH_VIEW_COUNT_FAILED = 'FETCH_VIEW_COUNT_FAILED';
|
||||||
|
export const FETCH_VIEW_COUNT_COMPLETED = 'FETCH_VIEW_COUNT_COMPLETED';
|
||||||
|
export const FETCH_SUB_COUNT_STARTED = 'FETCH_SUB_COUNT_STARTED';
|
||||||
|
export const FETCH_SUB_COUNT_FAILED = 'FETCH_SUB_COUNT_FAILED';
|
||||||
|
export const FETCH_SUB_COUNT_COMPLETED = 'FETCH_SUB_COUNT_COMPLETED';
|
||||||
|
|
||||||
|
// Cross-device Sync
|
||||||
|
export const GET_SYNC_STARTED = 'GET_SYNC_STARTED';
|
||||||
|
export const GET_SYNC_COMPLETED = 'GET_SYNC_COMPLETED';
|
||||||
|
export const GET_SYNC_FAILED = 'GET_SYNC_FAILED';
|
||||||
|
export const SET_SYNC_STARTED = 'SET_SYNC_STARTED';
|
||||||
|
export const SET_SYNC_FAILED = 'SET_SYNC_FAILED';
|
||||||
|
export const SET_SYNC_COMPLETED = 'SET_SYNC_COMPLETED';
|
||||||
|
export const SET_DEFAULT_ACCOUNT = 'SET_DEFAULT_ACCOUNT';
|
||||||
|
export const SYNC_APPLY_STARTED = 'SYNC_APPLY_STARTED';
|
||||||
|
export const SYNC_APPLY_COMPLETED = 'SYNC_APPLY_COMPLETED';
|
||||||
|
export const SYNC_APPLY_FAILED = 'SYNC_APPLY_FAILED';
|
||||||
|
export const SYNC_APPLY_BAD_PASSWORD = 'SYNC_APPLY_BAD_PASSWORD';
|
||||||
|
export const SYNC_RESET = 'SYNC_RESET';
|
||||||
|
|
||||||
|
// Lbry.tv
|
||||||
|
export const UPDATE_UPLOAD_PROGRESS = 'UPDATE_UPLOAD_PROGRESS';
|
||||||
|
|
||||||
|
// User
|
||||||
|
export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE';
|
||||||
|
export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED';
|
||||||
|
export const GENERATE_AUTH_TOKEN_SUCCESS = 'GENERATE_AUTH_TOKEN_SUCCESS';
|
5
extras/lbryinc/constants/claim.js
Normal file
5
extras/lbryinc/constants/claim.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export const MINIMUM_PUBLISH_BID = 0.00000001;
|
||||||
|
|
||||||
|
export const CHANNEL_ANONYMOUS = 'anonymous';
|
||||||
|
export const CHANNEL_NEW = 'new';
|
||||||
|
export const PAGE_SIZE = 20;
|
4
extras/lbryinc/constants/errors.js
Normal file
4
extras/lbryinc/constants/errors.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export const ALREADY_CLAIMED =
|
||||||
|
'once the invite reward has been claimed the referrer cannot be changed';
|
||||||
|
export const REFERRER_NOT_FOUND =
|
||||||
|
'A lbry.tv account could not be found for the referrer you provided.';
|
11
extras/lbryinc/constants/youtube.js
Normal file
11
extras/lbryinc/constants/youtube.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export const YOUTUBE_SYNC_NOT_TRANSFERRED = 'not_transferred';
|
||||||
|
export const YOUTUBE_SYNC_PENDING = 'pending';
|
||||||
|
export const YOUTUBE_SYNC_PENDING_EMAIL = 'pendingemail';
|
||||||
|
export const YOUTUBE_SYNC_PENDING_TRANSFER = 'pending_transfer';
|
||||||
|
export const YOUTUBE_SYNC_COMPLETED_TRANSFER = 'completed_transfer';
|
||||||
|
export const YOUTUBE_SYNC_QUEUED = 'queued';
|
||||||
|
export const YOUTUBE_SYNC_SYNCING = 'syncing';
|
||||||
|
export const YOUTUBE_SYNC_SYNCED = 'synced';
|
||||||
|
export const YOUTUBE_SYNC_FAILED = 'failed';
|
||||||
|
export const YOUTUBE_SYNC_PENDINGUPGRADE = 'pendingupgrade';
|
||||||
|
export const YOUTUBE_SYNC_ABANDONDED = 'abandoned';
|
71
extras/lbryinc/index.js
Normal file
71
extras/lbryinc/index.js
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import * as LBRYINC_ACTIONS from 'constants/action_types';
|
||||||
|
import * as YOUTUBE_STATUSES from 'constants/youtube';
|
||||||
|
import * as ERRORS from 'constants/errors';
|
||||||
|
import Lbryio from './lbryio';
|
||||||
|
|
||||||
|
export { Lbryio };
|
||||||
|
|
||||||
|
// constants
|
||||||
|
export { LBRYINC_ACTIONS, YOUTUBE_STATUSES, ERRORS };
|
||||||
|
|
||||||
|
// utils
|
||||||
|
export { doTransifexUpload } from 'util/transifex-upload';
|
||||||
|
|
||||||
|
// actions
|
||||||
|
export { doGenerateAuthToken } from './redux/actions/auth';
|
||||||
|
export { doFetchCostInfoForUri } from './redux/actions/cost_info';
|
||||||
|
export { doBlackListedOutpointsSubscribe } from './redux/actions/blacklist';
|
||||||
|
export { doFilteredOutpointsSubscribe } from './redux/actions/filtered';
|
||||||
|
export { doFetchViewCount, doFetchSubCount } from './redux/actions/stats';
|
||||||
|
export {
|
||||||
|
doCheckSync,
|
||||||
|
doGetSync,
|
||||||
|
doSetSync,
|
||||||
|
doSetDefaultAccount,
|
||||||
|
doSyncApply,
|
||||||
|
doResetSync,
|
||||||
|
doSyncEncryptAndDecrypt,
|
||||||
|
} from 'redux/actions/sync';
|
||||||
|
|
||||||
|
// reducers
|
||||||
|
export { authReducer } from './redux/reducers/auth';
|
||||||
|
export { costInfoReducer } from './redux/reducers/cost_info';
|
||||||
|
export { blacklistReducer } from './redux/reducers/blacklist';
|
||||||
|
export { filteredReducer } from './redux/reducers/filtered';
|
||||||
|
export { statsReducer } from './redux/reducers/stats';
|
||||||
|
export { syncReducer } from './redux/reducers/sync';
|
||||||
|
|
||||||
|
// selectors
|
||||||
|
export { selectAuthToken, selectIsAuthenticating } from './redux/selectors/auth';
|
||||||
|
export {
|
||||||
|
selectFetchingCostInfoForUri,
|
||||||
|
selectCostInfoForUri,
|
||||||
|
selectAllCostInfoByUri,
|
||||||
|
selectFetchingCostInfo,
|
||||||
|
} from './redux/selectors/cost_info';
|
||||||
|
export {
|
||||||
|
selectBlackListedOutpoints,
|
||||||
|
selectBlacklistedOutpointMap,
|
||||||
|
} from './redux/selectors/blacklist';
|
||||||
|
export { selectFilteredOutpoints, selectFilteredOutpointMap } from './redux/selectors/filtered';
|
||||||
|
export {
|
||||||
|
selectViewCount,
|
||||||
|
selectViewCountForUri,
|
||||||
|
// makeSelectViewCountForUri, // deprecated
|
||||||
|
selectSubCountForUri,
|
||||||
|
// makeSelectSubCountForUri, // deprecated
|
||||||
|
} from './redux/selectors/stats';
|
||||||
|
export { selectBanStateForUri } from './redux/selectors/ban';
|
||||||
|
export {
|
||||||
|
selectHasSyncedWallet,
|
||||||
|
selectSyncData,
|
||||||
|
selectSyncHash,
|
||||||
|
selectSetSyncErrorMessage,
|
||||||
|
selectGetSyncErrorMessage,
|
||||||
|
selectGetSyncIsPending,
|
||||||
|
selectSetSyncIsPending,
|
||||||
|
selectSyncApplyIsPending,
|
||||||
|
selectHashChanged,
|
||||||
|
selectSyncApplyErrorMessage,
|
||||||
|
selectSyncApplyPasswordError,
|
||||||
|
} from './redux/selectors/sync';
|
238
extras/lbryinc/lbryio.js
Normal file
238
extras/lbryinc/lbryio.js
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import Lbry from 'lbry';
|
||||||
|
import querystring from 'querystring';
|
||||||
|
|
||||||
|
const Lbryio = {
|
||||||
|
enabled: true,
|
||||||
|
authenticationPromise: null,
|
||||||
|
exchangePromise: null,
|
||||||
|
exchangeLastFetched: null,
|
||||||
|
CONNECTION_STRING: 'https://api.lbry.com/',
|
||||||
|
};
|
||||||
|
|
||||||
|
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
||||||
|
const INTERNAL_APIS_DOWN = 'internal_apis_down';
|
||||||
|
|
||||||
|
// We can't use env's because they aren't passed into node_modules
|
||||||
|
Lbryio.setLocalApi = endpoint => {
|
||||||
|
Lbryio.CONNECTION_STRING = endpoint.replace(/\/*$/, '/'); // exactly one slash at the end;
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.call = (resource, action, params = {}, method = 'get') => {
|
||||||
|
if (!Lbryio.enabled) {
|
||||||
|
return Promise.reject(new Error(__('LBRY internal API is disabled')));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(method === 'get' || method === 'post')) {
|
||||||
|
return Promise.reject(new Error(__('Invalid method')));
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAndParse(response) {
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 500) {
|
||||||
|
return Promise.reject(INTERNAL_APIS_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response)
|
||||||
|
return response.json().then(json => {
|
||||||
|
let error;
|
||||||
|
if (json.error) {
|
||||||
|
error = new Error(json.error);
|
||||||
|
} else {
|
||||||
|
error = new Error('Unknown API error signature');
|
||||||
|
}
|
||||||
|
error.response = response; // This is primarily a hack used in actions/user.js
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRequest(url, options) {
|
||||||
|
return fetch(url, options).then(checkAndParse);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbryio.getAuthToken().then(token => {
|
||||||
|
const fullParams = { auth_token: token, ...params };
|
||||||
|
Object.keys(fullParams).forEach(key => {
|
||||||
|
const value = fullParams[key];
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
fullParams[key] = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const qs = querystring.stringify(fullParams);
|
||||||
|
let url = `${Lbryio.CONNECTION_STRING}${resource}/${action}?${qs}`;
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (method === 'post') {
|
||||||
|
options = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: qs,
|
||||||
|
};
|
||||||
|
url = `${Lbryio.CONNECTION_STRING}${resource}/${action}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeRequest(url, options).then(response => response.data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.authToken = null;
|
||||||
|
|
||||||
|
Lbryio.getAuthToken = () =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
if (Lbryio.authToken) {
|
||||||
|
resolve(Lbryio.authToken);
|
||||||
|
} else if (Lbryio.overrides.getAuthToken) {
|
||||||
|
Lbryio.overrides.getAuthToken().then(token => {
|
||||||
|
resolve(token);
|
||||||
|
});
|
||||||
|
} else if (typeof window !== 'undefined') {
|
||||||
|
const { store } = window;
|
||||||
|
if (store) {
|
||||||
|
const state = store.getState();
|
||||||
|
const token = state.auth ? state.auth.authToken : null;
|
||||||
|
Lbryio.authToken = token;
|
||||||
|
resolve(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
|
||||||
|
|
||||||
|
Lbryio.authenticate = (domain, language) => {
|
||||||
|
if (!Lbryio.enabled) {
|
||||||
|
const params = {
|
||||||
|
id: 1,
|
||||||
|
primary_email: 'disabled@lbry.io',
|
||||||
|
has_verified_email: true,
|
||||||
|
is_identity_verified: true,
|
||||||
|
is_reward_approved: false,
|
||||||
|
language: language || 'en',
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
resolve(params);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lbryio.authenticationPromise === null) {
|
||||||
|
Lbryio.authenticationPromise = new Promise((resolve, reject) => {
|
||||||
|
Lbryio.getAuthToken()
|
||||||
|
.then(token => {
|
||||||
|
if (!token || token.length > 60) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that token works
|
||||||
|
return Lbryio.getCurrentUser()
|
||||||
|
.then(user => user)
|
||||||
|
.catch(error => {
|
||||||
|
if (error === INTERNAL_APIS_DOWN) {
|
||||||
|
throw new Error('Internal APIS down');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(user => {
|
||||||
|
if (user) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbry.status()
|
||||||
|
.then(
|
||||||
|
status =>
|
||||||
|
new Promise((res, rej) => {
|
||||||
|
const appId =
|
||||||
|
domain && domain !== 'lbry.tv'
|
||||||
|
? (domain.replace(/[.]/gi, '') + status.installation_id).slice(0, 66)
|
||||||
|
: status.installation_id;
|
||||||
|
Lbryio.call(
|
||||||
|
'user',
|
||||||
|
'new',
|
||||||
|
{
|
||||||
|
auth_token: '',
|
||||||
|
language: language || 'en',
|
||||||
|
app_id: appId,
|
||||||
|
},
|
||||||
|
'post'
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.auth_token) {
|
||||||
|
throw new Error('auth_token was not set in the response');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { store } = window;
|
||||||
|
if (Lbryio.overrides.setAuthToken) {
|
||||||
|
Lbryio.overrides.setAuthToken(response.auth_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (store) {
|
||||||
|
store.dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS,
|
||||||
|
data: { authToken: response.auth_token },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Lbryio.authToken = response.auth_token;
|
||||||
|
return res(response);
|
||||||
|
})
|
||||||
|
.catch(error => rej(error));
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(newUser => {
|
||||||
|
if (!newUser) {
|
||||||
|
return Lbryio.getCurrentUser();
|
||||||
|
}
|
||||||
|
return newUser;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbryio.authenticationPromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.getStripeToken = () =>
|
||||||
|
Lbryio.CONNECTION_STRING.startsWith('http://localhost:')
|
||||||
|
? 'pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
|
||||||
|
: 'pk_live_e8M4dRNnCCbmpZzduEUZBgJO';
|
||||||
|
|
||||||
|
Lbryio.getExchangeRates = () => {
|
||||||
|
if (
|
||||||
|
!Lbryio.exchangeLastFetched ||
|
||||||
|
Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
|
||||||
|
) {
|
||||||
|
Lbryio.exchangePromise = new Promise((resolve, reject) => {
|
||||||
|
Lbryio.call('lbc', 'exchange_rate', {}, 'get', true)
|
||||||
|
.then(({ lbc_usd: LBC_USD, lbc_btc: LBC_BTC, btc_usd: BTC_USD }) => {
|
||||||
|
const rates = { LBC_USD, LBC_BTC, BTC_USD };
|
||||||
|
resolve(rates);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
Lbryio.exchangeLastFetched = Date.now();
|
||||||
|
}
|
||||||
|
return Lbryio.exchangePromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allow overriding lbryio methods
|
||||||
|
// The desktop app will need to use it for getAuthToken because we use electron's ipcRenderer
|
||||||
|
Lbryio.overrides = {};
|
||||||
|
Lbryio.setOverride = (methodName, newMethod) => {
|
||||||
|
Lbryio.overrides[methodName] = newMethod;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Lbryio;
|
38
extras/lbryinc/redux/actions/auth.js
Normal file
38
extras/lbryinc/redux/actions/auth.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
|
||||||
|
export function doGenerateAuthToken(installationId) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbryio.call(
|
||||||
|
'user',
|
||||||
|
'new',
|
||||||
|
{
|
||||||
|
auth_token: '',
|
||||||
|
language: 'en',
|
||||||
|
app_id: installationId,
|
||||||
|
},
|
||||||
|
'post'
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.auth_token) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_FAILURE,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS,
|
||||||
|
data: { authToken: response.auth_token },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GENERATE_AUTH_TOKEN_FAILURE,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
52
extras/lbryinc/redux/actions/blacklist.js
Normal file
52
extras/lbryinc/redux/actions/blacklist.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const CHECK_BLACK_LISTED_CONTENT_INTERVAL = 60 * 60 * 1000;
|
||||||
|
|
||||||
|
export function doFetchBlackListedOutpoints() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = ({ outpoints }) => {
|
||||||
|
const splitOutpoints = [];
|
||||||
|
if (outpoints) {
|
||||||
|
outpoints.forEach((outpoint, index) => {
|
||||||
|
const [txid, nout] = outpoint.split(':');
|
||||||
|
|
||||||
|
splitOutpoints[index] = { txid, nout: Number.parseInt(nout, 10) };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_COMPLETED,
|
||||||
|
data: {
|
||||||
|
outpoints: splitOutpoints,
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const failure = ({ message: error }) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_FAILED,
|
||||||
|
data: {
|
||||||
|
error,
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.call('file', 'list_blocked', {
|
||||||
|
auth_token: '',
|
||||||
|
}).then(success, failure);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doBlackListedOutpointsSubscribe() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(doFetchBlackListedOutpoints());
|
||||||
|
setInterval(() => dispatch(doFetchBlackListedOutpoints()), CHECK_BLACK_LISTED_CONTENT_INTERVAL);
|
||||||
|
};
|
||||||
|
}
|
35
extras/lbryinc/redux/actions/cost_info.js
Normal file
35
extras/lbryinc/redux/actions/cost_info.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import { selectClaimForUri } from 'redux/selectors/claims';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export function doFetchCostInfoForUri(uri) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const claim = selectClaimForUri(state, uri);
|
||||||
|
|
||||||
|
if (!claim) return;
|
||||||
|
|
||||||
|
function resolve(costInfo) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_COST_INFO_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uri,
|
||||||
|
costInfo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fee = claim.value ? claim.value.fee : undefined;
|
||||||
|
|
||||||
|
if (fee === undefined) {
|
||||||
|
resolve({ cost: 0, includesData: true });
|
||||||
|
} else if (fee.currency === 'LBC') {
|
||||||
|
resolve({ cost: fee.amount, includesData: true });
|
||||||
|
} else {
|
||||||
|
Lbryio.getExchangeRates().then(({ LBC_USD }) => {
|
||||||
|
resolve({ cost: fee.amount / LBC_USD, includesData: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
47
extras/lbryinc/redux/actions/filtered.js
Normal file
47
extras/lbryinc/redux/actions/filtered.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const CHECK_FILTERED_CONTENT_INTERVAL = 60 * 60 * 1000;
|
||||||
|
|
||||||
|
export function doFetchFilteredOutpoints() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILTERED_CONTENT_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = ({ outpoints }) => {
|
||||||
|
let formattedOutpoints = [];
|
||||||
|
if (outpoints) {
|
||||||
|
formattedOutpoints = outpoints.map(outpoint => {
|
||||||
|
const [txid, nout] = outpoint.split(':');
|
||||||
|
return { txid, nout: Number.parseInt(nout, 10) };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILTERED_CONTENT_COMPLETED,
|
||||||
|
data: {
|
||||||
|
outpoints: formattedOutpoints,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const failure = ({ error }) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILTERED_CONTENT_FAILED,
|
||||||
|
data: {
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbryio.call('file', 'list_filtered', { auth_token: '' }).then(success, failure);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doFilteredOutpointsSubscribe() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(doFetchFilteredOutpoints());
|
||||||
|
setInterval(() => dispatch(doFetchFilteredOutpoints()), CHECK_FILTERED_CONTENT_INTERVAL);
|
||||||
|
};
|
||||||
|
}
|
32
extras/lbryinc/redux/actions/stats.js
Normal file
32
extras/lbryinc/redux/actions/stats.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// @flow
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
export const doFetchViewCount = (claimIdCsv: string) => (dispatch: Dispatch) => {
|
||||||
|
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_STARTED });
|
||||||
|
|
||||||
|
return Lbryio.call('file', 'view_count', { claim_id: claimIdCsv })
|
||||||
|
.then((result: Array<number>) => {
|
||||||
|
const viewCounts = result;
|
||||||
|
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_COMPLETED, data: { claimIdCsv, viewCounts } });
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_FAILED, data: error });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doFetchSubCount = (claimId: string) => (dispatch: Dispatch) => {
|
||||||
|
dispatch({ type: ACTIONS.FETCH_SUB_COUNT_STARTED });
|
||||||
|
|
||||||
|
return Lbryio.call('subscription', 'sub_count', { claim_id: claimId })
|
||||||
|
.then((result: Array<number>) => {
|
||||||
|
const subCount = result[0];
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_SUB_COUNT_COMPLETED,
|
||||||
|
data: { claimId, subCount },
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
dispatch({ type: ACTIONS.FETCH_SUB_COUNT_FAILED, data: error });
|
||||||
|
});
|
||||||
|
};
|
289
extras/lbryinc/redux/actions/sync.js
Normal file
289
extras/lbryinc/redux/actions/sync.js
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import Lbry from 'lbry';
|
||||||
|
import { doWalletEncrypt, doWalletDecrypt } from 'redux/actions/wallet';
|
||||||
|
|
||||||
|
const NO_WALLET_ERROR = 'no wallet found for this user';
|
||||||
|
|
||||||
|
export function doSetDefaultAccount(success, failure) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_DEFAULT_ACCOUNT,
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbry.account_list()
|
||||||
|
.then(accountList => {
|
||||||
|
const { lbc_mainnet: accounts } = accountList;
|
||||||
|
let defaultId;
|
||||||
|
for (let i = 0; i < accounts.length; ++i) {
|
||||||
|
if (accounts[i].satoshis > 0) {
|
||||||
|
defaultId = accounts[i].id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In a case where there's no balance on either account
|
||||||
|
// assume the second (which is created after sync) as default
|
||||||
|
if (!defaultId && accounts.length > 1) {
|
||||||
|
defaultId = accounts[1].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default account
|
||||||
|
if (defaultId) {
|
||||||
|
Lbry.account_set({ account_id: defaultId, default: true })
|
||||||
|
.then(() => {
|
||||||
|
if (success) {
|
||||||
|
success();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (failure) {
|
||||||
|
failure(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (failure) {
|
||||||
|
// no default account to set
|
||||||
|
failure('Could not set a default account'); // fail
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (failure) {
|
||||||
|
failure(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSetSync(oldHash, newHash, data) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_SYNC_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
|
||||||
|
.then(response => {
|
||||||
|
if (!response.hash) {
|
||||||
|
throw Error('No hash returned for sync/set.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatch({
|
||||||
|
type: ACTIONS.SET_SYNC_COMPLETED,
|
||||||
|
data: { syncHash: response.hash },
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SET_SYNC_FAILED,
|
||||||
|
data: { error },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doGetSync(passedPassword, callback) {
|
||||||
|
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
|
||||||
|
|
||||||
|
function handleCallback(error, hasNewData) {
|
||||||
|
if (callback) {
|
||||||
|
if (typeof callback !== 'function') {
|
||||||
|
throw new Error('Second argument passed to "doGetSync" must be a function');
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(error, hasNewData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = {};
|
||||||
|
|
||||||
|
Lbry.wallet_status()
|
||||||
|
.then(status => {
|
||||||
|
if (status.is_locked) {
|
||||||
|
return Lbry.wallet_unlock({ password });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wallet is already unlocked
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.then(isUnlocked => {
|
||||||
|
if (isUnlocked) {
|
||||||
|
return Lbry.sync_hash();
|
||||||
|
}
|
||||||
|
data.unlockFailed = true;
|
||||||
|
throw new Error();
|
||||||
|
})
|
||||||
|
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||||
|
.then(response => {
|
||||||
|
const syncHash = response.hash;
|
||||||
|
data.syncHash = syncHash;
|
||||||
|
data.syncData = response.data;
|
||||||
|
data.changed = response.changed;
|
||||||
|
data.hasSyncedWallet = true;
|
||||||
|
|
||||||
|
if (response.changed) {
|
||||||
|
return Lbry.sync_apply({ password, data: response.data, blocking: true });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response) {
|
||||||
|
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||||
|
handleCallback(null, data.changed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { hash: walletHash, data: walletData } = response;
|
||||||
|
|
||||||
|
if (walletHash !== data.syncHash) {
|
||||||
|
// different local hash, need to synchronise
|
||||||
|
dispatch(doSetSync(data.syncHash, walletHash, walletData));
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||||
|
handleCallback(null, data.changed);
|
||||||
|
})
|
||||||
|
.catch(syncAttemptError => {
|
||||||
|
if (data.unlockFailed) {
|
||||||
|
dispatch({ type: ACTIONS.GET_SYNC_FAILED, data: { error: syncAttemptError } });
|
||||||
|
|
||||||
|
if (password !== '') {
|
||||||
|
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCallback(syncAttemptError);
|
||||||
|
} else if (data.hasSyncedWallet) {
|
||||||
|
const error =
|
||||||
|
(syncAttemptError && syncAttemptError.message) || 'Error getting synced wallet';
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_FAILED,
|
||||||
|
data: {
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Temp solution until we have a bad password error code
|
||||||
|
// Don't fail on blank passwords so we don't show a "password error" message
|
||||||
|
// before users have ever entered a password
|
||||||
|
if (password !== '') {
|
||||||
|
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCallback(error);
|
||||||
|
} else {
|
||||||
|
// user doesn't have a synced wallet
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_COMPLETED,
|
||||||
|
data: { hasSyncedWallet: false, syncHash: null },
|
||||||
|
});
|
||||||
|
|
||||||
|
// call sync_apply to get data to sync
|
||||||
|
// first time sync. use any string for old hash
|
||||||
|
if (syncAttemptError.message === NO_WALLET_ERROR) {
|
||||||
|
Lbry.sync_apply({ password })
|
||||||
|
.then(({ hash: walletHash, data: syncApplyData }) => {
|
||||||
|
dispatch(doSetSync('', walletHash, syncApplyData, password));
|
||||||
|
handleCallback();
|
||||||
|
})
|
||||||
|
.catch(syncApplyError => {
|
||||||
|
handleCallback(syncApplyError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSyncApply(syncHash, syncData, password) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SYNC_APPLY_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbry.sync_apply({ password, data: syncData })
|
||||||
|
.then(({ hash: walletHash, data: walletData }) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SYNC_APPLY_COMPLETED,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (walletHash !== syncHash) {
|
||||||
|
// different local hash, need to synchronise
|
||||||
|
dispatch(doSetSync(syncHash, walletHash, walletData));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.SYNC_APPLY_FAILED,
|
||||||
|
data: {
|
||||||
|
error:
|
||||||
|
'Invalid password specified. Please enter the password for your previously synchronised wallet.',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCheckSync() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
Lbry.sync_hash().then(hash => {
|
||||||
|
Lbryio.call('sync', 'get', { hash }, 'post')
|
||||||
|
.then(response => {
|
||||||
|
const data = {
|
||||||
|
hasSyncedWallet: true,
|
||||||
|
syncHash: response.hash,
|
||||||
|
syncData: response.data,
|
||||||
|
hashChanged: response.changed,
|
||||||
|
};
|
||||||
|
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// user doesn't have a synced wallet
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.GET_SYNC_COMPLETED,
|
||||||
|
data: { hasSyncedWallet: false, syncHash: null },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doResetSync() {
|
||||||
|
return dispatch =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
dispatch({ type: ACTIONS.SYNC_RESET });
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSyncEncryptAndDecrypt(oldPassword, newPassword, encrypt) {
|
||||||
|
return dispatch => {
|
||||||
|
const data = {};
|
||||||
|
return Lbry.sync_hash()
|
||||||
|
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||||
|
.then(syncGetResponse => {
|
||||||
|
data.oldHash = syncGetResponse.hash;
|
||||||
|
|
||||||
|
return Lbry.sync_apply({ password: oldPassword, data: syncGetResponse.data });
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
if (encrypt) {
|
||||||
|
dispatch(doWalletEncrypt(newPassword));
|
||||||
|
} else {
|
||||||
|
dispatch(doWalletDecrypt());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => Lbry.sync_apply({ password: newPassword }))
|
||||||
|
.then(syncApplyResponse => {
|
||||||
|
if (syncApplyResponse.hash !== data.oldHash) {
|
||||||
|
return dispatch(doSetSync(data.oldHash, syncApplyResponse.hash, syncApplyResponse.data));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
};
|
||||||
|
}
|
29
extras/lbryinc/redux/reducers/auth.js
Normal file
29
extras/lbryinc/redux/reducers/auth.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const reducers = {};
|
||||||
|
const defaultState = {
|
||||||
|
authenticating: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.GENERATE_AUTH_TOKEN_FAILURE] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
authToken: null,
|
||||||
|
authenticating: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.GENERATE_AUTH_TOKEN_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
authenticating: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
authToken: action.data.authToken,
|
||||||
|
authenticating: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function authReducer(state = defaultState, action) {
|
||||||
|
const handler = reducers[action.type];
|
||||||
|
if (handler) return handler(state, action);
|
||||||
|
return state;
|
||||||
|
}
|
37
extras/lbryinc/redux/reducers/blacklist.js
Normal file
37
extras/lbryinc/redux/reducers/blacklist.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
fetchingBlackListedOutpoints: false,
|
||||||
|
fetchingBlackListedOutpointsSucceed: undefined,
|
||||||
|
blackListedOutpoints: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const blacklistReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_STARTED]: state => ({
|
||||||
|
...state,
|
||||||
|
fetchingBlackListedOutpoints: true,
|
||||||
|
}),
|
||||||
|
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_COMPLETED]: (state, action) => {
|
||||||
|
const { outpoints, success } = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingBlackListedOutpoints: false,
|
||||||
|
fetchingBlackListedOutpointsSucceed: success,
|
||||||
|
blackListedOutpoints: outpoints,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_FAILED]: (state, action) => {
|
||||||
|
const { error, success } = action.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingBlackListedOutpoints: false,
|
||||||
|
fetchingBlackListedOutpointsSucceed: success,
|
||||||
|
fetchingBlackListedOutpointsError: error,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
38
extras/lbryinc/redux/reducers/cost_info.js
Normal file
38
extras/lbryinc/redux/reducers/cost_info.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
fetching: {},
|
||||||
|
byUri: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const costInfoReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_COST_INFO_STARTED]: (state, action) => {
|
||||||
|
const { uri } = action.data;
|
||||||
|
const newFetching = Object.assign({}, state.fetching);
|
||||||
|
newFetching[uri] = true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetching: newFetching,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.FETCH_COST_INFO_COMPLETED]: (state, action) => {
|
||||||
|
const { uri, costInfo } = action.data;
|
||||||
|
const newByUri = Object.assign({}, state.byUri);
|
||||||
|
const newFetching = Object.assign({}, state.fetching);
|
||||||
|
|
||||||
|
newByUri[uri] = costInfo;
|
||||||
|
delete newFetching[uri];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
byUri: newByUri,
|
||||||
|
fetching: newFetching,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
34
extras/lbryinc/redux/reducers/filtered.js
Normal file
34
extras/lbryinc/redux/reducers/filtered.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
loading: false,
|
||||||
|
filteredOutpoints: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filteredReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_FILTERED_CONTENT_STARTED]: state => ({
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
}),
|
||||||
|
[ACTIONS.FETCH_FILTERED_CONTENT_COMPLETED]: (state, action) => {
|
||||||
|
const { outpoints } = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
filteredOutpoints: outpoints,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ACTIONS.FETCH_FILTERED_CONTENT_FAILED]: (state, action) => {
|
||||||
|
const { error } = action.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
fetchingFilteredOutpointsError: error,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
55
extras/lbryinc/redux/reducers/stats.js
Normal file
55
extras/lbryinc/redux/reducers/stats.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
fetchingViewCount: false,
|
||||||
|
viewCountError: undefined,
|
||||||
|
viewCountById: {},
|
||||||
|
fetchingSubCount: false,
|
||||||
|
subCountError: undefined,
|
||||||
|
subCountById: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const statsReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.FETCH_VIEW_COUNT_STARTED]: state => ({ ...state, fetchingViewCount: true }),
|
||||||
|
[ACTIONS.FETCH_VIEW_COUNT_FAILED]: (state, action) => ({
|
||||||
|
...state,
|
||||||
|
viewCountError: action.data,
|
||||||
|
}),
|
||||||
|
[ACTIONS.FETCH_VIEW_COUNT_COMPLETED]: (state, action) => {
|
||||||
|
const { claimIdCsv, viewCounts } = action.data;
|
||||||
|
|
||||||
|
const viewCountById = Object.assign({}, state.viewCountById);
|
||||||
|
const claimIds = claimIdCsv.split(',');
|
||||||
|
|
||||||
|
if (claimIds.length === viewCounts.length) {
|
||||||
|
claimIds.forEach((claimId, index) => {
|
||||||
|
viewCountById[claimId] = viewCounts[index];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingViewCount: false,
|
||||||
|
viewCountById,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ACTIONS.FETCH_SUB_COUNT_STARTED]: state => ({ ...state, fetchingSubCount: true }),
|
||||||
|
[ACTIONS.FETCH_SUB_COUNT_FAILED]: (state, action) => ({
|
||||||
|
...state,
|
||||||
|
subCountError: action.data,
|
||||||
|
}),
|
||||||
|
[ACTIONS.FETCH_SUB_COUNT_COMPLETED]: (state, action) => {
|
||||||
|
const { claimId, subCount } = action.data;
|
||||||
|
|
||||||
|
const subCountById = { ...state.subCountById, [claimId]: subCount };
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingSubCount: false,
|
||||||
|
subCountById,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
89
extras/lbryinc/redux/reducers/sync.js
Normal file
89
extras/lbryinc/redux/reducers/sync.js
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
const reducers = {};
|
||||||
|
const defaultState = {
|
||||||
|
hasSyncedWallet: false,
|
||||||
|
syncHash: null,
|
||||||
|
syncData: null,
|
||||||
|
setSyncErrorMessage: null,
|
||||||
|
getSyncErrorMessage: null,
|
||||||
|
syncApplyErrorMessage: '',
|
||||||
|
syncApplyIsPending: false,
|
||||||
|
syncApplyPasswordError: false,
|
||||||
|
getSyncIsPending: false,
|
||||||
|
setSyncIsPending: false,
|
||||||
|
hashChanged: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.GET_SYNC_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
getSyncIsPending: true,
|
||||||
|
getSyncErrorMessage: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.GET_SYNC_COMPLETED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncHash: action.data.syncHash,
|
||||||
|
syncData: action.data.syncData,
|
||||||
|
hasSyncedWallet: action.data.hasSyncedWallet,
|
||||||
|
getSyncIsPending: false,
|
||||||
|
hashChanged: action.data.hashChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.GET_SYNC_FAILED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
getSyncIsPending: false,
|
||||||
|
getSyncErrorMessage: action.data.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SET_SYNC_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
setSyncIsPending: true,
|
||||||
|
setSyncErrorMessage: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SET_SYNC_FAILED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
setSyncIsPending: false,
|
||||||
|
setSyncErrorMessage: action.data.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SET_SYNC_COMPLETED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
setSyncIsPending: false,
|
||||||
|
setSyncErrorMessage: null,
|
||||||
|
hasSyncedWallet: true, // sync was successful, so the user has a synced wallet at this point
|
||||||
|
syncHash: action.data.syncHash,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_APPLY_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncApplyPasswordError: false,
|
||||||
|
syncApplyIsPending: true,
|
||||||
|
syncApplyErrorMessage: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_APPLY_COMPLETED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncApplyIsPending: false,
|
||||||
|
syncApplyErrorMessage: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_APPLY_FAILED] = (state, action) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncApplyIsPending: false,
|
||||||
|
syncApplyErrorMessage: action.data.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_APPLY_BAD_PASSWORD] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
syncApplyPasswordError: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
|
||||||
|
|
||||||
|
export function syncReducer(state = defaultState, action) {
|
||||||
|
const handler = reducers[action.type];
|
||||||
|
if (handler) return handler(state, action);
|
||||||
|
return state;
|
||||||
|
}
|
7
extras/lbryinc/redux/selectors/auth.js
Normal file
7
extras/lbryinc/redux/selectors/auth.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const selectState = state => state.auth || {};
|
||||||
|
|
||||||
|
export const selectAuthToken = createSelector(selectState, state => state.authToken);
|
||||||
|
|
||||||
|
export const selectIsAuthenticating = createSelector(selectState, state => state.authenticating);
|
73
extras/lbryinc/redux/selectors/ban.js
Normal file
73
extras/lbryinc/redux/selectors/ban.js
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
// TODO: This should be in 'redux/selectors/claim.js'. Temporarily putting it
|
||||||
|
// here to get past importing issues with 'lbryinc', which the real fix might
|
||||||
|
// involve moving it from 'extras' to 'ui' (big change).
|
||||||
|
|
||||||
|
import { createCachedSelector } from 're-reselect';
|
||||||
|
import { selectClaimForUri, makeSelectIsBlacklisted } from 'redux/selectors/claims';
|
||||||
|
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||||
|
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||||
|
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
|
||||||
|
import { getChannelFromClaim } from 'util/claim';
|
||||||
|
import { isURIEqual } from 'util/lbryURI';
|
||||||
|
|
||||||
|
export const selectBanStateForUri = createCachedSelector(
|
||||||
|
selectClaimForUri,
|
||||||
|
selectBlacklistedOutpointMap,
|
||||||
|
selectFilteredOutpointMap,
|
||||||
|
selectMutedChannels,
|
||||||
|
selectModerationBlockList,
|
||||||
|
(state, uri) => makeSelectIsBlacklisted(uri)(state),
|
||||||
|
(claim, blackListedOutpointMap, filteredOutpointMap, mutedChannelUris, personalBlocklist, isBlacklisted) => {
|
||||||
|
const banState = {};
|
||||||
|
|
||||||
|
if (!claim) {
|
||||||
|
return banState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelClaim = getChannelFromClaim(claim);
|
||||||
|
|
||||||
|
if (isBlacklisted) {
|
||||||
|
banState['blacklisted'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will be replaced once blocking is done at the wallet server level.
|
||||||
|
if (blackListedOutpointMap) {
|
||||||
|
if (
|
||||||
|
(channelClaim && blackListedOutpointMap[`${channelClaim.txid}:${channelClaim.nout}`]) ||
|
||||||
|
blackListedOutpointMap[`${claim.txid}:${claim.nout}`]
|
||||||
|
) {
|
||||||
|
banState['blacklisted'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're checking to see if the stream outpoint or signing channel outpoint
|
||||||
|
// is in the filter list.
|
||||||
|
if (filteredOutpointMap) {
|
||||||
|
if (
|
||||||
|
(channelClaim && filteredOutpointMap[`${channelClaim.txid}:${channelClaim.nout}`]) ||
|
||||||
|
filteredOutpointMap[`${claim.txid}:${claim.nout}`]
|
||||||
|
) {
|
||||||
|
banState['filtered'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// block stream claims
|
||||||
|
// block channel claims if we can't control for them in claim search
|
||||||
|
if (mutedChannelUris.length && channelClaim) {
|
||||||
|
if (mutedChannelUris.some((blockedUri) => isURIEqual(blockedUri, channelClaim.permanent_url))) {
|
||||||
|
banState['muted'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commentron blocklist
|
||||||
|
if (personalBlocklist.length && channelClaim) {
|
||||||
|
if (personalBlocklist.some((blockedUri) => isURIEqual(blockedUri, channelClaim.permanent_url))) {
|
||||||
|
banState['blocked'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return banState;
|
||||||
|
}
|
||||||
|
)((state, uri) => String(uri));
|
20
extras/lbryinc/redux/selectors/blacklist.js
Normal file
20
extras/lbryinc/redux/selectors/blacklist.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
export const selectState = state => state.blacklist || {};
|
||||||
|
|
||||||
|
export const selectBlackListedOutpoints = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.blackListedOutpoints
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectBlacklistedOutpointMap = createSelector(
|
||||||
|
selectBlackListedOutpoints,
|
||||||
|
outpoints =>
|
||||||
|
outpoints
|
||||||
|
? outpoints.reduce((acc, val) => {
|
||||||
|
const outpoint = `${val.txid}:${val.nout}`;
|
||||||
|
acc[outpoint] = 1;
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: {}
|
||||||
|
);
|
16
extras/lbryinc/redux/selectors/cost_info.js
Normal file
16
extras/lbryinc/redux/selectors/cost_info.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// @flow
|
||||||
|
type State = { costInfo: any };
|
||||||
|
|
||||||
|
export const selectState = (state: State) => state.costInfo || {};
|
||||||
|
export const selectAllCostInfoByUri = (state: State) => selectState(state).byUri;
|
||||||
|
export const selectFetchingCostInfo = (state: State) => selectState(state).fetching;
|
||||||
|
|
||||||
|
export const selectCostInfoForUri = (state: State, uri: string) => {
|
||||||
|
const costInfos = selectAllCostInfoByUri(state);
|
||||||
|
return costInfos && costInfos[uri];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectFetchingCostInfoForUri = (state: State, uri: string) => {
|
||||||
|
const fetchingByUri = selectFetchingCostInfo(state);
|
||||||
|
return fetchingByUri && fetchingByUri[uri];
|
||||||
|
};
|
20
extras/lbryinc/redux/selectors/filtered.js
Normal file
20
extras/lbryinc/redux/selectors/filtered.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
export const selectState = state => state.filtered || {};
|
||||||
|
|
||||||
|
export const selectFilteredOutpoints = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.filteredOutpoints
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectFilteredOutpointMap = createSelector(
|
||||||
|
selectFilteredOutpoints,
|
||||||
|
outpoints =>
|
||||||
|
outpoints
|
||||||
|
? outpoints.reduce((acc, val) => {
|
||||||
|
const outpoint = `${val.txid}:${val.nout}`;
|
||||||
|
acc[outpoint] = 1;
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: {}
|
||||||
|
);
|
20
extras/lbryinc/redux/selectors/stats.js
Normal file
20
extras/lbryinc/redux/selectors/stats.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// @flow
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { selectClaimIdForUri } from 'redux/selectors/claims';
|
||||||
|
|
||||||
|
type State = { claims: any };
|
||||||
|
const selectState = state => state.stats || {};
|
||||||
|
export const selectViewCount = createSelector(selectState, state => state.viewCountById);
|
||||||
|
export const selectSubCount = createSelector(selectState, state => state.subCountById);
|
||||||
|
|
||||||
|
export const selectViewCountForUri = (state: State, uri: string) => {
|
||||||
|
const claimId = selectClaimIdForUri(state, uri);
|
||||||
|
const viewCountById = selectViewCount(state);
|
||||||
|
return claimId ? viewCountById[claimId] || 0 : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectSubCountForUri = (state: State, uri: string) => {
|
||||||
|
const claimId = selectClaimIdForUri(state, uri);
|
||||||
|
const subCountById = selectSubCount(state);
|
||||||
|
return claimId ? subCountById[claimId] || 0 : 0;
|
||||||
|
};
|
40
extras/lbryinc/redux/selectors/sync.js
Normal file
40
extras/lbryinc/redux/selectors/sync.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const selectState = state => state.sync || {};
|
||||||
|
|
||||||
|
export const selectHasSyncedWallet = createSelector(selectState, state => state.hasSyncedWallet);
|
||||||
|
|
||||||
|
export const selectSyncHash = createSelector(selectState, state => state.syncHash);
|
||||||
|
|
||||||
|
export const selectSyncData = createSelector(selectState, state => state.syncData);
|
||||||
|
|
||||||
|
export const selectSetSyncErrorMessage = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.setSyncErrorMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectGetSyncErrorMessage = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.getSyncErrorMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectGetSyncIsPending = createSelector(selectState, state => state.getSyncIsPending);
|
||||||
|
|
||||||
|
export const selectSetSyncIsPending = createSelector(selectState, state => state.setSyncIsPending);
|
||||||
|
|
||||||
|
export const selectHashChanged = createSelector(selectState, state => state.hashChanged);
|
||||||
|
|
||||||
|
export const selectSyncApplyIsPending = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.syncApplyIsPending
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectSyncApplyErrorMessage = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.syncApplyErrorMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectSyncApplyPasswordError = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.syncApplyPasswordError
|
||||||
|
);
|
17
extras/lbryinc/util/redux-utils-delete-me.js
Normal file
17
extras/lbryinc/util/redux-utils-delete-me.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// util for creating reducers
|
||||||
|
// based off of redux-actions
|
||||||
|
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
|
||||||
|
const handler = actionMap[action.type];
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
const newState = handler(state, action);
|
||||||
|
return Object.assign({}, state, newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// just return the original state if no handler
|
||||||
|
// returning a copy here breaks redux-persist
|
||||||
|
return state;
|
||||||
|
};
|
10
extras/lbryinc/util/swap-json.js
Normal file
10
extras/lbryinc/util/swap-json.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export function swapKeyAndValue(dict) {
|
||||||
|
const ret = {};
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const key in dict) {
|
||||||
|
if (dict.hasOwnProperty(key)) {
|
||||||
|
ret[dict[key]] = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
78
extras/lbryinc/util/transifex-upload.js
Normal file
78
extras/lbryinc/util/transifex-upload.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
const apiBaseUrl = 'https://www.transifex.com/api/2/project';
|
||||||
|
const resource = 'app-strings';
|
||||||
|
|
||||||
|
export function doTransifexUpload(contents, project, token, success, fail) {
|
||||||
|
const url = `${apiBaseUrl}/${project}/resources/`;
|
||||||
|
const updateUrl = `${apiBaseUrl}/${project}/resource/${resource}/content/`;
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Basic ${Buffer.from(`api:${token}`).toString('base64')}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
accept_translations: true,
|
||||||
|
i18n_type: 'KEYVALUEJSON',
|
||||||
|
name: resource,
|
||||||
|
slug: resource,
|
||||||
|
content: contents,
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleResponse(text) {
|
||||||
|
let json;
|
||||||
|
try {
|
||||||
|
// transifex api returns Python dicts for some reason.
|
||||||
|
// Any way to get the api to return valid JSON?
|
||||||
|
json = JSON.parse(text);
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
success(json || text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(err) {
|
||||||
|
if (fail) {
|
||||||
|
fail(err.message ? err.message : 'Could not upload strings resource to Transifex');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the resource exists
|
||||||
|
fetch(updateUrl, { headers })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(() => {
|
||||||
|
// perform an update
|
||||||
|
fetch(updateUrl, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({ content: contents }),
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status !== 200 && response.status !== 201) {
|
||||||
|
throw new Error('failed to update transifex');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then(handleResponse)
|
||||||
|
.catch(handleError);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// resource doesn't exist, create a fresh resource
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(req),
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status !== 200 && response.status !== 201) {
|
||||||
|
throw new Error('failed to upload to transifex');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then(handleResponse)
|
||||||
|
.catch(handleError);
|
||||||
|
});
|
||||||
|
}
|
3
extras/recsys/index.js
Normal file
3
extras/recsys/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import Recsys from './recsys';
|
||||||
|
|
||||||
|
export default Recsys;
|
|
@ -1,10 +1,12 @@
|
||||||
import { selectUser } from 'redux/selectors/user';
|
import { selectUser } from 'redux/selectors/user';
|
||||||
import { makeSelectRecommendedRecsysIdForClaimId } from 'redux/selectors/search';
|
import { makeSelectRecommendedRecsysIdForClaimId } from 'redux/selectors/search';
|
||||||
import { v4 as Uuidv4 } from 'uuid';
|
import { v4 as Uuidv4 } from 'uuid';
|
||||||
import { parseURI, SETTINGS, makeSelectClaimForUri } from 'lbry-redux';
|
import { parseURI } from 'util/lbryURI';
|
||||||
|
import * as SETTINGS from 'constants/settings';
|
||||||
|
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
import { selectPlayingUri, selectPrimaryUri } from 'redux/selectors/content';
|
import { selectPlayingUri, selectPrimaryUri } from 'redux/selectors/content';
|
||||||
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
|
||||||
import { history } from './store';
|
import { history } from 'ui/store';
|
||||||
|
|
||||||
const recsysEndpoint = 'https://clickstream.odysee.com/log/video/view';
|
const recsysEndpoint = 'https://clickstream.odysee.com/log/video/view';
|
||||||
const recsysId = 'lighthouse-v0';
|
const recsysId = 'lighthouse-v0';
|
||||||
|
@ -92,7 +94,8 @@ const recsys = {
|
||||||
createRecsysEntry: function (claimId, parentUuid) {
|
createRecsysEntry: function (claimId, parentUuid) {
|
||||||
if (window.store && claimId) {
|
if (window.store && claimId) {
|
||||||
const state = window.store.getState();
|
const state = window.store.getState();
|
||||||
const { id: userId } = selectUser(state);
|
const user = selectUser(state);
|
||||||
|
const userId = user ? user.id : null;
|
||||||
if (parentUuid) {
|
if (parentUuid) {
|
||||||
// Make a stub entry that will be filled out on page load
|
// Make a stub entry that will be filled out on page load
|
||||||
recsys.entries[claimId] = {
|
recsys.entries[claimId] = {
|
10
flow-typed/Blocklist.js
vendored
Normal file
10
flow-typed/Blocklist.js
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
declare type BlocklistState = {
|
||||||
|
blockedChannels: Array<string>
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type BlocklistAction = {
|
||||||
|
type: string,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
},
|
||||||
|
};
|
251
flow-typed/Claim.js
vendored
Normal file
251
flow-typed/Claim.js
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
declare type Claim = StreamClaim | ChannelClaim | CollectionClaim;
|
||||||
|
|
||||||
|
declare type ChannelClaim = GenericClaim & {
|
||||||
|
value: ChannelMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionClaim = GenericClaim & {
|
||||||
|
value: CollectionMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type StreamClaim = GenericClaim & {
|
||||||
|
value: StreamMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type GenericClaim = {
|
||||||
|
address: string, // address associated with tx
|
||||||
|
amount: string, // bid amount at time of tx
|
||||||
|
canonical_url: string, // URL with short id, includes channel with short id
|
||||||
|
claim_id: string, // unique claim identifier
|
||||||
|
claim_sequence: number, // not being used currently
|
||||||
|
claim_op: 'create' | 'update',
|
||||||
|
confirmations: number,
|
||||||
|
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
|
||||||
|
timestamp?: number, // date of last transaction
|
||||||
|
height: number, // block height the tx was confirmed
|
||||||
|
is_channel_signature_valid?: boolean,
|
||||||
|
is_my_output: boolean,
|
||||||
|
name: string,
|
||||||
|
normalized_name: string, // `name` normalized via unicode NFD spec,
|
||||||
|
nout: number, // index number for an output of a tx
|
||||||
|
permanent_url: string, // name + claim_id
|
||||||
|
short_url: string, // permanent_url with short id, no channel
|
||||||
|
txid: string, // unique tx id
|
||||||
|
type: 'claim' | 'update' | 'support',
|
||||||
|
value_type: 'stream' | 'channel' | 'collection',
|
||||||
|
signing_channel?: ChannelClaim,
|
||||||
|
reposted_claim?: GenericClaim,
|
||||||
|
repost_channel_url?: string,
|
||||||
|
repost_url?: string,
|
||||||
|
repost_bid_amount?: string,
|
||||||
|
purchase_receipt?: PurchaseReceipt,
|
||||||
|
meta: {
|
||||||
|
activation_height: number,
|
||||||
|
claims_in_channel?: number,
|
||||||
|
creation_height: number,
|
||||||
|
creation_timestamp: number,
|
||||||
|
effective_amount: string,
|
||||||
|
expiration_height: number,
|
||||||
|
is_controlling: boolean,
|
||||||
|
support_amount: string,
|
||||||
|
reposted: number,
|
||||||
|
trending_global: number,
|
||||||
|
trending_group: number,
|
||||||
|
trending_local: number,
|
||||||
|
trending_mixed: number,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type GenericMetadata = {
|
||||||
|
title?: string,
|
||||||
|
description?: string,
|
||||||
|
thumbnail?: {
|
||||||
|
url?: string,
|
||||||
|
},
|
||||||
|
languages?: Array<string>,
|
||||||
|
tags?: Array<string>,
|
||||||
|
locations?: Array<Location>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelMetadata = GenericMetadata & {
|
||||||
|
public_key: string,
|
||||||
|
public_key_id: string,
|
||||||
|
cover_url?: string,
|
||||||
|
email?: string,
|
||||||
|
website_url?: string,
|
||||||
|
featured?: Array<string>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionMetadata = GenericMetadata & {
|
||||||
|
claims: Array<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type StreamMetadata = GenericMetadata & {
|
||||||
|
license?: string, // License "title" ex: Creative Commons, Custom copyright
|
||||||
|
license_url?: string, // Link to full license
|
||||||
|
release_time?: number, // linux timestamp
|
||||||
|
author?: string,
|
||||||
|
|
||||||
|
source: {
|
||||||
|
sd_hash: string,
|
||||||
|
media_type?: string,
|
||||||
|
hash?: string,
|
||||||
|
name?: string, // file name
|
||||||
|
size?: number, // size of file in bytes
|
||||||
|
},
|
||||||
|
|
||||||
|
// Only exists if a stream has a fee
|
||||||
|
fee?: Fee,
|
||||||
|
|
||||||
|
stream_type: 'video' | 'audio' | 'image' | 'software',
|
||||||
|
// Below correspond to `stream_type`
|
||||||
|
video?: {
|
||||||
|
duration: number,
|
||||||
|
height: number,
|
||||||
|
width: number,
|
||||||
|
},
|
||||||
|
audio?: {
|
||||||
|
duration: number,
|
||||||
|
},
|
||||||
|
image?: {
|
||||||
|
height: number,
|
||||||
|
width: number,
|
||||||
|
},
|
||||||
|
software?: {
|
||||||
|
os: string,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Location = {
|
||||||
|
latitude?: number,
|
||||||
|
longitude?: number,
|
||||||
|
country?: string,
|
||||||
|
state?: string,
|
||||||
|
city?: string,
|
||||||
|
code?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Fee = {
|
||||||
|
amount: string,
|
||||||
|
currency: string,
|
||||||
|
address: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseReceipt = {
|
||||||
|
address: string,
|
||||||
|
amount: string,
|
||||||
|
claim_id: string,
|
||||||
|
confirmations: number,
|
||||||
|
height: number,
|
||||||
|
nout: number,
|
||||||
|
timestamp: number,
|
||||||
|
txid: string,
|
||||||
|
type: 'purchase',
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ClaimErrorCensor = {
|
||||||
|
address: string,
|
||||||
|
amount: string,
|
||||||
|
canonical_url: string,
|
||||||
|
claim_id: string,
|
||||||
|
claim_op: string,
|
||||||
|
confirmations: number,
|
||||||
|
has_signing_key: boolean,
|
||||||
|
height: number,
|
||||||
|
meta: {
|
||||||
|
activation_height: number,
|
||||||
|
claims_in_channel: number,
|
||||||
|
creation_height: number,
|
||||||
|
creation_timestamp: number,
|
||||||
|
effective_amount: string,
|
||||||
|
expiration_height: number,
|
||||||
|
is_controlling: boolean,
|
||||||
|
reposted: number,
|
||||||
|
support_amount: string,
|
||||||
|
take_over_height: number,
|
||||||
|
},
|
||||||
|
name: string,
|
||||||
|
normalized_name: string,
|
||||||
|
nout: number,
|
||||||
|
permanent_url: string,
|
||||||
|
short_url: string,
|
||||||
|
timestamp: number,
|
||||||
|
txid: string,
|
||||||
|
type: string,
|
||||||
|
value: {
|
||||||
|
public_key: string,
|
||||||
|
public_key_id: string,
|
||||||
|
},
|
||||||
|
value_type: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ClaimActionResolveInfo = {
|
||||||
|
[string]: {
|
||||||
|
stream: ?StreamClaim,
|
||||||
|
channel: ?ChannelClaim,
|
||||||
|
claimsInChannel: ?number,
|
||||||
|
collection: ?CollectionClaim,
|
||||||
|
errorCensor: ?ClaimErrorCensor,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ChannelUpdateParams = {
|
||||||
|
claim_id: string,
|
||||||
|
bid?: string,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
languages?: Array<string>,
|
||||||
|
locations?: Array<string>,
|
||||||
|
blocking?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ChannelPublishParams = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionUpdateParams = {
|
||||||
|
claim_id: string,
|
||||||
|
claim_ids?: Array<string>,
|
||||||
|
bid?: string,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
languages?: Array<string>,
|
||||||
|
locations?: Array<string>,
|
||||||
|
blocking?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionPublishParams = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
claim_ids: Array<string>,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
}
|
29
flow-typed/CoinSwap.js
vendored
Normal file
29
flow-typed/CoinSwap.js
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
declare type CoinSwapInfo = {
|
||||||
|
chargeCode: string,
|
||||||
|
coins: Array<string>,
|
||||||
|
sendAddresses: { [string]: string},
|
||||||
|
sendAmounts: { [string]: any },
|
||||||
|
lbcAmount: number,
|
||||||
|
status?: {
|
||||||
|
status: string,
|
||||||
|
receiptCurrency: string,
|
||||||
|
receiptTxid: string,
|
||||||
|
lbcTxid: string,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CoinSwapState = {
|
||||||
|
coinSwaps: Array<CoinSwapInfo>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CoinSwapAddAction = {
|
||||||
|
type: string,
|
||||||
|
data: CoinSwapInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CoinSwapRemoveAction = {
|
||||||
|
type: string,
|
||||||
|
data: {
|
||||||
|
chargeCode: string,
|
||||||
|
},
|
||||||
|
};
|
33
flow-typed/Collections.js
vendored
Normal file
33
flow-typed/Collections.js
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
declare type Collection = {
|
||||||
|
id: string,
|
||||||
|
items: Array<?string>,
|
||||||
|
name: string,
|
||||||
|
type: string,
|
||||||
|
updatedAt: number,
|
||||||
|
totalItems?: number,
|
||||||
|
sourceId?: string, // if copied, claimId of original collection
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionState = {
|
||||||
|
unpublished: CollectionGroup,
|
||||||
|
resolved: CollectionGroup,
|
||||||
|
pending: CollectionGroup,
|
||||||
|
edited: CollectionGroup,
|
||||||
|
builtin: CollectionGroup,
|
||||||
|
saved: Array<string>,
|
||||||
|
isResolvingCollectionById: { [string]: boolean },
|
||||||
|
error?: string | null,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionGroup = {
|
||||||
|
[string]: Collection,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionEditParams = {
|
||||||
|
uris?: Array<string>,
|
||||||
|
remove?: boolean,
|
||||||
|
replace?: boolean,
|
||||||
|
order?: { from: number, to: number },
|
||||||
|
type?: string,
|
||||||
|
name?: string,
|
||||||
|
}
|
8
flow-typed/Comment.js
vendored
8
flow-typed/Comment.js
vendored
|
@ -43,7 +43,9 @@ declare type CommentsState = {
|
||||||
linkedCommentAncestors: { [string]: Array<string> }, // {"linkedCommentId": ["parentId", "grandParentId", ...]}
|
linkedCommentAncestors: { [string]: Array<string> }, // {"linkedCommentId": ["parentId", "grandParentId", ...]}
|
||||||
pinnedCommentsById: {}, // ClaimId -> array of pinned comment IDs
|
pinnedCommentsById: {}, // ClaimId -> array of pinned comment IDs
|
||||||
isLoading: boolean,
|
isLoading: boolean,
|
||||||
|
isLoadingById: boolean,
|
||||||
isLoadingByParentId: { [string]: boolean },
|
isLoadingByParentId: { [string]: boolean },
|
||||||
|
isCommenting: boolean,
|
||||||
myComments: ?Set<string>,
|
myComments: ?Set<string>,
|
||||||
isFetchingReacts: boolean,
|
isFetchingReacts: boolean,
|
||||||
myReactsByCommentId: ?{ [string]: Array<string> }, // {"CommentId:MyChannelId": ["like", "dislike", ...]}
|
myReactsByCommentId: ?{ [string]: Array<string> }, // {"CommentId:MyChannelId": ["like", "dislike", ...]}
|
||||||
|
@ -110,14 +112,14 @@ declare type ReactionListResponse = {
|
||||||
declare type CommentListParams = {
|
declare type CommentListParams = {
|
||||||
page: number, // pagination: which page of results
|
page: number, // pagination: which page of results
|
||||||
page_size: number, // pagination: nr of comments to show in a page (max 200)
|
page_size: number, // pagination: nr of comments to show in a page (max 200)
|
||||||
claim_id: string, // claim id of claim being commented on
|
claim_id?: string, // claim id of claim being commented on
|
||||||
channel_name?: string, // signing channel name of claim (enables 'commentsEnabled' check)
|
channel_name?: string, // signing channel name of claim (enables 'commentsEnabled' check)
|
||||||
channel_id?: string, // signing channel claim id of claim (enables 'commentsEnabled' check)
|
channel_id?: string, // signing channel claim id of claim (enables 'commentsEnabled' check)
|
||||||
author_claim_id?: string, // filters comments to just this author
|
author_claim_id?: string, // filters comments to just this author
|
||||||
parent_id?: string, // filters comments to those under this thread
|
parent_id?: string, // filters comments to those under this thread
|
||||||
top_level?: boolean, // filters to only top level comments
|
top_level?: boolean, // filters to only top level comments
|
||||||
hidden?: boolean, // if true, will show hidden comments as well
|
hidden?: boolean, // if true, will show hidden comments as well
|
||||||
sort_by?: number, // NEWEST=0, OLDEST=1, CONTROVERSY=2, POPULARITY=3,
|
sort_by?: number, // @see: ui/constants/comments.js::SORT_BY
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type CommentListResponse = {
|
declare type CommentListResponse = {
|
||||||
|
@ -176,7 +178,7 @@ declare type CommentCreateParams = {
|
||||||
claim_id: string,
|
claim_id: string,
|
||||||
parent_id?: string,
|
parent_id?: string,
|
||||||
signature: string,
|
signature: string,
|
||||||
signing_ts: number,
|
signing_ts: string,
|
||||||
support_tx_id?: string,
|
support_tx_id?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
78
flow-typed/File.js
vendored
Normal file
78
flow-typed/File.js
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
declare type FileListItem = {
|
||||||
|
metadata: StreamMetadata,
|
||||||
|
added_on: number,
|
||||||
|
blobs_completed: number,
|
||||||
|
blobs_in_stream: number,
|
||||||
|
blobs_remaining: number,
|
||||||
|
channel_claim_id: string,
|
||||||
|
channel_name: string,
|
||||||
|
claim_id: string,
|
||||||
|
claim_name: string,
|
||||||
|
completed: false,
|
||||||
|
content_fee?: { txid: string },
|
||||||
|
purchase_receipt?: { txid: string, amount: string },
|
||||||
|
download_directory: string,
|
||||||
|
download_path: string,
|
||||||
|
file_name: string,
|
||||||
|
key: string,
|
||||||
|
mime_type: string,
|
||||||
|
nout: number,
|
||||||
|
outpoint: string,
|
||||||
|
points_paid: number,
|
||||||
|
protobuf: string,
|
||||||
|
reflector_progress: number,
|
||||||
|
sd_hash: string,
|
||||||
|
status: string,
|
||||||
|
stopped: false,
|
||||||
|
stream_hash: string,
|
||||||
|
stream_name: string,
|
||||||
|
streaming_url: string,
|
||||||
|
suggested_file_name: string,
|
||||||
|
total_bytes: number,
|
||||||
|
total_bytes_lower_bound: number,
|
||||||
|
is_fully_reflected: boolean,
|
||||||
|
// TODO: sdk plans to change `tx`
|
||||||
|
// It isn't currently used by the apps
|
||||||
|
tx: {},
|
||||||
|
txid: string,
|
||||||
|
uploading_to_reflector: boolean,
|
||||||
|
written_bytes: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type FileState = {
|
||||||
|
failedPurchaseUris: Array<string>,
|
||||||
|
purchasedUris: Array<string>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseUriCompleted = {
|
||||||
|
type: ACTIONS.PURCHASE_URI_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
streamingUrl: string,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseUriFailed = {
|
||||||
|
type: ACTIONS.PURCHASE_URI_FAILED,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
error: any,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseUriStarted = {
|
||||||
|
type: ACTIONS.PURCHASE_URI_STARTED,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
streamingUrl: string,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DeletePurchasedUri = {
|
||||||
|
type: ACTIONS.CLEAR_PURCHASED_URI_SUCCESS,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
},
|
||||||
|
};
|
369
flow-typed/Lbry.js
vendored
Normal file
369
flow-typed/Lbry.js
vendored
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
// @flow
|
||||||
|
declare type StatusResponse = {
|
||||||
|
blob_manager: {
|
||||||
|
finished_blobs: number,
|
||||||
|
},
|
||||||
|
blockchain_headers: {
|
||||||
|
download_progress: number,
|
||||||
|
downloading_headers: boolean,
|
||||||
|
},
|
||||||
|
dht: {
|
||||||
|
node_id: string,
|
||||||
|
peers_in_routing_table: number,
|
||||||
|
},
|
||||||
|
hash_announcer: {
|
||||||
|
announce_queue_size: number,
|
||||||
|
},
|
||||||
|
installation_id: string,
|
||||||
|
is_running: boolean,
|
||||||
|
skipped_components: Array<string>,
|
||||||
|
startup_status: {
|
||||||
|
blob_manager: boolean,
|
||||||
|
blockchain_headers: boolean,
|
||||||
|
database: boolean,
|
||||||
|
dht: boolean,
|
||||||
|
exchange_rate_manager: boolean,
|
||||||
|
hash_announcer: boolean,
|
||||||
|
peer_protocol_server: boolean,
|
||||||
|
stream_manager: boolean,
|
||||||
|
upnp: boolean,
|
||||||
|
wallet: boolean,
|
||||||
|
},
|
||||||
|
stream_manager: {
|
||||||
|
managed_files: number,
|
||||||
|
},
|
||||||
|
upnp: {
|
||||||
|
aioupnp_version: string,
|
||||||
|
dht_redirect_set: boolean,
|
||||||
|
external_ip: string,
|
||||||
|
gateway: string,
|
||||||
|
peer_redirect_set: boolean,
|
||||||
|
redirects: {},
|
||||||
|
},
|
||||||
|
wallet: ?{
|
||||||
|
connected: string,
|
||||||
|
best_blockhash: string,
|
||||||
|
blocks: number,
|
||||||
|
blocks_behind: number,
|
||||||
|
is_encrypted: boolean,
|
||||||
|
is_locked: boolean,
|
||||||
|
headers_synchronization_progress: number,
|
||||||
|
available_servers: number,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type VersionResponse = {
|
||||||
|
build: string,
|
||||||
|
lbrynet_version: string,
|
||||||
|
os_release: string,
|
||||||
|
os_system: string,
|
||||||
|
platform: string,
|
||||||
|
processor: string,
|
||||||
|
python_version: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type BalanceResponse = {
|
||||||
|
available: string,
|
||||||
|
reserved: string,
|
||||||
|
reserved_subtotals: ?{
|
||||||
|
claims: string,
|
||||||
|
supports: string,
|
||||||
|
tips: string,
|
||||||
|
},
|
||||||
|
total: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ResolveResponse = {
|
||||||
|
// Keys are the url(s) passed to resolve
|
||||||
|
[string]: { error?: {}, stream?: StreamClaim, channel?: ChannelClaim, collection?: CollectionClaim, claimsInChannel?: number },
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type GetResponse = FileListItem & { error?: string };
|
||||||
|
|
||||||
|
declare type GenericTxResponse = {
|
||||||
|
height: number,
|
||||||
|
hex: string,
|
||||||
|
inputs: Array<{}>,
|
||||||
|
outputs: Array<{}>,
|
||||||
|
total_fee: string,
|
||||||
|
total_input: string,
|
||||||
|
total_output: string,
|
||||||
|
txid: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PublishResponse = GenericTxResponse & {
|
||||||
|
// Only first value in outputs is a claim
|
||||||
|
// That's the only value we care about
|
||||||
|
outputs: Array<Claim>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ClaimSearchResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ClaimListResponse = {
|
||||||
|
items: Array<ChannelClaim | Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelCreateResponse = GenericTxResponse & {
|
||||||
|
outputs: Array<ChannelClaim>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelUpdateResponse = GenericTxResponse & {
|
||||||
|
outputs: Array<ChannelClaim>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentCreateResponse = Comment;
|
||||||
|
declare type CommentUpdateResponse = Comment;
|
||||||
|
|
||||||
|
declare type MyReactions = {
|
||||||
|
// Keys are the commentId
|
||||||
|
[string]: Array<string>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type OthersReactions = {
|
||||||
|
// Keys are the commentId
|
||||||
|
[string]: {
|
||||||
|
// Keys are the reaction_type, e.g. 'like'
|
||||||
|
[string]: number,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentReactListResponse = {
|
||||||
|
my_reactions: Array<MyReactions>,
|
||||||
|
others_reactions: Array<OthersReactions>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentHideResponse = {
|
||||||
|
// keyed by the CommentIds entered
|
||||||
|
[string]: { hidden: boolean },
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentPinResponse = {
|
||||||
|
// keyed by the CommentIds entered
|
||||||
|
items: Comment,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CommentAbandonResponse = {
|
||||||
|
// keyed by the CommentId given
|
||||||
|
abandoned: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelListResponse = {
|
||||||
|
items: Array<ChannelClaim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ChannelSignResponse = {
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionCreateResponse = {
|
||||||
|
outputs: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionListResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionResolveResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
total_items: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionResolveOptions = {
|
||||||
|
claim_id: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionListOptions = {
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
resolve?: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type FileListResponse = {
|
||||||
|
items: Array<FileListItem>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type TxListResponse = {
|
||||||
|
items: Array<Transaction>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type SupportListResponse = {
|
||||||
|
items: Array<Support>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type BlobListResponse = { items: Array<string> };
|
||||||
|
|
||||||
|
declare type WalletListResponse = Array<{
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
}>;
|
||||||
|
|
||||||
|
declare type WalletStatusResponse = {
|
||||||
|
is_encrypted: boolean,
|
||||||
|
is_locked: boolean,
|
||||||
|
is_syncing: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type SyncApplyResponse = {
|
||||||
|
hash: string,
|
||||||
|
data: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type SupportAbandonResponse = GenericTxResponse;
|
||||||
|
|
||||||
|
declare type StreamListResponse = {
|
||||||
|
items: Array<StreamClaim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type StreamRepostOptions = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
claim_id: string,
|
||||||
|
channel_id?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type StreamRepostResponse = GenericTxResponse;
|
||||||
|
|
||||||
|
declare type PurchaseListResponse = {
|
||||||
|
items: Array<PurchaseReceipt & { claim: StreamClaim }>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseListOptions = {
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
resolve: boolean,
|
||||||
|
claim_id?: string,
|
||||||
|
channel_id?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Types used in the generic Lbry object that is exported
|
||||||
|
//
|
||||||
|
declare type LbryTypes = {
|
||||||
|
isConnected: boolean,
|
||||||
|
connectPromise: any, // null |
|
||||||
|
connect: () => any, // void | Promise<any> ?
|
||||||
|
daemonConnectionString: string,
|
||||||
|
alternateConnectionString: string,
|
||||||
|
methodsUsingAlternateConnectionString: Array<string>,
|
||||||
|
apiRequestHeaders: { [key: string]: string },
|
||||||
|
setDaemonConnectionString: string => void,
|
||||||
|
setApiHeader: (string, string) => void,
|
||||||
|
unsetApiHeader: string => void,
|
||||||
|
overrides: { [string]: ?Function },
|
||||||
|
setOverride: (string, Function) => void,
|
||||||
|
// getMediaType: (?string, ?string) => string,
|
||||||
|
|
||||||
|
// Lbry Methods
|
||||||
|
stop: () => Promise<string>,
|
||||||
|
status: () => Promise<StatusResponse>,
|
||||||
|
version: () => Promise<VersionResponse>,
|
||||||
|
resolve: (params: {}) => Promise<ResolveResponse>,
|
||||||
|
get: (params: {}) => Promise<GetResponse>,
|
||||||
|
publish: (params: {}) => Promise<PublishResponse>,
|
||||||
|
|
||||||
|
claim_search: (params: {}) => Promise<ClaimSearchResponse>,
|
||||||
|
claim_list: (params: {}) => Promise<ClaimListResponse>,
|
||||||
|
channel_create: (params: {}) => Promise<ChannelCreateResponse>,
|
||||||
|
channel_update: (params: {}) => Promise<ChannelUpdateResponse>,
|
||||||
|
channel_import: (params: {}) => Promise<string>,
|
||||||
|
channel_list: (params: {}) => Promise<ChannelListResponse>,
|
||||||
|
channel_sign: (params: {}) => Promise<ChannelSignResponse>,
|
||||||
|
stream_abandon: (params: {}) => Promise<GenericTxResponse>,
|
||||||
|
stream_list: (params: {}) => Promise<StreamListResponse>,
|
||||||
|
channel_abandon: (params: {}) => Promise<GenericTxResponse>,
|
||||||
|
support_create: (params: {}) => Promise<GenericTxResponse>,
|
||||||
|
support_list: (params: {}) => Promise<SupportListResponse>,
|
||||||
|
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
|
||||||
|
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
|
||||||
|
purchase_list: (params: PurchaseListOptions) => Promise<PurchaseListResponse>,
|
||||||
|
collection_resolve: (params: CollectionResolveOptions) => Promise<CollectionResolveResponse>,
|
||||||
|
collection_list: (params: CollectionListOptions) => Promise<CollectionListResponse>,
|
||||||
|
collection_create: (params: {}) => Promise<CollectionCreateResponse>,
|
||||||
|
collection_update: (params: {}) => Promise<CollectionCreateResponse>,
|
||||||
|
|
||||||
|
// File fetching and manipulation
|
||||||
|
file_list: (params: {}) => Promise<FileListResponse>,
|
||||||
|
file_delete: (params: {}) => Promise<boolean>,
|
||||||
|
blob_delete: (params: {}) => Promise<string>,
|
||||||
|
blob_list: (params: {}) => Promise<BlobListResponse>,
|
||||||
|
file_set_status: (params: {}) => Promise<any>,
|
||||||
|
file_reflect: (params: {}) => Promise<any>,
|
||||||
|
|
||||||
|
// Preferences
|
||||||
|
preference_get: (params?: {}) => Promise<any>,
|
||||||
|
preference_set: (params: {}) => Promise<any>,
|
||||||
|
|
||||||
|
// Commenting
|
||||||
|
comment_update: (params: {}) => Promise<CommentUpdateResponse>,
|
||||||
|
comment_hide: (params: {}) => Promise<CommentHideResponse>,
|
||||||
|
comment_abandon: (params: {}) => Promise<CommentAbandonResponse>,
|
||||||
|
comment_list: (params: {}) => Promise<any>,
|
||||||
|
comment_create: (params: {}) => Promise<any>,
|
||||||
|
|
||||||
|
// Wallet utilities
|
||||||
|
wallet_balance: (params: {}) => Promise<BalanceResponse>,
|
||||||
|
wallet_decrypt: (prams: {}) => Promise<boolean>,
|
||||||
|
wallet_encrypt: (params: {}) => Promise<boolean>,
|
||||||
|
wallet_unlock: (params: {}) => Promise<boolean>,
|
||||||
|
wallet_list: (params: {}) => Promise<WalletListResponse>,
|
||||||
|
wallet_send: (params: {}) => Promise<GenericTxResponse>,
|
||||||
|
wallet_status: (params?: {}) => Promise<WalletStatusResponse>,
|
||||||
|
address_is_mine: (params: {}) => Promise<boolean>,
|
||||||
|
address_unused: (params: {}) => Promise<string>, // New address
|
||||||
|
address_list: (params: {}) => Promise<string>,
|
||||||
|
transaction_list: (params: {}) => Promise<TxListResponse>,
|
||||||
|
txo_list: (params: {}) => Promise<any>,
|
||||||
|
account_set: (params: {}) => Promise<any>,
|
||||||
|
account_list: (params?: {}) => Promise<any>,
|
||||||
|
|
||||||
|
// Sync
|
||||||
|
sync_hash: (params?: {}) => Promise<string>,
|
||||||
|
sync_apply: (params: {}) => Promise<SyncApplyResponse>,
|
||||||
|
// syncGet
|
||||||
|
|
||||||
|
// The app shouldn't need to do this
|
||||||
|
utxo_release: () => Promise<any>,
|
||||||
|
};
|
99
flow-typed/LbryFirst.js
vendored
Normal file
99
flow-typed/LbryFirst.js
vendored
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// @flow
|
||||||
|
declare type LbryFirstStatusResponse = {
|
||||||
|
Version: string,
|
||||||
|
Message: string,
|
||||||
|
Running: boolean,
|
||||||
|
Commit: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type LbryFirstVersionResponse = {
|
||||||
|
build: string,
|
||||||
|
lbrynet_version: string,
|
||||||
|
os_release: string,
|
||||||
|
os_system: string,
|
||||||
|
platform: string,
|
||||||
|
processor: string,
|
||||||
|
python_version: string,
|
||||||
|
};
|
||||||
|
/* SAMPLE UPLOAD RESPONSE (FULL)
|
||||||
|
"Video": {
|
||||||
|
"etag": "\"Dn5xIderbhAnUk5TAW0qkFFir0M/xlGLrlTox7VFTRcR8F77RbKtaU4\"",
|
||||||
|
"id": "8InjtdvVmwE",
|
||||||
|
"kind": "youtube#video",
|
||||||
|
"snippet": {
|
||||||
|
"categoryId": "22",
|
||||||
|
"channelId": "UCXiVsGTU88fJjheB2rqF0rA",
|
||||||
|
"channelTitle": "Mark Beamer",
|
||||||
|
"liveBroadcastContent": "none",
|
||||||
|
"localized": {
|
||||||
|
"title": "my title"
|
||||||
|
},
|
||||||
|
"publishedAt": "2020-05-05T04:17:53.000Z",
|
||||||
|
"thumbnails": {
|
||||||
|
"default": {
|
||||||
|
"height": 90,
|
||||||
|
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/default.jpg?sqp=CMTQw_UF&rs=AOn4CLB6dlhZMSMrazDlWRsitPgCsn8fVw",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
"high": {
|
||||||
|
"height": 360,
|
||||||
|
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/hqdefault.jpg?sqp=CMTQw_UF&rs=AOn4CLB-Je_7l6qvASRAR_bSGWZHaXaJWQ",
|
||||||
|
"width": 480
|
||||||
|
},
|
||||||
|
"medium": {
|
||||||
|
"height": 180,
|
||||||
|
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/mqdefault.jpg?sqp=CMTQw_UF&rs=AOn4CLCvSnDLqVznRNMKuvJ_0misY_chPQ",
|
||||||
|
"width": 320
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "my title"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"embeddable": true,
|
||||||
|
"license": "youtube",
|
||||||
|
"privacyStatus": "private",
|
||||||
|
"publicStatsViewable": true,
|
||||||
|
"uploadStatus": "uploaded"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
declare type UploadResponse = {
|
||||||
|
Video: {
|
||||||
|
id: string,
|
||||||
|
snippet: {
|
||||||
|
channelId: string,
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
uploadStatus: string,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type HasYTAuthResponse = {
|
||||||
|
HashAuth: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type YTSignupResponse = {};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Types used in the generic LbryFirst object that is exported
|
||||||
|
//
|
||||||
|
declare type LbryFirstTypes = {
|
||||||
|
isConnected: boolean,
|
||||||
|
connectPromise: ?Promise<any>,
|
||||||
|
connect: () => void,
|
||||||
|
lbryFirstConnectionString: string,
|
||||||
|
apiRequestHeaders: { [key: string]: string },
|
||||||
|
setApiHeader: (string, string) => void,
|
||||||
|
unsetApiHeader: string => void,
|
||||||
|
overrides: { [string]: ?Function },
|
||||||
|
setOverride: (string, Function) => void,
|
||||||
|
|
||||||
|
// LbryFirst Methods
|
||||||
|
stop: () => Promise<string>,
|
||||||
|
status: () => Promise<StatusResponse>,
|
||||||
|
version: () => Promise<VersionResponse>,
|
||||||
|
upload: any => Promise<?UploadResponse>,
|
||||||
|
hasYTAuth: string => Promise<HasYTAuthResponse>,
|
||||||
|
ytSignup: () => Promise<YTSignupResponse>,
|
||||||
|
};
|
136
flow-typed/Notification.js
vendored
Normal file
136
flow-typed/Notification.js
vendored
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
// @flow
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Toasts:
|
||||||
|
- First-in, first-out queue
|
||||||
|
- Simple messages that are shown in response to user interactions
|
||||||
|
- Never saved
|
||||||
|
- If they are the result of errors, use the isError flag when creating
|
||||||
|
- For errors that should interrupt user behavior, use Error
|
||||||
|
*/
|
||||||
|
declare type ToastParams = {
|
||||||
|
message: string,
|
||||||
|
title?: string,
|
||||||
|
linkText?: string,
|
||||||
|
linkTarget?: string,
|
||||||
|
isError?: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Toast = {
|
||||||
|
id: string,
|
||||||
|
params: ToastParams,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoToast = {
|
||||||
|
type: ACTIONS.CREATE_TOAST,
|
||||||
|
data: Toast,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Notifications:
|
||||||
|
- List of notifications based on user interactions/app notifications
|
||||||
|
- Always saved, but can be manually deleted
|
||||||
|
- Can happen in the background, or because of user interaction (ex: publish confirmed)
|
||||||
|
*/
|
||||||
|
declare type Notification = {
|
||||||
|
id: string, // Unique id
|
||||||
|
dateCreated: number,
|
||||||
|
isRead: boolean, // Used to display "new" notifications that a user hasn't seen yet
|
||||||
|
source?: string, // The type/area an notification is from. Used for sorting (ex: publishes, transactions)
|
||||||
|
// We may want to use priority/isDismissed in the future to specify how urgent a notification is
|
||||||
|
// and if the user should see it immediately
|
||||||
|
// isDissmied: boolean,
|
||||||
|
// priority?: number
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoNotification = {
|
||||||
|
type: ACTIONS.CREATE_NOTIFICATION,
|
||||||
|
data: Notification,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoEditNotification = {
|
||||||
|
type: ACTIONS.EDIT_NOTIFICATION,
|
||||||
|
data: {
|
||||||
|
notification: Notification,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoDeleteNotification = {
|
||||||
|
type: ACTIONS.DELETE_NOTIFICATION,
|
||||||
|
data: {
|
||||||
|
id: string, // The id to delete
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Errors:
|
||||||
|
- First-in, first-out queue
|
||||||
|
- Errors that should interupt user behavior
|
||||||
|
- For errors that can be shown without interrupting a user, use Toast with the isError flag
|
||||||
|
*/
|
||||||
|
declare type ErrorNotification = {
|
||||||
|
title: string,
|
||||||
|
text: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoError = {
|
||||||
|
type: ACTIONS.CREATE_ERROR,
|
||||||
|
data: ErrorNotification,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DoDismissError = {
|
||||||
|
type: ACTIONS.DISMISS_ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
NotificationState
|
||||||
|
*/
|
||||||
|
declare type NotificationState = {
|
||||||
|
notifications: Array<Notification>,
|
||||||
|
errors: Array<ErrorNotification>,
|
||||||
|
toasts: Array<Toast>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type WebNotification = {
|
||||||
|
active_at: string,
|
||||||
|
created_at: string,
|
||||||
|
id: number,
|
||||||
|
is_app_readable: boolean,
|
||||||
|
is_device_notified: boolean,
|
||||||
|
is_emailed: boolean,
|
||||||
|
is_read: boolean,
|
||||||
|
is_seen: boolean,
|
||||||
|
notification_parameters: {
|
||||||
|
device: {
|
||||||
|
analytics_label: string,
|
||||||
|
image_url: string,
|
||||||
|
is_data_only: boolean,
|
||||||
|
name: string,
|
||||||
|
placeholders: ?string,
|
||||||
|
target: string,
|
||||||
|
text: string,
|
||||||
|
title: string,
|
||||||
|
type: string,
|
||||||
|
},
|
||||||
|
dynamic: {
|
||||||
|
comment_author: string,
|
||||||
|
reply_author: string,
|
||||||
|
hash: string,
|
||||||
|
claim_title: string,
|
||||||
|
comment?: string,
|
||||||
|
channel_url: string,
|
||||||
|
},
|
||||||
|
email: {},
|
||||||
|
},
|
||||||
|
notification_rule: string,
|
||||||
|
type: string,
|
||||||
|
updated_at: string,
|
||||||
|
user_id: number,
|
||||||
|
group_count?: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type NotificationCategory = {
|
||||||
|
name: string,
|
||||||
|
types: ?Array<string>,
|
||||||
|
};
|
|
@ -25,7 +25,7 @@ declare type UpdatePublishFormData = {
|
||||||
licenseType?: string,
|
licenseType?: string,
|
||||||
uri?: string,
|
uri?: string,
|
||||||
nsfw: boolean,
|
nsfw: boolean,
|
||||||
isMarkdownPost: boolean,
|
isMarkdownPost?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type PublishParams = {
|
declare type PublishParams = {
|
6
flow-typed/Redux.js
vendored
Normal file
6
flow-typed/Redux.js
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// @flow
|
||||||
|
/* eslint-disable no-use-before-define */
|
||||||
|
declare type GetState = () => any;
|
||||||
|
declare type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
|
||||||
|
declare type Dispatch = (action: {} | Promise<*> | Array<{}> | ThunkAction) => any; // Need to refer to ThunkAction
|
||||||
|
/* eslint-enable */
|
5
flow-typed/Reflector.js
vendored
Normal file
5
flow-typed/Reflector.js
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
declare type ReflectingUpdate = {
|
||||||
|
fileListItem: FileListItem,
|
||||||
|
progress: number | boolean,
|
||||||
|
stalled: boolean,
|
||||||
|
};
|
13
flow-typed/Settings.js
vendored
Normal file
13
flow-typed/Settings.js
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
declare type CommentServerDetails = {
|
||||||
|
name: string,
|
||||||
|
url: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type WalletServerDetails = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DiskSpace = {
|
||||||
|
total: number,
|
||||||
|
free: number,
|
||||||
|
};
|
21
flow-typed/Tags.js
vendored
Normal file
21
flow-typed/Tags.js
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
declare type TagState = {
|
||||||
|
followedTags: FollowedTags,
|
||||||
|
knownTags: KnownTags,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Tag = {
|
||||||
|
name: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type KnownTags = {
|
||||||
|
[string]: Tag,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type FollowedTags = Array<string>;
|
||||||
|
|
||||||
|
declare type TagAction = {
|
||||||
|
type: string,
|
||||||
|
data: {
|
||||||
|
name: string,
|
||||||
|
},
|
||||||
|
};
|
28
flow-typed/Transaction.js
vendored
Normal file
28
flow-typed/Transaction.js
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// @flow
|
||||||
|
declare type Transaction = {
|
||||||
|
amount: number,
|
||||||
|
claim_id: string,
|
||||||
|
claim_name: string,
|
||||||
|
fee: number,
|
||||||
|
nout: number,
|
||||||
|
txid: string,
|
||||||
|
type: string,
|
||||||
|
date: Date,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Support = {
|
||||||
|
address: string,
|
||||||
|
amount: string,
|
||||||
|
claim_id: string,
|
||||||
|
confirmations: number,
|
||||||
|
height: string,
|
||||||
|
is_change: string,
|
||||||
|
is_mine: string,
|
||||||
|
name: string,
|
||||||
|
normalized_name: string,
|
||||||
|
nout: string,
|
||||||
|
permanent_url: string,
|
||||||
|
timestamp: number,
|
||||||
|
txid: string,
|
||||||
|
type: string,
|
||||||
|
};
|
27
flow-typed/Txo.js
vendored
Normal file
27
flow-typed/Txo.js
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
declare type Txo = {
|
||||||
|
amount: number,
|
||||||
|
claim_id: string,
|
||||||
|
normalized_name: string,
|
||||||
|
nout: number,
|
||||||
|
txid: string,
|
||||||
|
type: string,
|
||||||
|
value_type: string,
|
||||||
|
timestamp: number,
|
||||||
|
is_my_output: boolean,
|
||||||
|
is_my_input: boolean,
|
||||||
|
is_spent: boolean,
|
||||||
|
signing_channel?: {
|
||||||
|
channel_id: string,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type TxoListParams = {
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
type: string,
|
||||||
|
is_my_input?: boolean,
|
||||||
|
is_my_output?: boolean,
|
||||||
|
is_not_my_input?: boolean,
|
||||||
|
is_not_my_output?: boolean,
|
||||||
|
is_spent?: boolean,
|
||||||
|
};
|
10
flow-typed/file-data.js
vendored
Normal file
10
flow-typed/file-data.js
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
declare type FileData = {
|
||||||
|
file?: Blob,
|
||||||
|
path: string,
|
||||||
|
duration?: number,
|
||||||
|
size?: number,
|
||||||
|
mimeType: string,
|
||||||
|
error?: string,
|
||||||
|
}
|
9
flow-typed/file-with-path.js
vendored
Normal file
9
flow-typed/file-with-path.js
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
declare type FileWithPath = {
|
||||||
|
file: File,
|
||||||
|
// The full path will only be available in
|
||||||
|
// the application. For browser, the name
|
||||||
|
// of the file will be used.
|
||||||
|
path: string,
|
||||||
|
}
|
1
flow-typed/homepage.js
vendored
1
flow-typed/homepage.js
vendored
|
@ -22,6 +22,7 @@ declare type RowDataItem = {
|
||||||
channelIds?: Array<string>,
|
channelIds?: Array<string>,
|
||||||
limitClaimsPerChannel?: number,
|
limitClaimsPerChannel?: number,
|
||||||
pageSize?: number,
|
pageSize?: number,
|
||||||
|
languages?: Array<string>,
|
||||||
},
|
},
|
||||||
route?: string,
|
route?: string,
|
||||||
hideForUnauth?: boolean,
|
hideForUnauth?: boolean,
|
||||||
|
|
2
flow-typed/i18n.js
vendored
Normal file
2
flow-typed/i18n.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// @flow
|
||||||
|
declare function __(a: string, b?: {}): string;
|
21
flow-typed/lbryURI.js
vendored
Normal file
21
flow-typed/lbryURI.js
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// @flow
|
||||||
|
declare type LbryUrlObj = {
|
||||||
|
// Path and channel will always exist when calling parseURI
|
||||||
|
// But they may not exist when code calls buildURI
|
||||||
|
isChannel?: boolean,
|
||||||
|
path?: string,
|
||||||
|
streamName?: string,
|
||||||
|
streamClaimId?: string,
|
||||||
|
channelName?: string,
|
||||||
|
channelClaimId?: string,
|
||||||
|
primaryClaimSequence?: number,
|
||||||
|
secondaryClaimSequence?: number,
|
||||||
|
primaryBidPosition?: number,
|
||||||
|
secondaryBidPosition?: number,
|
||||||
|
startTime?: number,
|
||||||
|
|
||||||
|
// Below are considered deprecated and should not be used due to unreliableness with claim.canonical_url
|
||||||
|
claimName?: string,
|
||||||
|
claimId?: string,
|
||||||
|
contentName?: string,
|
||||||
|
};
|
14
flow-typed/livestream.js
vendored
14
flow-typed/livestream.js
vendored
|
@ -24,4 +24,18 @@ declare type LivestreamReplayData = Array<LivestreamReplayItem>;
|
||||||
declare type LivestreamState = {
|
declare type LivestreamState = {
|
||||||
fetchingById: {},
|
fetchingById: {},
|
||||||
viewersById: {},
|
viewersById: {},
|
||||||
|
fetchingActiveLivestreams: boolean,
|
||||||
|
activeLivestreams: ?LivestreamInfo,
|
||||||
|
activeLivestreamsLastFetchedDate: number,
|
||||||
|
activeLivestreamsLastFetchedOptions: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type LivestreamInfo = {
|
||||||
|
[/* creatorId */ string]: {
|
||||||
|
live: boolean,
|
||||||
|
viewCount: number,
|
||||||
|
creatorId: string,
|
||||||
|
latestClaimId: string,
|
||||||
|
latestClaimUri: string,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
43
flow-typed/notification.js
vendored
43
flow-typed/notification.js
vendored
|
@ -1,43 +0,0 @@
|
||||||
// @flow
|
|
||||||
declare type WebNotification = {
|
|
||||||
active_at: string,
|
|
||||||
created_at: string,
|
|
||||||
id: number,
|
|
||||||
is_app_readable: boolean,
|
|
||||||
is_device_notified: boolean,
|
|
||||||
is_emailed: boolean,
|
|
||||||
is_read: boolean,
|
|
||||||
is_seen: boolean,
|
|
||||||
notification_parameters: {
|
|
||||||
device: {
|
|
||||||
analytics_label: string,
|
|
||||||
image_url: string,
|
|
||||||
is_data_only: boolean,
|
|
||||||
name: string,
|
|
||||||
placeholders: ?string,
|
|
||||||
target: string,
|
|
||||||
text: string,
|
|
||||||
title: string,
|
|
||||||
type: string,
|
|
||||||
},
|
|
||||||
dynamic: {
|
|
||||||
comment_author: string,
|
|
||||||
reply_author: string,
|
|
||||||
hash: string,
|
|
||||||
claim_title: string,
|
|
||||||
comment?: string,
|
|
||||||
channel_url: string,
|
|
||||||
},
|
|
||||||
email: {},
|
|
||||||
},
|
|
||||||
notification_rule: string,
|
|
||||||
type: string,
|
|
||||||
updated_at: string,
|
|
||||||
user_id: number,
|
|
||||||
group_count?: number,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type NotificationCategory = {
|
|
||||||
name: string,
|
|
||||||
types: ?Array<string>,
|
|
||||||
};
|
|
3
flow-typed/redux.js
vendored
3
flow-typed/redux.js
vendored
|
@ -1,3 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
declare type Dispatch = any;
|
|
3
flow-typed/search.js
vendored
3
flow-typed/search.js
vendored
|
@ -29,8 +29,10 @@ declare type SearchOptions = {
|
||||||
declare type SearchState = {
|
declare type SearchState = {
|
||||||
options: SearchOptions,
|
options: SearchOptions,
|
||||||
resultsByQuery: {},
|
resultsByQuery: {},
|
||||||
|
results: Array<string>,
|
||||||
hasReachedMaxResultsLength: {},
|
hasReachedMaxResultsLength: {},
|
||||||
searching: boolean,
|
searching: boolean,
|
||||||
|
mentionQuery: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type SearchSuccess = {
|
declare type SearchSuccess = {
|
||||||
|
@ -41,6 +43,7 @@ declare type SearchSuccess = {
|
||||||
size: number,
|
size: number,
|
||||||
uris: Array<string>,
|
uris: Array<string>,
|
||||||
recsys: string,
|
recsys: string,
|
||||||
|
query: string,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
6
flow-typed/web-file.js
vendored
6
flow-typed/web-file.js
vendored
|
@ -1,6 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
declare type WebFile = File & {
|
|
||||||
path?: string,
|
|
||||||
title?: string,
|
|
||||||
}
|
|
116
package.json
116
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "lbry",
|
"name": "lbry",
|
||||||
"version": "0.51.2",
|
"version": "0.53.9",
|
||||||
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
|
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"lbry"
|
"lbry"
|
||||||
|
@ -22,75 +22,69 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "source-map-explorer --only-mapped dist/electron/webpack/ui*.js --html dist/sourceMap.html",
|
"analyze": "source-map-explorer --only-mapped dist/electron/webpack/ui*.js --html dist/sourceMap.html",
|
||||||
"compile:electron": "node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --config webpack.electron.config.js",
|
"compile:electron": "node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --config webpack.electron.config.js",
|
||||||
"compile:web": "yarn copyenv && cd web && node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --config webpack.config.js",
|
"compile": "cross-env NODE_ENV=production yarn compile:electron",
|
||||||
"compile": "cross-env NODE_ENV=production yarn compile:electron && cross-env NODE_ENV=production yarn compile:web",
|
|
||||||
"copyenv": "copyfiles ./.env* web/",
|
|
||||||
"dev": "yarn dev:electron",
|
"dev": "yarn dev:electron",
|
||||||
"dev:electron": "cross-env NODE_ENV=development node ./electron/devServer.js",
|
"dev:electron": "cross-env NODE_ENV=development node ./electron/devServer.js",
|
||||||
"dev:web": "yarn copyenv && cd web && yarn dev",
|
|
||||||
"dev:web-server": "cross-env NODE_ENV=development yarn compile:web && concurrently \"cross-env NODE_ENV=development yarn compile:web --watch\" \"cd web && yarn dev:server\"",
|
|
||||||
"dev:internal-apis": "LBRY_API_URL='http://localhost:8080' yarn dev:electron",
|
|
||||||
"dev:iatv": "LBRY_API_URL='http://localhost:15400' SDK_API_URL='http://localhost:15100' yarn dev:web",
|
|
||||||
"run:web-server": "cross-env NODE_ENV=production yarn compile:web && cd web && yarn dev:server",
|
|
||||||
"pack": "electron-builder --dir",
|
"pack": "electron-builder --dir",
|
||||||
"dist": "electron-builder",
|
"dist": "electron-builder",
|
||||||
"build": "cross-env NODE_ENV=production yarn compile:electron && electron-builder build",
|
"build": "cross-env NODE_ENV=production yarn compile:electron && electron-builder build",
|
||||||
"build:dir": "yarn build -- --dir -c.compression=store -c.mac.identity=null",
|
"build:dir": "yarn build -- --dir -c.compression=store -c.mac.identity=null",
|
||||||
"crossenv": "./node_modules/cross-env/dist/bin/cross-env",
|
"crossenv": "cross-env",
|
||||||
"lint": "eslint 'ui/**/*.{js,jsx}' && eslint 'web/**/*.{js,jsx}' && eslint 'electron/**/*.js' && flow",
|
"flow": "flow",
|
||||||
"lint-fix": "eslint --fix --quiet 'ui/**/*.{js,jsx}' && eslint --fix --quiet 'web/**/*.{js,jsx}' && eslint --fix --quiet 'electron/**/*.js'",
|
"lint": "eslint 'ui/**/*.{js,jsx}' && eslint 'electron/**/*.js' && flow",
|
||||||
|
"lint-fix": "eslint --fix --quiet 'ui/**/*.{js,jsx}' && eslint --fix --quiet 'electron/**/*.js'",
|
||||||
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write",
|
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write",
|
||||||
"flow-defs": "flow-typed install",
|
"flow-defs": "flow-typed install",
|
||||||
"precommit": "lint-staged",
|
"precommit": "lint-staged",
|
||||||
"preinstall": "yarn cache clean lbry-redux && yarn cache clean lbryinc",
|
"postinstall": "electron-builder install-app-deps && node ./build/downloadDaemon.js",
|
||||||
"postinstall": "cd web && yarn && cd .. && if-env NODE_ENV=production && yarn postinstall:warning || if-env APP_ENV=web && echo 'Done installing deps' || yarn postinstall:electron",
|
|
||||||
"postinstall:electron": "electron-builder install-app-deps && node ./build/downloadDaemon.js && node ./build/downloadLBRYFirst.js",
|
|
||||||
"postinstall:warning": "echo '\n\nWARNING\n\nNot all node modules were installed because NODE_ENV is set to \"production\".\nThis should only be set after installing dependencies with \"yarn\". The app will not work.\n\n'"
|
"postinstall:warning": "echo '\n\nWARNING\n\nNot all node modules were installed because NODE_ENV is set to \"production\".\nThis should only be set after installing dependencies with \"yarn\". The app will not work.\n\n'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-keycloak/web": "^3.4.0",
|
"@electron/remote": "^2.0.1",
|
||||||
|
"@emotion/react": "^11.10.4",
|
||||||
|
"@emotion/styled": "^11.10.4",
|
||||||
|
"@mui/material": "^5.2.1",
|
||||||
|
"@ungap/from-entries": "^0.2.1",
|
||||||
"auto-launch": "^5.0.5",
|
"auto-launch": "^5.0.5",
|
||||||
"electron-dl": "^1.11.0",
|
"electron-dl": "^3.2.0",
|
||||||
"electron-log": "^2.2.12",
|
"electron-log": "^4.4.8",
|
||||||
"electron-notarize": "^1.0.0",
|
"electron-notarize": "^1.0.0",
|
||||||
"electron-updater": "^4.2.4",
|
"electron-updater": "^4.2.4",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
|
"ffmpeg-probe": "^1.0.6",
|
||||||
"humanize-duration": "^3.27.0",
|
"humanize-duration": "^3.27.0",
|
||||||
"if-env": "^1.0.4",
|
|
||||||
"match-sorter": "^6.3.0",
|
"match-sorter": "^6.3.0",
|
||||||
|
"mime": "^3.0.0",
|
||||||
|
"node-html-parser": "^5.1.0",
|
||||||
"parse-duration": "^1.0.0",
|
"parse-duration": "^1.0.0",
|
||||||
"keycloak-js": "^15.0.2",
|
"proxy-polyfill": "0.1.6",
|
||||||
"react-datetime-picker": "^3.2.1",
|
"re-reselect": "^4.0.0",
|
||||||
"react-plastic": "^1.1.1",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-top-loading-bar": "^2.0.1",
|
"react-datetime-picker": "^3.4.3",
|
||||||
"remove-markdown": "^0.3.0",
|
|
||||||
"rss": "^1.2.2",
|
|
||||||
"source-map-explorer": "^2.5.2",
|
"source-map-explorer": "^2.5.2",
|
||||||
"tempy": "^0.6.0",
|
"sudo-prompt": "^9.2.1",
|
||||||
"videojs-contrib-ads": "^6.9.0",
|
"tempy": "^0.6.0"
|
||||||
"videojs-ima": "^1.11.0",
|
|
||||||
"videojs-logo": "^2.1.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.0.0",
|
"@babel/core": "^7.0.0",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||||
"@babel/plugin-proposal-decorators": "^7.3.0",
|
"@babel/plugin-proposal-decorators": "^7.3.0",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.6.2",
|
"@babel/plugin-proposal-object-rest-spread": "^7.6.2",
|
||||||
|
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
"@babel/plugin-transform-flow-strip-types": "^7.2.3",
|
"@babel/plugin-transform-flow-strip-types": "^7.2.3",
|
||||||
"@babel/plugin-transform-runtime": "^7.4.3",
|
"@babel/plugin-transform-runtime": "^7.4.3",
|
||||||
"@babel/polyfill": "^7.2.5",
|
"@babel/polyfill": "^7.12.1",
|
||||||
"@babel/preset-env": "^7.12.11",
|
"@babel/preset-env": "^7.12.11",
|
||||||
"@babel/preset-flow": "^7.12.1",
|
"@babel/preset-flow": "^7.12.1",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"@babel/register": "^7.0.0",
|
"@babel/register": "^7.0.0",
|
||||||
"@datapunt/matomo-tracker-js": "^0.1.4",
|
"@datapunt/matomo-tracker-js": "^0.1.4",
|
||||||
"@exponent/electron-cookies": "^2.0.0",
|
|
||||||
"@hot-loader/react-dom": "^16.13",
|
"@hot-loader/react-dom": "^16.13",
|
||||||
"@reach/auto-id": "^0.13.0",
|
"@meetfranz/electron-cookies": "^3.0.2",
|
||||||
"@reach/combobox": "^0.12.1",
|
"@reach/combobox": "^0.12.1",
|
||||||
"@reach/menu-button": "0.7.4",
|
"@reach/menu-button": "0.8.6",
|
||||||
"@reach/rect": "^0.13.0",
|
"@reach/rect": "^0.16.0",
|
||||||
"@reach/tabs": "^0.1.5",
|
"@reach/tabs": "^0.1.5",
|
||||||
"@reach/tooltip": "^0.12.1",
|
"@reach/tooltip": "^0.12.1",
|
||||||
"@reach/utils": "^0.12.1",
|
"@reach/utils": "^0.12.1",
|
||||||
|
@ -98,21 +92,17 @@
|
||||||
"@sentry/webpack-plugin": "^1.10.0",
|
"@sentry/webpack-plugin": "^1.10.0",
|
||||||
"@types/three": "^0.103.2",
|
"@types/three": "^0.103.2",
|
||||||
"adm-zip": "^0.4.13",
|
"adm-zip": "^0.4.13",
|
||||||
"async-exit-hook": "^2.0.1",
|
|
||||||
"babel-eslint": "^10.0.1",
|
"babel-eslint": "^10.0.1",
|
||||||
"babel-loader": "^8.0.5",
|
"babel-loader": "^8.0.5",
|
||||||
"babel-plugin-add-module-exports": "^1.0.4",
|
"babel-plugin-add-module-exports": "^1.0.4",
|
||||||
"babel-plugin-import-glob": "^2.0.0",
|
"babel-plugin-import-glob": "^2.0.0",
|
||||||
"babel-plugin-transform-imports": "^1.5.1",
|
"babel-plugin-transform-imports": "^1.5.1",
|
||||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||||
"bluebird": "^3.5.1",
|
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"codemirror": "^5.39.2",
|
"codemirror": "^5.39.2",
|
||||||
"concurrently": "^4.1.2",
|
|
||||||
"connected-react-router": "^6.8.0",
|
"connected-react-router": "^6.8.0",
|
||||||
"copy-webpack-plugin": "^5.1.2",
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
"copyfiles": "^2.4.1",
|
|
||||||
"country-data": "^0.0.31",
|
"country-data": "^0.0.31",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
|
@ -123,11 +113,10 @@
|
||||||
"decompress": "^4.2.1",
|
"decompress": "^4.2.1",
|
||||||
"del": "^3.0.0",
|
"del": "^3.0.0",
|
||||||
"devtron": "^1.4.0",
|
"devtron": "^1.4.0",
|
||||||
"dom-scroll-into-view": "^1.2.1",
|
|
||||||
"dotenv-defaults": "^2.0.1",
|
"dotenv-defaults": "^2.0.1",
|
||||||
"dotenv-webpack": "^1.8.0",
|
"dotenv-webpack": "^1.8.0",
|
||||||
"electron": "9.4.0",
|
"electron": "17.2.0",
|
||||||
"electron-builder": "^22.9.1",
|
"electron-builder": "^22.10.5",
|
||||||
"electron-devtools-installer": "^3.1.1",
|
"electron-devtools-installer": "^3.1.1",
|
||||||
"electron-is-dev": "^0.3.0",
|
"electron-is-dev": "^0.3.0",
|
||||||
"electron-webpack": "^2.8.2",
|
"electron-webpack": "^2.8.2",
|
||||||
|
@ -149,24 +138,19 @@
|
||||||
"eslint-plugin-standard": "^4.0.1",
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
"file-loader": "^4.2.0",
|
"file-loader": "^4.2.0",
|
||||||
"flow-bin": "^0.97.0",
|
"flow-bin": "^0.97.0",
|
||||||
"flow-typed": "^2.3.0",
|
"flow-typed": "^3.7.0",
|
||||||
"formik": "^0.10.4",
|
"formik": "^0.10.4",
|
||||||
"hast-util-sanitize": "^3.0.2",
|
"hast-util-sanitize": "^3.0.2",
|
||||||
"history": "^4.9.0",
|
"history": "^4.9.0",
|
||||||
"husky": "^3.1.0",
|
"husky": "^3.1.0",
|
||||||
"imagesloaded": "^4.1.4",
|
"imagesloaded": "^4.1.4",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
|
||||||
"lbry-redux": "lbryio/lbry-redux#32b578707116d45f5b51b7ab523d200e75668676",
|
|
||||||
"lbryinc": "lbryio/lbryinc#97fc74315c89580f9fa2408643b0f4feef42750a",
|
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"lodash-es": "^4.17.14",
|
"lodash-es": "^4.17.21",
|
||||||
"mammoth": "^1.4.16",
|
"mammoth": "^1.4.16",
|
||||||
"moment": "^2.22.0",
|
"moment": "^2.29.2",
|
||||||
"node-abi": "^2.5.1",
|
"node-fetch": "^2.6.7",
|
||||||
"node-fetch": "^2.6.1",
|
|
||||||
"node-libs-browser": "^2.1.0",
|
|
||||||
"node-loader": "^0.6.0",
|
"node-loader": "^0.6.0",
|
||||||
"node-wget": "^0.4.3",
|
"node-wget": "^0.4.3",
|
||||||
"nodemon": "^1.19.1",
|
"nodemon": "^1.19.1",
|
||||||
|
@ -181,7 +165,6 @@
|
||||||
"rc-progress": "^2.0.6",
|
"rc-progress": "^2.0.6",
|
||||||
"react": "^16.8.2",
|
"react": "^16.8.2",
|
||||||
"react-awesome-lightbox": "^1.7.3",
|
"react-awesome-lightbox": "^1.7.3",
|
||||||
"react-confetti": "^4.0.1",
|
|
||||||
"react-dom": "^16.8.2",
|
"react-dom": "^16.8.2",
|
||||||
"react-draggable": "^3.3.0",
|
"react-draggable": "^3.3.0",
|
||||||
"react-google-recaptcha": "^2.0.1",
|
"react-google-recaptcha": "^2.0.1",
|
||||||
|
@ -192,7 +175,6 @@
|
||||||
"react-router": "^5.1.0",
|
"react-router": "^5.1.0",
|
||||||
"react-router-dom": "^5.1.0",
|
"react-router-dom": "^5.1.0",
|
||||||
"react-simplemde-editor": "^4.1.3",
|
"react-simplemde-editor": "^4.1.3",
|
||||||
"react-spring": "^8.0.20",
|
|
||||||
"reakit": "^1.0.0-beta.13",
|
"reakit": "^1.0.0-beta.13",
|
||||||
"redux": "^3.6.0",
|
"redux": "^3.6.0",
|
||||||
"redux-persist": "^5.10.0",
|
"redux-persist": "^5.10.0",
|
||||||
|
@ -209,23 +191,17 @@
|
||||||
"sass": "^1.29.0",
|
"sass": "^1.29.0",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
"stream-to-blob-url": "^2.1.1",
|
|
||||||
"strip-markdown": "^3.0.3",
|
"strip-markdown": "^3.0.3",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"terser-webpack-plugin": "^1.2.3",
|
"terser-webpack-plugin": "^4.2.3",
|
||||||
"three": "^0.125.0",
|
"three-full": "^28.0.2",
|
||||||
"three-full": "^17.1.0",
|
|
||||||
"tiny-relative-date": "^1.3.0",
|
|
||||||
"tree-kill": "^1.1.0",
|
|
||||||
"unist-util-visit": "^2.0.3",
|
"unist-util-visit": "^2.0.3",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"vast-client": "^3.1.1",
|
"video.js": "^7.14.3",
|
||||||
"video.js": "^7.13.3",
|
|
||||||
"videojs-contrib-quality-levels": "^2.0.9",
|
"videojs-contrib-quality-levels": "^2.0.9",
|
||||||
"videojs-event-tracking": "^1.0.1",
|
"videojs-event-tracking": "^1.0.1",
|
||||||
"villain-react": "^1.0.9",
|
"villain-react": "^1.0.9",
|
||||||
"wavesurfer.js": "^2.2.1",
|
"webpack": "^4.44.2",
|
||||||
"webpack": "^4.28.4",
|
|
||||||
"webpack-bundle-analyzer": "^3.1.0",
|
"webpack-bundle-analyzer": "^3.1.0",
|
||||||
"webpack-cli": "^3.3.10",
|
"webpack-cli": "^3.3.10",
|
||||||
"webpack-config-utils": "^2.3.1",
|
"webpack-config-utils": "^2.3.1",
|
||||||
|
@ -234,21 +210,17 @@
|
||||||
"webpack-hot-middleware": "^2.24.3",
|
"webpack-hot-middleware": "^2.24.3",
|
||||||
"webpack-merge": "^4.2.1",
|
"webpack-merge": "^4.2.1",
|
||||||
"webpack-node-externals": "^1.7.2",
|
"webpack-node-externals": "^1.7.2",
|
||||||
"y18n": "^4.0.1",
|
|
||||||
"yarnhook": "^0.2.0"
|
"yarnhook": "^0.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=7",
|
"node": ">=16.13",
|
||||||
"yarn": "^1.3"
|
"yarn": "^1.3"
|
||||||
},
|
},
|
||||||
"lbrySettings": {
|
"lbrySettings": {
|
||||||
"lbrynetDaemonVersion": "0.99.0",
|
"lbrynetDaemonVersion": "0.113.0",
|
||||||
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-OSNAME.zip",
|
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-OSNAME.zip",
|
||||||
"lbrynetDaemonDir": "static/daemon",
|
"lbrynetDaemonDir": "static/daemon",
|
||||||
"lbrynetDaemonFileName": "lbrynet",
|
"lbrynetDaemonFileName": "lbrynet"
|
||||||
"LBRYFirstVersion": "0.0.20",
|
},
|
||||||
"LBRYFirstUrlTemplate": "https://github.com/lbryio/lbry-first/releases/download/vLBRYFIRSTVER/lbry-first_OSNAME_amd64.zip",
|
"packageManager": "yarn@3.2.0"
|
||||||
"LBRYFirstDir": "static/lbry-first",
|
|
||||||
"LBRYFirstFileName": "lbry-first"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,6 @@
|
||||||
"Description of your content": "Description of your content",
|
"Description of your content": "Description of your content",
|
||||||
"Price": "Price",
|
"Price": "Price",
|
||||||
"Free": "Free",
|
"Free": "Free",
|
||||||
"Learn more": "Learn more",
|
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Deposit": "Deposit",
|
"Deposit": "Deposit",
|
||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
|
@ -103,7 +102,6 @@
|
||||||
"Enter a URL for your thumbnail.": "Enter a URL for your thumbnail.",
|
"Enter a URL for your thumbnail.": "Enter a URL for your thumbnail.",
|
||||||
"Thumbnail Preview": "Thumbnail Preview",
|
"Thumbnail Preview": "Thumbnail Preview",
|
||||||
"Use thumbnail upload tool": "Use thumbnail upload tool",
|
"Use thumbnail upload tool": "Use thumbnail upload tool",
|
||||||
"Thumbnails that entice a viewer to watch a video work best": "Thumbnails that entice a viewer to watch a video work best",
|
|
||||||
"Create a URL for this content. Simpler names are easier to find and remember.": "Create a URL for this content. Simpler names are easier to find and remember.",
|
"Create a URL for this content. Simpler names are easier to find and remember.": "Create a URL for this content. Simpler names are easier to find and remember.",
|
||||||
"Open file": "Open file",
|
"Open file": "Open file",
|
||||||
"Delete": "Delete",
|
"Delete": "Delete",
|
||||||
|
@ -175,7 +173,6 @@
|
||||||
"Continue": "Continue",
|
"Continue": "Continue",
|
||||||
"This file has been shared with you by other people.": "This file has been shared with you by other people.",
|
"This file has been shared with you by other people.": "This file has been shared with you by other people.",
|
||||||
"LBRY Inc is not responsible for its content, click continue to proceed at your own risk.": "LBRY Inc is not responsible for its content, click continue to proceed at your own risk.",
|
"LBRY Inc is not responsible for its content, click continue to proceed at your own risk.": "LBRY Inc is not responsible for its content, click continue to proceed at your own risk.",
|
||||||
"Find what you were looking for?": "Find what you were looking for?",
|
|
||||||
"Yes": "Yes",
|
"Yes": "Yes",
|
||||||
"No": "No",
|
"No": "No",
|
||||||
"These search results are provided by LBRY, Inc.": "These search results are provided by LBRY, Inc.",
|
"These search results are provided by LBRY, Inc.": "These search results are provided by LBRY, Inc.",
|
||||||
|
@ -207,8 +204,14 @@
|
||||||
"View": "View",
|
"View": "View",
|
||||||
"Edit": "Edit",
|
"Edit": "Edit",
|
||||||
"Copied": "Copied",
|
"Copied": "Copied",
|
||||||
|
"Copied stream key.": "Copied stream key.",
|
||||||
|
"Copied stream server URL.": "Copied stream server URL.",
|
||||||
|
"Failed to copy.": "Failed to copy.",
|
||||||
"The publisher has chosen to charge %lbc% to view this content. Your balance is currently too low to view it. Check out %reward_link% for free %lbc% or send more %lbc% to your wallet. You can also %buy_link% more %lbc%.": "The publisher has chosen to charge %lbc% to view this content. Your balance is currently too low to view it. Check out %reward_link% for free %lbc% or send more %lbc% to your wallet. You can also %buy_link% more %lbc%.",
|
"The publisher has chosen to charge %lbc% to view this content. Your balance is currently too low to view it. Check out %reward_link% for free %lbc% or send more %lbc% to your wallet. You can also %buy_link% more %lbc%.": "The publisher has chosen to charge %lbc% to view this content. Your balance is currently too low to view it. Check out %reward_link% for free %lbc% or send more %lbc% to your wallet. You can also %buy_link% more %lbc%.",
|
||||||
"Connecting...": "Connecting...",
|
"Connecting...": "Connecting...",
|
||||||
|
"Your comments": "Your comments",
|
||||||
|
"View your past comments.": "View your past comments.",
|
||||||
|
"Content or channel was deleted.": "Content or channel was deleted.",
|
||||||
"Comments": "Comments",
|
"Comments": "Comments",
|
||||||
"Comment": "Comment",
|
"Comment": "Comment",
|
||||||
"Comment --[button to submit something]--": "Comment",
|
"Comment --[button to submit something]--": "Comment",
|
||||||
|
@ -314,6 +317,8 @@
|
||||||
"Confirm Thumbnail Upload": "Confirm Thumbnail Upload",
|
"Confirm Thumbnail Upload": "Confirm Thumbnail Upload",
|
||||||
"Upload": "Upload",
|
"Upload": "Upload",
|
||||||
"upload": "upload",
|
"upload": "upload",
|
||||||
|
"Your uploads": "Your uploads",
|
||||||
|
"Invalid thumbnail": "Invalid thumbnail",
|
||||||
"Uploading thumbnail": "Uploading thumbnail",
|
"Uploading thumbnail": "Uploading thumbnail",
|
||||||
"Please wait for thumbnail to finish uploading": "Please wait for thumbnail to finish uploading",
|
"Please wait for thumbnail to finish uploading": "Please wait for thumbnail to finish uploading",
|
||||||
"A thumbnail is required. Please upload or provide an image URL above.": "A thumbnail is required. Please upload or provide an image URL above.",
|
"A thumbnail is required. Please upload or provide an image URL above.": "A thumbnail is required. Please upload or provide an image URL above.",
|
||||||
|
@ -538,6 +543,7 @@
|
||||||
"Create a new account or sign in.": "Create a new account or sign in.",
|
"Create a new account or sign in.": "Create a new account or sign in.",
|
||||||
"Terms of Service": "Terms of Service",
|
"Terms of Service": "Terms of Service",
|
||||||
"Learn More": "Learn More",
|
"Learn More": "Learn More",
|
||||||
|
"Learn more": "Learn more",
|
||||||
"Community": "Community",
|
"Community": "Community",
|
||||||
"Resources": "Resources",
|
"Resources": "Resources",
|
||||||
"Policies": "Policies",
|
"Policies": "Policies",
|
||||||
|
@ -780,6 +786,7 @@
|
||||||
"There was an error reposting this claim. Please try again later.": "There was an error reposting this claim. Please try again later.",
|
"There was an error reposting this claim. Please try again later.": "There was an error reposting this claim. Please try again later.",
|
||||||
"Claim ID": "Claim ID",
|
"Claim ID": "Claim ID",
|
||||||
"Official YouTube Creator": "Official YouTube Creator",
|
"Official YouTube Creator": "Official YouTube Creator",
|
||||||
|
"YouTube Sync": "YouTube Sync",
|
||||||
"Last checked %time_ago%": "Last checked %time_ago%",
|
"Last checked %time_ago%": "Last checked %time_ago%",
|
||||||
"Install Now": "Install Now",
|
"Install Now": "Install Now",
|
||||||
"Invite Link": "Invite Link",
|
"Invite Link": "Invite Link",
|
||||||
|
@ -1397,8 +1404,8 @@
|
||||||
"Gaming": "Gaming",
|
"Gaming": "Gaming",
|
||||||
"Game": "Game",
|
"Game": "Game",
|
||||||
"Nice People": "Nice People",
|
"Nice People": "Nice People",
|
||||||
"Tech": "Tech",
|
|
||||||
"Lab": "Lab",
|
"Lab": "Lab",
|
||||||
|
"Tech": "Tech",
|
||||||
"Technology": "Technology",
|
"Technology": "Technology",
|
||||||
"Movies": "Movies",
|
"Movies": "Movies",
|
||||||
"News": "News",
|
"News": "News",
|
||||||
|
@ -1456,6 +1463,7 @@
|
||||||
"Staked LBRY Credits": "Staked LBRY Credits",
|
"Staked LBRY Credits": "Staked LBRY Credits",
|
||||||
"1 comment": "1 comment",
|
"1 comment": "1 comment",
|
||||||
"%total_comments% comments": "%total_comments% comments",
|
"%total_comments% comments": "%total_comments% comments",
|
||||||
|
"No comments": "No comments",
|
||||||
"Upvote": "Upvote",
|
"Upvote": "Upvote",
|
||||||
"Downvote": "Downvote",
|
"Downvote": "Downvote",
|
||||||
"You loved this": "You loved this",
|
"You loved this": "You loved this",
|
||||||
|
@ -1630,6 +1638,7 @@
|
||||||
"This link leads to an external website.": "This link leads to an external website.",
|
"This link leads to an external website.": "This link leads to an external website.",
|
||||||
"No Content Found": "No Content Found",
|
"No Content Found": "No Content Found",
|
||||||
"No Lists Found": "No Lists Found",
|
"No Lists Found": "No Lists Found",
|
||||||
|
"No matching playlists": "No matching playlists",
|
||||||
"You have no lists! Create one from any playable content.": "You have no lists! Create one from any playable content.",
|
"You have no lists! Create one from any playable content.": "You have no lists! Create one from any playable content.",
|
||||||
"Pick": "Pick",
|
"Pick": "Pick",
|
||||||
"You have unpublished lists! %pick% one and publish it!": "You have unpublished lists! %pick% one and publish it!",
|
"You have unpublished lists! %pick% one and publish it!": "You have unpublished lists! %pick% one and publish it!",
|
||||||
|
@ -1752,12 +1761,8 @@
|
||||||
"Moderator": "Moderator",
|
"Moderator": "Moderator",
|
||||||
"Global Unblock Channel": "Global Unblock Channel",
|
"Global Unblock Channel": "Global Unblock Channel",
|
||||||
"Global Block Channel": "Global Block Channel",
|
"Global Block Channel": "Global Block Channel",
|
||||||
"Moderator tools": "Moderator tools",
|
"Block this channel on behalf of %creator%.": "Block this channel on behalf of %creator%.",
|
||||||
"Global Block": "Global Block",
|
"Block this channel on behalf of the creator.": "Block this channel on behalf of the creator.",
|
||||||
"Block this channel as global admin": "Block this channel as global admin",
|
|
||||||
"Moderator Block": "Moderator Block",
|
|
||||||
"Block this channel on behalf of %creator%": "Block this channel on behalf of %creator%",
|
|
||||||
"creator": "creator",
|
|
||||||
"Enter the timeout duration. Examples: %examples%": "Enter the timeout duration. Examples: %examples%",
|
"Enter the timeout duration. Examples: %examples%": "Enter the timeout duration. Examples: %examples%",
|
||||||
"Wow, banned for more than 100 years?": "Wow, banned for more than 100 years?",
|
"Wow, banned for more than 100 years?": "Wow, banned for more than 100 years?",
|
||||||
"Invalid duration.": "Invalid duration.",
|
"Invalid duration.": "Invalid duration.",
|
||||||
|
@ -1775,12 +1780,15 @@
|
||||||
"Moderators": "Moderators",
|
"Moderators": "Moderators",
|
||||||
"Moderators can block channels on your behalf. Blocked channels will appear in your \"Blocked and Muted\" list.": "Moderators can block channels on your behalf. Blocked channels will appear in your \"Blocked and Muted\" list.",
|
"Moderators can block channels on your behalf. Blocked channels will appear in your \"Blocked and Muted\" list.": "Moderators can block channels on your behalf. Blocked channels will appear in your \"Blocked and Muted\" list.",
|
||||||
"Add as moderator": "Add as moderator",
|
"Add as moderator": "Add as moderator",
|
||||||
"Assign this user to moderate %channel%": "Assign this user to moderate %channel%",
|
"Assign this user to moderate %channel%.": "Assign this user to moderate %channel%.",
|
||||||
|
"Assign this user to moderate your channel.": "Assign this user to moderate your channel.",
|
||||||
"Mute (m)": "Mute (m)",
|
"Mute (m)": "Mute (m)",
|
||||||
"Playback Rate (<, >)": "Playback Rate (<, >)",
|
"Playback Rate (<, >)": "Playback Rate (<, >)",
|
||||||
"Fullscreen (f)": "Fullscreen (f)",
|
"Fullscreen (f)": "Fullscreen (f)",
|
||||||
"Pause (space)": "Pause (space)",
|
"Pause (space)": "Pause (space)",
|
||||||
"Play (space)": "Play (space)",
|
"Play (space)": "Play (space)",
|
||||||
|
"Play Next (SHIFT+N)": "Play Next (SHIFT+N)",
|
||||||
|
"Play Previous (SHIFT+P)": "Play Previous (SHIFT+P)",
|
||||||
"Unmute (m)": "Unmute (m)",
|
"Unmute (m)": "Unmute (m)",
|
||||||
"Exit Fullscreen (f)": "Exit Fullscreen (f)",
|
"Exit Fullscreen (f)": "Exit Fullscreen (f)",
|
||||||
"Theater Mode (t)": "Theater Mode (t)",
|
"Theater Mode (t)": "Theater Mode (t)",
|
||||||
|
@ -2051,6 +2059,8 @@
|
||||||
"MyAwesomeList": "MyAwesomeList",
|
"MyAwesomeList": "MyAwesomeList",
|
||||||
"My Awesome List": "My Awesome List",
|
"My Awesome List": "My Awesome List",
|
||||||
"This list has no items.": "This list has no items.",
|
"This list has no items.": "This list has no items.",
|
||||||
|
"1 item": "1 item",
|
||||||
|
"%collectionCount% items": "%collectionCount% items",
|
||||||
"Select File": "Select File",
|
"Select File": "Select File",
|
||||||
"File Selected": "File Selected",
|
"File Selected": "File Selected",
|
||||||
"Url": "Url",
|
"Url": "Url",
|
||||||
|
@ -2108,6 +2118,7 @@
|
||||||
"Manage Card": "Manage Card",
|
"Manage Card": "Manage Card",
|
||||||
"Card Details": "Card Details",
|
"Card Details": "Card Details",
|
||||||
"Remove Card": "Remove Card",
|
"Remove Card": "Remove Card",
|
||||||
|
"Sorry, there was an error in processing your payment!": "Sorry, there was an error in processing your payment!",
|
||||||
"Congratulations! Your account has been connected with Odysee.": "Congratulations! Your account has been connected with Odysee.",
|
"Congratulations! Your account has been connected with Odysee.": "Congratulations! Your account has been connected with Odysee.",
|
||||||
"Your account balance is %balance% USD. Functionality to view your transactions and withdraw your balance will be landing shortly.": "Your account balance is %balance% USD. Functionality to view your transactions and withdraw your balance will be landing shortly.",
|
"Your account balance is %balance% USD. Functionality to view your transactions and withdraw your balance will be landing shortly.": "Your account balance is %balance% USD. Functionality to view your transactions and withdraw your balance will be landing shortly.",
|
||||||
"Your account balance is $0 USD. When you receive a tip you will see it here.": "Your account balance is $0 USD. When you receive a tip you will see it here.",
|
"Your account balance is $0 USD. When you receive a tip you will see it here.": "Your account balance is $0 USD. When you receive a tip you will see it here.",
|
||||||
|
@ -2137,11 +2148,8 @@
|
||||||
"%title% by %channelTitle%": "%title% by %channelTitle%",
|
"%title% by %channelTitle%": "%title% by %channelTitle%",
|
||||||
"%title% by %channelTitle% %ariaDate%": "%title% by %channelTitle% %ariaDate%",
|
"%title% by %channelTitle% %ariaDate%": "%title% by %channelTitle% %ariaDate%",
|
||||||
"%title% by %channelTitle% %ariaDate%, %mediaDuration%": "%title% by %channelTitle% %ariaDate%, %mediaDuration%",
|
"%title% by %channelTitle% %ariaDate%, %mediaDuration%": "%title% by %channelTitle% %ariaDate%, %mediaDuration%",
|
||||||
"Collapse Thread": "Collapse Thread",
|
|
||||||
"Collapse": "Collapse",
|
"Collapse": "Collapse",
|
||||||
"Expand Comments": "Expand Comments",
|
|
||||||
"Expand": "Expand",
|
"Expand": "Expand",
|
||||||
"Load More": "Load More",
|
|
||||||
"%formattedSubCount% Followers": "%formattedSubCount% Followers",
|
"%formattedSubCount% Followers": "%formattedSubCount% Followers",
|
||||||
"1 Follower": "1 Follower",
|
"1 Follower": "1 Follower",
|
||||||
"Collection": "Collection",
|
"Collection": "Collection",
|
||||||
|
@ -2169,5 +2177,150 @@
|
||||||
"Content Page": "Content Page",
|
"Content Page": "Content Page",
|
||||||
"Card Last 4": "Card Last 4",
|
"Card Last 4": "Card Last 4",
|
||||||
"Search blocked channel name": "Search blocked channel name",
|
"Search blocked channel name": "Search blocked channel name",
|
||||||
|
"Discuss": "Discuss",
|
||||||
|
"lbry.tv has been retired. You have been magically transported to Odysee.com. %more%": "lbry.tv has been retired. You have been magically transported to Odysee.com. %more%",
|
||||||
|
"Show more livestreams": "Show more livestreams",
|
||||||
|
"Creator": "Creator",
|
||||||
|
"From comments": "From comments",
|
||||||
|
"From search": "From search",
|
||||||
|
"Manage tags": "Manage tags",
|
||||||
|
"No Reposts": "No Reposts",
|
||||||
|
"You haven't reposted anything yet. Do it.": "You haven't reposted anything yet. Do it.",
|
||||||
|
"Claiming credits...": "Claiming credits...",
|
||||||
|
"%totalComments% comments": "%totalComments% comments",
|
||||||
|
"(%lbc_balance% available)": "(%lbc_balance% available)",
|
||||||
|
"Sending...": "Sending...",
|
||||||
|
"Trending for #Art": "Trending for #Art",
|
||||||
|
"Trending for #Btc": "Trending for #Btc",
|
||||||
|
"Trending for #Music": "Trending for #Music",
|
||||||
|
"You sent %lbc% as a tip, Mahalo!": "You sent %lbc% as a tip, Mahalo!",
|
||||||
|
"Export All": "Export All",
|
||||||
|
"Default share url (%name%)": "Default share url (%name%)",
|
||||||
|
"Custom share url": "Custom share url",
|
||||||
|
"Share url": "Share url",
|
||||||
|
"Odysee Connect": "Odysee Connect",
|
||||||
|
"Log in to %CLOUD_CONNECT_SITE_NAME%": "Log in to %CLOUD_CONNECT_SITE_NAME%",
|
||||||
|
"Cloud Connect": "Cloud Connect",
|
||||||
|
"Connect your wallet to Odysee": "Connect your wallet to Odysee",
|
||||||
|
"Minimum time gap in seconds between comments.": "Minimum time gap in seconds between comments.",
|
||||||
|
"Enabling a minimum amount to comment will force all comments to have tips associated with them. This can help prevent spam.": "Enabling a minimum amount to comment will force all comments to have tips associated with them. This can help prevent spam.",
|
||||||
|
"Comments containing these words will be blocked.": "Comments containing these words will be blocked.",
|
||||||
|
"Enter the full channel name or URL to search.\n\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8": "Enter the full channel name or URL to search.\n\nExamples:\n - @channel\n - @channel#3\n - https://odysee.com/@Odysee:8\n - lbry://@Odysee#8",
|
||||||
|
"Enable Data Hosting": "Enable Data Hosting",
|
||||||
|
"Data over the limit will be deleted within 30 minutes. This will make the Yrbl cry a little bit.": "Data over the limit will be deleted within 30 minutes. This will make the Yrbl cry a little bit.",
|
||||||
|
"Choose %asset%": "Choose %asset%",
|
||||||
|
"Showing %filtered% results of %total%": "Showing %filtered% results of %total%",
|
||||||
|
"filtered": "filtered",
|
||||||
|
"View All Playlists": "View All Playlists",
|
||||||
|
"Your wallet is not currently using a cloud sync service. You are in control of backing up your wallet.": "Your wallet is not currently using a cloud sync service. You are in control of backing up your wallet.",
|
||||||
|
"Sending": "Sending",
|
||||||
|
"You sent %lbc%": "You sent %lbc%",
|
||||||
|
"Buy LBC": "Buy LBC",
|
||||||
|
"This is information like error logging, performance tracking, and usage statistics. It includes your IP address and basic system details, but no other identifying information (unless you connect to a cloud service)": "This is information like error logging, performance tracking, and usage statistics. It includes your IP address and basic system details, but no other identifying information (unless you connect to a cloud service)",
|
||||||
|
"Use official LBRY wallet servers": "Use official LBRY wallet servers",
|
||||||
|
"Enable Prerelease Updates": "Enable Prerelease Updates",
|
||||||
|
"Enable Upgrade to Test Builds": "Enable Upgrade to Test Builds",
|
||||||
|
"Prereleases may break things and we may not be able to fix them for you.": "Prereleases may break things and we may not be able to fix them for you.",
|
||||||
|
"A channel is required to repost on LBRY": "A channel is required to repost on LBRY",
|
||||||
|
"Admin": "Admin",
|
||||||
|
"Stickers": "Stickers",
|
||||||
|
"Different Sticker": "Different Sticker",
|
||||||
|
"LBC": "LBC",
|
||||||
|
"Add a Card": "Add a Card",
|
||||||
|
" To Tip Creators": " To Tip Creators",
|
||||||
|
"Nothing found": "Nothing found",
|
||||||
|
"From Comments": "From Comments",
|
||||||
|
"This support is priced in $USD.": "This support is priced in $USD.",
|
||||||
|
"The current exchange rate for the submitted LBC amount is ~ $%exchange_amount%.": "The current exchange rate for the submitted LBC amount is ~ $%exchange_amount%.",
|
||||||
|
"Amount of $%input_amount% LBC in USB is lower than price of $%price_amount%": "Amount of $%input_amount% LBC in USB is lower than price of $%price_amount%",
|
||||||
|
"Hosting for content you have downloaded": "Hosting for content you have downloaded",
|
||||||
|
"Hosting content selected by the network": "Hosting content selected by the network",
|
||||||
|
"Remove all unavailable claims": "Remove all unavailable claims",
|
||||||
|
"Drag": "Drag",
|
||||||
|
"Move Top": "Move Top",
|
||||||
|
"Move Bottom": "Move Bottom",
|
||||||
|
"Move Up": "Move Up",
|
||||||
|
"Move Down": "Move Down",
|
||||||
|
"Trending for #Game": "Trending for #Game",
|
||||||
|
"Remove custom comment server": "Remove custom comment server",
|
||||||
|
"Use Https": "Use Https",
|
||||||
|
"Server URL": "Server URL",
|
||||||
|
"Use https": "Use https",
|
||||||
|
"Custom Servers": "Custom Servers",
|
||||||
|
"Add A Server": "Add A Server",
|
||||||
|
"Autoplay Next is off.": "Autoplay Next is off.",
|
||||||
|
"Shuffle is on.": "Shuffle is on.",
|
||||||
|
"Shuffle is off.": "Shuffle is off.",
|
||||||
|
"Loop is on.": "Loop is on.",
|
||||||
|
"Loop is off.": "Loop is off.",
|
||||||
|
"View History Hosting lets you choose how much storage to use helping content you've consumed.": "View History Hosting lets you choose how much storage to use helping content you've consumed.",
|
||||||
|
"Clean Now": "Clean Now",
|
||||||
|
"Enable Automatic Hosting": "Enable Automatic Hosting",
|
||||||
|
"Download and serve arbitrary data on the network.": "Download and serve arbitrary data on the network.",
|
||||||
|
"View History Hosting": "View History Hosting",
|
||||||
|
"Disable automatic updates": "Disable automatic updates",
|
||||||
|
"Preven't new updates to be downloaded automatically in the background (we will keep notifying you if there is an update)": "Preven't new updates to be downloaded automatically in the background (we will keep notifying you if there is an update)",
|
||||||
|
"Unlimited View Hosting": "Unlimited View Hosting",
|
||||||
|
"Choose View Hosting Limit": "Choose View Hosting Limit",
|
||||||
|
"View Hosting Limit (GB)": "View Hosting Limit (GB)",
|
||||||
|
"%free% of %total% available": "%free% of %total% available",
|
||||||
|
"Short (< 4 min)": "Short (< 4 min)",
|
||||||
|
"Medium (4 - 20 min)": "Medium (4 - 20 min)",
|
||||||
|
"Rename List": "Rename List",
|
||||||
|
"New Name": "New Name",
|
||||||
|
"Rename": "Rename",
|
||||||
|
"New Collection Name": "New Collection Name",
|
||||||
|
"In %collection%": "In %collection%",
|
||||||
|
"Add to %collection%": "Add to %collection%",
|
||||||
|
"Show this channel your appreciation by sending a donation of Credits. ": "Show this channel your appreciation by sending a donation of Credits. ",
|
||||||
|
"You've entered the land of content freedom! Let's make sure everything is ship shape.": "You've entered the land of content freedom! Let's make sure everything is ship shape.",
|
||||||
|
"By continuing, you agree to the %terms%": "By continuing, you agree to the %terms%",
|
||||||
|
"Privacy": "Privacy",
|
||||||
|
"LBRY takes privacy and choice seriously. Is it ok if we monitor performance and help creators track their views?": "LBRY takes privacy and choice seriously. Is it ok if we monitor performance and help creators track their views?",
|
||||||
|
"Yes, share with LBRY": "Yes, share with LBRY",
|
||||||
|
"Search Uploads": "Search Uploads",
|
||||||
|
"This refundable boost will improve the discoverability of this %claimTypeText% while active. ": "This refundable boost will improve the discoverability of this %claimTypeText% while active. ",
|
||||||
|
"Show less": "Show less",
|
||||||
|
"Elements": "Elements",
|
||||||
|
"Icons": "Icons",
|
||||||
|
"Go to": "Go to",
|
||||||
|
"Clearing...": "Clearing...",
|
||||||
|
"Clear Views": "Clear Views",
|
||||||
|
"Show Video View Progress": "Show Video View Progress",
|
||||||
|
"Display view progress on thumbnail. This setting will not hide any blockchain activity or downloads.": "Display view progress on thumbnail. This setting will not hide any blockchain activity or downloads.",
|
||||||
|
"Content Hosting": "Content Hosting",
|
||||||
|
"Hosting": "Hosting",
|
||||||
|
"Viewed Hosting": "Viewed Hosting",
|
||||||
|
"Auto Hosting": "Auto Hosting",
|
||||||
|
"Help creators and improve the P2P data network by hosting content.": "Help creators and improve the P2P data network by hosting content.",
|
||||||
|
"I'm happy with my settings": "I'm happy with my settings",
|
||||||
|
"We've noticed you already have some settings.": "We've noticed you already have some settings.",
|
||||||
|
"You choose how much data to host.": "You choose how much data to host.",
|
||||||
|
"Go back": "Go back",
|
||||||
|
"Custom Hosting": "Custom Hosting",
|
||||||
|
"Automatic Hosting (GB)": "Automatic Hosting (GB)",
|
||||||
|
"* Note that as peer-to-peer software, your IP address and potentially other system information can be sent to other users, though this information is not stored permanently.": "* Note that as peer-to-peer software, your IP address and potentially other system information can be sent to other users, though this information is not stored permanently.",
|
||||||
|
"Help improve the P2P data network (and make LBRY users happy) by hosting data.": "Help improve the P2P data network (and make LBRY users happy) by hosting data.",
|
||||||
|
"View History Hosting lets you choose how much storage to use hosting content you've consumed.": "View History Hosting lets you choose how much storage to use hosting content you've consumed.",
|
||||||
|
"Automatic Hosting downloads a small portion of content active on the network.": "Automatic Hosting downloads a small portion of content active on the network.",
|
||||||
|
"Publishes --[legend, storage category]--": "Publishes",
|
||||||
|
"Auto Hosting --[legend, storage category]--": "Auto Hosting",
|
||||||
|
"View Hosting --[legend, storage category]--": "View Hosting",
|
||||||
|
"%spaceUsed% of %limit% GB": "%spaceUsed% of %limit% GB",
|
||||||
|
"%spaceUsed% of %limit% Free GB": "%spaceUsed% of %limit% Free GB",
|
||||||
|
"Disabled": "Disabled",
|
||||||
|
"Free --[legend, unused disk space]--": "Free",
|
||||||
|
"Top content in %language%": "Top content in %language%",
|
||||||
|
"Apply": "Apply",
|
||||||
|
"Disable background": "Disable background",
|
||||||
|
"Installing, please wait...": "Installing, please wait...",
|
||||||
|
"There was an error during installation. Please, try again.": "There was an error during installation. Please, try again.",
|
||||||
|
"Odysee Connect --[Section in Help Page]--": "Odysee Connect",
|
||||||
|
"Your hub has blocked this content because it subscribes to the following blocking channel:": "Your hub has blocked this content because it subscribes to the following blocking channel:",
|
||||||
|
"Your hub has blocked access to this content do to a complaint received under the US Digital Millennium Copyright Act.": "Your hub has blocked access to this content do to a complaint received under the US Digital Millennium Copyright Act.",
|
||||||
|
"Autoplay Next is on.": "Autoplay Next is on.",
|
||||||
|
"This will be visible in a few minutes after you submit this form.": "This will be visible in a few minutes after you submit this form.",
|
||||||
|
"Anon --[used in <%anonymous% Reposted>]--": "Anon",
|
||||||
|
"Your update is now pending. It will take a few minutes to appear for other users.": "Your update is now pending. It will take a few minutes to appear for other users.",
|
||||||
"--end--": "--end--"
|
"--end--": "--end--"
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue