Compare commits
708 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
947887f261 | ||
|
6f34fdb616 | ||
|
8518d005fd | ||
|
bb911043de | ||
|
da0333f1cb | ||
|
d8a0ee1956 | ||
|
91c71471ab | ||
|
98eacb2238 | ||
|
80c13bf0ef | ||
|
e17c2e5da5 | ||
|
4a618908e8 | ||
|
a208e7d655 | ||
|
c1b9ae7fc2 | ||
|
dc8a6244fc | ||
|
0f52e42c2d | ||
|
85af2405cf | ||
|
47ace633dc | ||
|
85d5518b6b | ||
|
5104bd7988 | ||
|
3e425b51fd | ||
|
37dbfa4cd7 | ||
|
0d23c81662 | ||
|
b436b31d5a | ||
|
72133fbed6 | ||
|
abbdacedc5 | ||
|
ddd3251912 | ||
|
605e2a417c | ||
|
f8fe394e7a | ||
|
2a545e0fb1 | ||
|
ce812466c7 | ||
|
47bf7a8047 | ||
|
85d405c810 | ||
|
f596a413ef | ||
|
9e53fe5c29 | ||
|
3690f04e4a | ||
|
f3e8bc9f8f | ||
|
dcf0530218 | ||
|
47338bc13d | ||
|
6fb9726b99 | ||
|
8015fd7600 | ||
|
4919b89ab8 | ||
|
2925372ff4 | ||
|
778430b54a | ||
|
5282ba862a | ||
|
0464602978 | ||
|
9b03cf0ddd | ||
|
cdff1da901 | ||
|
1a065fb146 | ||
|
022008a2a6 | ||
|
a3715598cc | ||
|
1be48dd805 | ||
|
6384041d17 | ||
|
140e73bc82 | ||
|
e3fae6f52c | ||
|
65d8c73bae | ||
|
177dd8bb53 | ||
|
380b20eed6 | ||
|
c207b4bb33 | ||
|
b87eb8ea14 | ||
|
8902e265b4 | ||
|
b8ea28d6d0 | ||
|
f741673638 | ||
|
0a0b9a271a | ||
|
7d2b4186c3 | ||
|
90689190a3 | ||
|
8acadeea76 | ||
|
75c6513c67 | ||
|
73540ffe6b | ||
|
92bb166246 | ||
|
8cf8ce4ac0 | ||
|
0f1b1d78b1 | ||
|
d3bbef27e7 | ||
|
f0634ba876 | ||
|
1d68fe1a60 | ||
|
6bd6dcf6df | ||
|
28d2920472 | ||
|
34bfea8bbf | ||
|
2d91944285 | ||
|
0026ba2751 | ||
|
b623dd12c1 | ||
|
722d152082 | ||
|
7623766241 | ||
|
e34c5a3503 | ||
|
004672aa6c | ||
|
ad4a28f4f6 | ||
|
d8ae3efec3 | ||
|
cd81a1c52a | ||
|
dcf73ddeff | ||
|
d81b706f12 | ||
|
30fa5fe1a4 | ||
|
7a7bfa5170 | ||
|
e969c78645 | ||
|
7adac1bc51 | ||
|
e859d6f259 | ||
|
a0880edc6e | ||
|
61fcdbbf7e | ||
|
43af695ba1 | ||
|
facd90e7a6 | ||
|
6201f96b8a | ||
|
c26cea262b | ||
|
1f1d6bf2a0 | ||
|
4c06d1cb24 | ||
|
2985d08951 | ||
|
66ca7157db | ||
|
4addf051d4 | ||
|
ab914ce6d5 | ||
|
6a4b224397 | ||
|
6adbd114c1 | ||
|
037f96c5ae | ||
|
f54dca06a9 | ||
|
370fa70924 | ||
|
5be1214c26 | ||
|
f7a30e2fae | ||
|
3f815b2052 | ||
|
defe4f9bc3 | ||
|
943775fd90 | ||
|
42844df966 | ||
|
b0fe58dc69 | ||
|
e07b57852e | ||
|
7c7c18fdea | ||
|
a84664026e | ||
|
02a0fd5b64 | ||
|
6505a42be0 | ||
|
e674608d10 | ||
|
c7af8cbc90 | ||
|
9475fbae78 | ||
|
00e61d6807 | ||
|
19084d3c6c | ||
|
e014bf8ed0 | ||
|
f6e2309e70 | ||
|
9d2154c4ab | ||
|
1dfd27a028 | ||
|
b97ebaf620 | ||
|
8ee2eb5d2e | ||
|
20b647020b | ||
|
3eedad2737 | ||
|
ce7c0def88 | ||
|
dab8fc4584 | ||
|
8a597f0138 | ||
|
3363f2f4d6 | ||
|
c7f2d6af55 | ||
|
e878ddb7c0 | ||
|
336f0b0823 | ||
|
3ea3f24a02 | ||
|
d567a382e3 | ||
|
18fe77084f | ||
|
dc253ea234 | ||
|
9304114b57 | ||
|
1fd5251376 | ||
|
edddc7c791 | ||
|
10768aa204 | ||
|
e98559c3ff | ||
|
2212dc4aaa | ||
|
e1fdac3e9a | ||
|
1162f61ca3 | ||
|
39ea5c0e2e | ||
|
509b0cfafc | ||
|
fda5c699c2 | ||
|
cb7ee4698f | ||
|
d010e270e6 | ||
|
d1e08bd38c | ||
|
dbccdcc1b1 | ||
|
5c63523972 | ||
|
de4681b2be | ||
|
a132332b86 | ||
|
b25e42a77f | ||
|
5236a62861 | ||
|
0f155829b7 | ||
|
84dda45df9 | ||
|
9c7505489f | ||
|
75cad1d9d6 | ||
|
2cc3111a77 | ||
|
bf811e4d4a | ||
|
d6774d2ca3 | ||
|
bd669e3907 | ||
|
1a4860a57a | ||
|
41fa53253c | ||
|
c00ead8a72 | ||
|
e49dc6a06e | ||
|
0e12a8dab9 | ||
|
3652a39de0 | ||
|
79335e46fd | ||
|
7c6e02aaf3 | ||
|
7f55430652 | ||
|
8235623362 | ||
|
83435c49ea | ||
|
93de41b39b | ||
|
b1d4b74a44 | ||
|
bfdf47bc98 | ||
|
33f669a5f8 | ||
|
3576fa0d59 | ||
|
1dcfb90202 | ||
|
22cf18e16f | ||
|
0ebe7d6d23 | ||
|
23081bb299 | ||
|
4c7fe48c40 | ||
|
499cc7b803 | ||
|
7db98aa70e | ||
|
e031fd60ad | ||
|
bc4fad9e22 | ||
|
5ac4d677e9 | ||
|
b42bdd80e8 | ||
|
76fa9d2488 | ||
|
dfc43a6d3d | ||
|
67bc58dd60 | ||
|
2d39560dc1 | ||
|
c49ff7395e | ||
|
e0ada97770 | ||
|
3a2003ba86 | ||
|
9a81be0d37 | ||
|
5e2c5e95b6 | ||
|
34a93ccf57 | ||
|
922fb74197 | ||
|
7bf2d6cb06 | ||
|
11e5c965c3 | ||
|
34157d118c | ||
|
7b92950f1c | ||
|
97d7028c31 | ||
|
a7f2961621 | ||
|
00dda99789 | ||
|
2e27ce3b61 | ||
|
2c10c5a069 | ||
|
bd4dd4c4a0 | ||
|
7d33b60f3f | ||
|
aecce5694b | ||
|
0e4ca51951 | ||
|
dde043f6cd | ||
|
c778a60e4f | ||
|
c347327d54 | ||
|
fd328cf6e8 | ||
|
7b473d7514 | ||
|
dff576b75d | ||
|
52ae83d008 | ||
|
5aacd9d4c7 | ||
|
d24d3fa283 | ||
|
c8a226f61c | ||
|
7a281c477a | ||
|
91c789ec63 | ||
|
9ead3d1cdb | ||
|
402c19a924 | ||
|
b5e8994844 | ||
|
4bd327a0c5 | ||
|
184325077e | ||
|
8236f942ff | ||
|
8963f8c3c2 | ||
|
5e41c26203 | ||
|
45837c533e | ||
|
3fa8512474 | ||
|
0e20de9f89 | ||
|
24d645b7d0 | ||
|
7b23f79d41 | ||
|
3b4095cf1b | ||
|
28cbfb9f10 | ||
|
189a06d2a2 | ||
|
450441fc11 | ||
|
b619362a36 | ||
|
425d02287a | ||
|
2e429c0c25 | ||
|
e0e12b0fee | ||
|
62ca37884a | ||
|
f9180823bc | ||
|
4b0c667c09 | ||
|
1b732cad61 | ||
|
ecef03bb15 | ||
|
9642601126 | ||
|
3836d293a1 | ||
|
0734e1fe33 | ||
|
44cb08297c | ||
|
bd21afb5ed | ||
|
ef80ad17b3 | ||
|
9ea4f37e78 | ||
|
c48772fd3f | ||
|
860e257a68 | ||
|
902d9e34b4 | ||
|
2d97c898f2 | ||
|
f6a93fc150 | ||
|
019f3377bb | ||
|
4b11675bdc | ||
|
2531c5953b | ||
|
c6db416ff7 | ||
|
b00cb2aed3 | ||
|
7c67cb5997 | ||
|
a098d08d12 | ||
|
6267759607 | ||
|
bc39ad37c4 | ||
|
a6ba004bf5 | ||
|
b89ab7e69d | ||
|
33d7338779 | ||
|
cf4fe6cab8 | ||
|
2241a15ee9 | ||
|
bca334cd28 | ||
|
3e3ec9b2c8 | ||
|
a8736aab7a | ||
|
71b266377c | ||
|
08dce5e607 | ||
|
2469fd1cdc | ||
|
531c1bb245 | ||
|
58f5040ee8 | ||
|
838f51770b | ||
|
c52090dbfe | ||
|
807c192fcf | ||
|
3b59f9c6c2 | ||
|
135bdd149e | ||
|
3572138b16 | ||
|
9f69aa3cb1 | ||
|
f5c3d20e9c | ||
|
1ec7c87001 | ||
|
8e4d1cba00 | ||
|
676ba50601 | ||
|
bbc3db8b20 | ||
|
f937cad68f | ||
|
be83d450eb | ||
|
1fd18a61bd | ||
|
5d9f479538 | ||
|
3ce9ca4c99 | ||
|
2ca1f0737a | ||
|
19ecde8fe7 | ||
|
7ee5fc5d68 | ||
|
4289ed1d13 | ||
|
256e3adc1d | ||
|
152b4d54e8 | ||
|
ea2ef16ea4 | ||
|
1d3e0a5060 | ||
|
bf575a1f5e | ||
|
860ffc0560 | ||
|
7eb4abe20a | ||
|
1baa75f79f | ||
|
1d436a4322 | ||
|
8fd174298d | ||
|
9afd7dadbf | ||
|
8e84177305 | ||
|
a28ce13b3e | ||
|
e1b42e9aa0 | ||
|
b51398d0dd | ||
|
ec34ec63b1 | ||
|
4a4733b397 | ||
|
bda7391221 | ||
|
b9e8ffbd12 | ||
|
7966d3a872 | ||
|
422e4d897b | ||
|
cb2707776f | ||
|
48e7a22e34 | ||
|
2bb5486357 | ||
|
60e2b951de | ||
|
a94c152fd3 | ||
|
9d04de1c8d | ||
|
73e4468ff3 | ||
|
fbbd80b40b | ||
|
361a606edb | ||
|
df92f010ad | ||
|
07af8c05fd | ||
|
aa662cecad | ||
|
84608c3ff8 | ||
|
b69365e397 | ||
|
a478af92c3 | ||
|
7fba4cb3d1 | ||
|
a4c757767f | ||
|
2af5cd96fe | ||
|
860f408475 | ||
|
440441ccb3 | ||
|
3eb13307ca | ||
|
756db8103a | ||
|
20c0054460 | ||
|
ae78d012ac | ||
|
ef900789bc | ||
|
d78f555254 | ||
|
c2f70829d9 | ||
|
b280c387c8 | ||
|
b75f13927e | ||
|
22cb286ad7 | ||
|
8f4b7c1820 | ||
|
2e112e2406 | ||
|
812fe90eca | ||
|
6c1122a1d9 | ||
|
d3be2b582a | ||
|
419226d1f6 | ||
|
f554807563 | ||
|
d972845ff6 | ||
|
2c405aed55 | ||
|
da0a18a318 | ||
|
8ed3fa1693 | ||
|
60fe9983ee | ||
|
724fc3cbdf | ||
|
de475cf8d3 | ||
|
b369fc2de4 | ||
|
8c5eaf7ae9 | ||
|
7eb8b2efad | ||
|
b6f6152e26 | ||
|
f8ee136c29 | ||
|
f1ab70649b | ||
|
1548695c83 | ||
|
3da521a586 | ||
|
d22cec81fb | ||
|
d2e0edd721 | ||
|
3002a89419 | ||
|
17ba662004 | ||
|
db4119f971 | ||
|
4a3db71692 | ||
|
dc559d6b7a | ||
|
595e060347 | ||
|
8e4fc5d5d2 | ||
|
b8b7b506a2 | ||
|
550863198c | ||
|
198ae3e366 | ||
|
6e4c7d6211 | ||
|
d2542dcec0 | ||
|
f18a6c2cf2 | ||
|
25e5aa645d | ||
|
620d0d8029 | ||
|
8ec8410651 | ||
|
4cc8ddabe5 | ||
|
07e875972a | ||
|
79ef8b3653 | ||
|
b11c4326d2 | ||
|
390a2a8ab9 | ||
|
cf6f67997e | ||
|
4d1ce3c7ad | ||
|
76449df903 | ||
|
226c9836e4 | ||
|
05008f3930 | ||
|
59ceeae8ea | ||
|
b397f69633 | ||
|
5b3c7572ca | ||
|
e89e4355eb | ||
|
abe0d9421f | ||
|
7c1f3f8163 | ||
|
eab93992d1 | ||
|
0d59d7c680 | ||
|
1efda1c453 | ||
|
a51c8074df | ||
|
3722f90865 | ||
|
9bddb946f0 | ||
|
bbaac89eb0 | ||
|
0dfffb6dcb | ||
|
1b0a5658f1 | ||
|
682b68438e | ||
|
09ec6e504b | ||
|
a2a2af244c | ||
|
cb50ecdb07 | ||
|
1379124682 | ||
|
0b34ade66b | ||
|
191696ab30 | ||
|
af706583bd | ||
|
85c9496340 | ||
|
6ee3a10f17 | ||
|
d0dd9eb5b5 | ||
|
a588358f41 | ||
|
0a110d07b6 | ||
|
5f727f9068 | ||
|
72c8562cc9 | ||
|
882e4f5322 | ||
|
bc1a91f4cd | ||
|
aeb90b7c4a | ||
|
fb87e847bc | ||
|
657496b5a9 | ||
|
fd03a3d957 | ||
|
4bd0488a77 | ||
|
1b17da6ed9 | ||
|
dc5b746f42 | ||
|
89210781cb | ||
|
e9810cbad6 | ||
|
1027556614 | ||
|
3dcb5fa28f | ||
|
1d5dcfcd46 | ||
|
66ff9ed34e | ||
|
66328adf83 | ||
|
b65950bb2e | ||
|
0d70fe2659 | ||
|
a1fc2cfa09 | ||
|
b535966ab5 | ||
|
02412429ab | ||
|
5abdc77c80 | ||
|
b5a9c6b3d2 | ||
|
60f3230a05 | ||
|
0cb4b9205c | ||
|
43d754eb42 | ||
|
2cc0d56652 | ||
|
e0c3ed29d8 | ||
|
2991a7cfe6 | ||
|
44a3584e2d | ||
|
831386977e | ||
|
68035966fb | ||
|
62a98a3f0e | ||
|
888864ad5a | ||
|
654f4f62ed | ||
|
58bcd50f7f | ||
|
60ecfb87ae | ||
|
d0ef318eaa | ||
|
65f9db73b0 | ||
|
a822f7a05a | ||
|
a2c8da0185 | ||
|
88fd5cb688 | ||
|
c78e8c01a3 | ||
|
0ec77c5b3e | ||
|
2d000e9c4e | ||
|
b913746752 | ||
|
9cd3a6836b | ||
|
53c2274d48 | ||
|
7ff84cb07e | ||
|
e6fbf0334f | ||
|
72698bc3b4 | ||
|
65027657ec | ||
|
08949cca41 | ||
|
a231f915a0 | ||
|
c3ef5d5414 | ||
|
57a3d71c90 | ||
|
43db2cf5e7 | ||
|
cc9a6a710f | ||
|
2fba4196ef | ||
|
fd66f7cdc0 | ||
|
d142544159 | ||
|
7ac092513c | ||
|
2db53526c9 | ||
|
1f28d40c78 | ||
|
e2491680e6 | ||
|
3a38322a54 | ||
|
29d8313b28 | ||
|
682507bc3c | ||
|
441d6dc734 | ||
|
d5cabfe5c6 | ||
|
af6a84da14 | ||
|
08e94d1b19 | ||
|
2fba94b36e | ||
|
8c9116dc98 | ||
|
42eb841dc2 | ||
|
584b45530c | ||
|
f5cdea5122 | ||
|
f36a791227 | ||
|
ef226a6f22 | ||
|
7c249dfd88 | ||
|
5bea42412e | ||
|
04166c4a35 | ||
|
fed585e3f4 | ||
|
406229d927 | ||
|
7a7d12d27f | ||
|
cd830a2fab | ||
|
aef554d553 | ||
|
01c4c29b3a | ||
|
459bbfa4b2 | ||
|
7140def5c9 | ||
|
b85dec2b97 | ||
|
cbd673601c | ||
|
66a3979cba | ||
|
9de254c46e | ||
|
388e70b881 | ||
|
8c9aff0bef | ||
|
48594b18e6 | ||
|
b18504adfe | ||
|
bba537a7be | ||
|
0291b73de7 | ||
|
28e674bc6a | ||
|
9d84dda213 | ||
|
d63c291f86 | ||
|
6ad19036e3 | ||
|
3bdcf5d8f0 | ||
|
5c1f70b5c5 | ||
|
c7848f54ff | ||
|
267ed3d74b | ||
|
d3704fdb09 | ||
|
ca05bfaac7 | ||
|
e4b84c7ba5 | ||
|
983593ddf4 | ||
|
f14df43435 | ||
|
f000673599 | ||
|
5b6c2a1e72 | ||
|
d372068620 | ||
|
139d183485 | ||
|
d7c17c32af | ||
|
ee1486a7de | ||
|
b8ba719f73 | ||
|
ada8a6cb77 | ||
|
6c678b7472 | ||
|
bfbfaf9f9f | ||
|
df81bc4a97 | ||
|
87588fa894 | ||
|
74036a2c9d | ||
|
05b72368ed | ||
|
6f71cfeff9 | ||
|
59ca634b89 | ||
|
8009366231 | ||
|
bd71327180 | ||
|
67b7d3d3b6 | ||
|
6358a169fd | ||
|
99b9a0e5de | ||
|
aa235318fc | ||
|
a0b1951791 | ||
|
2d45794956 | ||
|
453fb84c9c | ||
|
59804abc3d | ||
|
496f466d73 | ||
|
fa033c4d5f | ||
|
b8e166894b | ||
|
1f15a15621 | ||
|
fd1e29c3f8 | ||
|
553e13144f | ||
|
494945ff4f | ||
|
7c0cd2597a | ||
|
37caf0b36e | ||
|
cf0b753209 | ||
|
ddc34feb58 | ||
|
3f5b994ff0 | ||
|
dacdfec973 | ||
|
72c30f8393 | ||
|
4e05751346 | ||
|
ee3e0a93f4 | ||
|
d1290fbd8f | ||
|
484c9709b6 | ||
|
d08f1112d5 | ||
|
bcfd9a2f8e | ||
|
886176f854 | ||
|
d397d0d681 | ||
|
20c37ed0f9 | ||
|
9501a87704 | ||
|
8f0f4a861a | ||
|
8c9ea9b849 | ||
|
4d22d03fab | ||
|
81584779cb | ||
|
61c33652ad | ||
|
f9d398e8fb | ||
|
74c8ca699c | ||
|
eddb95b012 | ||
|
84eb425f38 | ||
|
a50a87457e | ||
|
566e0a772d | ||
|
11077af52f | ||
|
0fc73a6e47 | ||
|
2bd132d458 | ||
|
91ddd345f2 | ||
|
75bd141e22 | ||
|
0cdcf32865 | ||
|
629a4d0fca | ||
|
e95983f5df | ||
|
e37e84d210 | ||
|
e57e6f509d | ||
|
bea117a4b6 | ||
|
908b96a370 | ||
|
13c16b4e95 | ||
|
4fcc0d5ac9 | ||
|
3b51581f1b | ||
|
db92eec876 | ||
|
44969307c7 | ||
|
4babdff72f | ||
|
c997091166 | ||
|
005f1fd360 | ||
|
8d4e7504b1 | ||
|
aa6a26a2d5 | ||
|
d91ba3c8d0 | ||
|
bafd22ecf4 | ||
|
dd9d57300b | ||
|
8c5ad23b24 | ||
|
53384b0ffe | ||
|
1c469ca98b | ||
|
e61ecf4091 | ||
|
90c00f075a | ||
|
38473f0aa0 | ||
|
24a5d13d60 | ||
|
383c0b7802 | ||
|
bf8031e984 | ||
|
ab307b816b | ||
|
40562fd266 | ||
|
5f9cb48882 | ||
|
2ab7dc9a55 | ||
|
2b9bc9c154 | ||
|
f5bf5ebb82 | ||
|
26bc591572 | ||
|
268dd32d76 | ||
|
bea97ea766 | ||
|
03f3223d72 | ||
|
7880671f35 | ||
|
b5eec34230 | ||
|
2128682162 | ||
|
e68c0ce5f6 | ||
|
54dddfe9b8 | ||
|
aea3aff4e4 | ||
|
46943b64c6 | ||
|
302c0d2046 | ||
|
22b1a70274 | ||
|
6f75c8451d | ||
|
b9b78549f3 | ||
|
438ce5809f | ||
|
f485fa31f3 | ||
|
34ae4cf511 | ||
|
298796cc7b | ||
|
a4859446ab | ||
|
7bffd16024 | ||
|
2bd46f442d | ||
|
11b706acdf | ||
|
33b9e8d461 | ||
|
f025cc6782 | ||
|
3988f2dade | ||
|
1899cf5f04 | ||
|
5259319cf5 | ||
|
b83bc0ae64 | ||
|
282427cdd9 | ||
|
c67d3c990b | ||
|
2e47fe3e1a | ||
|
e12bb39c20 | ||
|
5caa727e7e | ||
|
0a46201a66 | ||
|
3f248dcaae | ||
|
baa43e40a0 | ||
|
a6788662b0 | ||
|
4a5f73c8ae | ||
|
fdcf884cf7 | ||
|
964035b118 | ||
|
5135d609b7 | ||
|
f48cb3eb17 | ||
|
8325866c61 |
63
.babelrc
63
.babelrc
@@ -1,7 +1,62 @@
|
|||||||
{
|
{
|
||||||
"presets": ["es2015", "react"],
|
"presets": [
|
||||||
|
"react",
|
||||||
|
[
|
||||||
|
"env",
|
||||||
|
{
|
||||||
|
"loose": true,
|
||||||
|
"modules": false,
|
||||||
|
"targets": {
|
||||||
|
"browsers": ["last 2 versions", "IE >= 11", "iOS >= 9"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"transform-decorators-legacy",
|
"syntax-dynamic-import",
|
||||||
"transform-object-rest-spread"
|
["transform-object-rest-spread", { "useBuiltIns": true }],
|
||||||
]
|
"transform-class-properties",
|
||||||
|
[
|
||||||
|
"react-intl",
|
||||||
|
{
|
||||||
|
"messagesDir": "./build/messages"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"development": {
|
||||||
|
"plugins": [
|
||||||
|
"transform-react-jsx-source",
|
||||||
|
"transform-react-jsx-self"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"plugins": [
|
||||||
|
"lodash",
|
||||||
|
[
|
||||||
|
"transform-react-remove-prop-types",
|
||||||
|
{
|
||||||
|
"mode": "remove",
|
||||||
|
"removeImport": true,
|
||||||
|
"additionalLibraries": [
|
||||||
|
"react-immutable-proptypes"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"transform-runtime",
|
||||||
|
{
|
||||||
|
"helpers": true,
|
||||||
|
"polyfill": false,
|
||||||
|
"regenerator": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"plugins": [
|
||||||
|
"transform-es2015-modules-commonjs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
|
https://github.com/heroku/heroku-buildpack-apt
|
||||||
https://github.com/Scalingo/nodejs-buildpack
|
https://github.com/Scalingo/nodejs-buildpack
|
||||||
https://github.com/Scalingo/ruby-buildpack
|
https://github.com/Scalingo/ruby-buildpack
|
||||||
|
@@ -1,14 +1,21 @@
|
|||||||
engines:
|
engines:
|
||||||
duplication:
|
brakeman:
|
||||||
enabled: false
|
enabled: true
|
||||||
rubocop:
|
bundler-audit:
|
||||||
enabled: true
|
enabled: true
|
||||||
eslint:
|
duplication:
|
||||||
enabled: true
|
enabled: false
|
||||||
|
eslint:
|
||||||
|
enabled: true
|
||||||
|
rubocop:
|
||||||
|
enabled: true
|
||||||
|
scss-lint:
|
||||||
|
enabled: true
|
||||||
ratings:
|
ratings:
|
||||||
paths:
|
paths:
|
||||||
- "**.rb"
|
- "**.rb"
|
||||||
- "**.js"
|
- "**.js"
|
||||||
|
- "**.scss"
|
||||||
exclude_paths:
|
exclude_paths:
|
||||||
- spec/
|
- spec/
|
||||||
- vendor/asset
|
- vendor/asset
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
.env.*
|
.env.*
|
||||||
public/system
|
public/system
|
||||||
public/assets
|
public/assets
|
||||||
|
public/packs
|
||||||
node_modules
|
node_modules
|
||||||
storybook
|
storybook
|
||||||
neo4j
|
neo4j
|
||||||
@@ -9,3 +10,5 @@ vendor/bundle
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.swp
|
*.swp
|
||||||
*~
|
*~
|
||||||
|
postgres
|
||||||
|
redis
|
||||||
|
111
.env.nanobox
Normal file
111
.env.nanobox
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# Service dependencies
|
||||||
|
# You may set REDIS_URL instead for more advanced options
|
||||||
|
REDIS_HOST=$DATA_REDIS_HOST
|
||||||
|
REDIS_PORT=6379
|
||||||
|
# REDIS_DB=0
|
||||||
|
|
||||||
|
# You may set DATABASE_URL instead for more advanced options
|
||||||
|
DB_HOST=$DATA_DB_HOST
|
||||||
|
DB_USER=$DATA_DB_USER
|
||||||
|
DB_NAME=gonano
|
||||||
|
DB_PASS=$DATA_DB_PASS
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
|
||||||
|
|
||||||
|
# Federation
|
||||||
|
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
|
||||||
|
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||||
|
LOCAL_DOMAIN=${APP_NAME}.nanoapp.io
|
||||||
|
LOCAL_HTTPS=false
|
||||||
|
|
||||||
|
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||||
|
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||||
|
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
|
||||||
|
# WEB_DOMAIN=mastodon.example.com
|
||||||
|
|
||||||
|
# Use this if you want to have several aliases handler@example1.com
|
||||||
|
# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not
|
||||||
|
# be added. Comma separated values
|
||||||
|
# ALTERNATE_DOMAINS=example1.com,example2.com
|
||||||
|
|
||||||
|
# Application secrets
|
||||||
|
# Generate each with the `rake secret` task (`nanobox run bundle exec rake secret`)
|
||||||
|
PAPERCLIP_SECRET=$PAPERCLIP_SECRET
|
||||||
|
SECRET_KEY_BASE=$SECRET_KEY_BASE
|
||||||
|
OTP_SECRET=$OTP_SECRET
|
||||||
|
|
||||||
|
# Registrations
|
||||||
|
# Single user mode will disable registrations and redirect frontpage to the first profile
|
||||||
|
# SINGLE_USER_MODE=true
|
||||||
|
# Prevent registrations with following e-mail domains
|
||||||
|
# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc
|
||||||
|
# Only allow registrations with the following e-mail domains
|
||||||
|
# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc
|
||||||
|
|
||||||
|
# Optionally change default language
|
||||||
|
# DEFAULT_LOCALE=de
|
||||||
|
|
||||||
|
# E-mail configuration
|
||||||
|
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
||||||
|
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
|
||||||
|
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
|
||||||
|
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
|
||||||
|
SMTP_SERVER=$SMTP_SERVER
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_LOGIN=$SMTP_LOGIN
|
||||||
|
SMTP_PASSWORD=$SMTP_PASSWORD
|
||||||
|
SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
|
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
||||||
|
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
||||||
|
#SMTP_AUTH_METHOD=plain
|
||||||
|
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||||
|
#SMTP_OPENSSL_VERIFY_MODE=peer
|
||||||
|
#SMTP_ENABLE_STARTTLS_AUTO=true
|
||||||
|
|
||||||
|
|
||||||
|
# Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files.
|
||||||
|
# PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system
|
||||||
|
# PAPERCLIP_ROOT_URL=/system
|
||||||
|
|
||||||
|
# Optional asset host for multi-server setups
|
||||||
|
# CDN_HOST=assets.example.com
|
||||||
|
|
||||||
|
# S3 (optional)
|
||||||
|
# S3_ENABLED=true
|
||||||
|
# S3_BUCKET=
|
||||||
|
# AWS_ACCESS_KEY_ID=
|
||||||
|
# AWS_SECRET_ACCESS_KEY=
|
||||||
|
# S3_REGION=
|
||||||
|
# S3_PROTOCOL=http
|
||||||
|
# S3_HOSTNAME=192.168.1.123:9000
|
||||||
|
|
||||||
|
# S3 (Minio Config (optional) Please check Minio instance for details)
|
||||||
|
# S3_ENABLED=true
|
||||||
|
# S3_BUCKET=
|
||||||
|
# AWS_ACCESS_KEY_ID=
|
||||||
|
# AWS_SECRET_ACCESS_KEY=
|
||||||
|
# S3_REGION=
|
||||||
|
# S3_PROTOCOL=https
|
||||||
|
# S3_HOSTNAME=
|
||||||
|
# S3_ENDPOINT=
|
||||||
|
# S3_SIGNATURE_VERSION=
|
||||||
|
|
||||||
|
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
|
||||||
|
# S3_CLOUDFRONT_HOST=
|
||||||
|
|
||||||
|
# Streaming API integration
|
||||||
|
# STREAMING_API_BASE_URL=
|
||||||
|
|
||||||
|
# Advanced settings
|
||||||
|
# If you need to use pgBouncer, you need to disable prepared statements:
|
||||||
|
# PREPARED_STATEMENTS=false
|
||||||
|
|
||||||
|
# Cluster number setting for streaming API server.
|
||||||
|
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||||
|
STREAMING_CLUSTER_NUM=1
|
||||||
|
|
||||||
|
# Docker mastodon user
|
||||||
|
# If you use Docker, you may want to assign UID/GID manually.
|
||||||
|
# UID=1000
|
||||||
|
# GID=1000
|
@@ -1,7 +1,8 @@
|
|||||||
# Service dependencies
|
# Service dependencies
|
||||||
|
# You may set REDIS_URL instead for more advanced options
|
||||||
REDIS_HOST=redis
|
REDIS_HOST=redis
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
# REDIS_DB=0
|
# You may set DATABASE_URL instead for more advanced options
|
||||||
DB_HOST=db
|
DB_HOST=db
|
||||||
DB_USER=postgres
|
DB_USER=postgres
|
||||||
DB_NAME=postgres
|
DB_NAME=postgres
|
||||||
@@ -9,13 +10,21 @@ DB_PASS=
|
|||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
|
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
|
||||||
|
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||||
LOCAL_DOMAIN=example.com
|
LOCAL_DOMAIN=example.com
|
||||||
LOCAL_HTTPS=true
|
LOCAL_HTTPS=true
|
||||||
|
|
||||||
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||||
# Do not use this unless you know exactly what you are doing.
|
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||||
|
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
|
||||||
# WEB_DOMAIN=mastodon.example.com
|
# WEB_DOMAIN=mastodon.example.com
|
||||||
|
|
||||||
|
# Use this if you want to have several aliases handler@example1.com
|
||||||
|
# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not
|
||||||
|
# be added. Comma separated values
|
||||||
|
# ALTERNATE_DOMAINS=example1.com,example2.com
|
||||||
|
|
||||||
# Application secrets
|
# Application secrets
|
||||||
# Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
# Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
||||||
PAPERCLIP_SECRET=
|
PAPERCLIP_SECRET=
|
||||||
@@ -36,8 +45,8 @@ OTP_SECRET=
|
|||||||
# E-mail configuration
|
# E-mail configuration
|
||||||
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
||||||
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
|
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
|
||||||
# then set SMTP_AUTH_METHOD to 'none' and *comment* SMTP_LOGIN and SMTP_PASSWORD.
|
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
|
||||||
# Leaving them blank is not enough for authentication method 'none'.
|
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
|
||||||
SMTP_SERVER=smtp.mailgun.org
|
SMTP_SERVER=smtp.mailgun.org
|
||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
SMTP_LOGIN=
|
SMTP_LOGIN=
|
||||||
@@ -46,6 +55,7 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
|||||||
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
||||||
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
||||||
#SMTP_AUTH_METHOD=plain
|
#SMTP_AUTH_METHOD=plain
|
||||||
|
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||||
#SMTP_OPENSSL_VERIFY_MODE=peer
|
#SMTP_OPENSSL_VERIFY_MODE=peer
|
||||||
#SMTP_ENABLE_STARTTLS_AUTO=true
|
#SMTP_ENABLE_STARTTLS_AUTO=true
|
||||||
|
|
||||||
@@ -55,7 +65,7 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
|||||||
# PAPERCLIP_ROOT_URL=/system
|
# PAPERCLIP_ROOT_URL=/system
|
||||||
|
|
||||||
# Optional asset host for multi-server setups
|
# Optional asset host for multi-server setups
|
||||||
# CDN_HOST=assets.example.com
|
# CDN_HOST=https://assets.example.com
|
||||||
|
|
||||||
# S3 (optional)
|
# S3 (optional)
|
||||||
# S3_ENABLED=true
|
# S3_ENABLED=true
|
||||||
@@ -90,3 +100,8 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
|||||||
# Cluster number setting for streaming API server.
|
# Cluster number setting for streaming API server.
|
||||||
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||||
STREAMING_CLUSTER_NUM=1
|
STREAMING_CLUSTER_NUM=1
|
||||||
|
|
||||||
|
# Docker mastodon user
|
||||||
|
# If you use Docker, you may want to assign UID/GID manually.
|
||||||
|
# UID=1000
|
||||||
|
# GID=1000
|
||||||
|
@@ -1,79 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"node": false,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"parser": "babel-eslint",
|
|
||||||
|
|
||||||
"plugins": [
|
|
||||||
"react",
|
|
||||||
"jsx-a11y"
|
|
||||||
],
|
|
||||||
|
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "module",
|
|
||||||
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"arrowFunctions": true,
|
|
||||||
"jsx": true,
|
|
||||||
"destructuring": true,
|
|
||||||
"modules": true,
|
|
||||||
"spread": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"rules": {
|
|
||||||
"no-cond-assign": 2,
|
|
||||||
"no-console": 1,
|
|
||||||
"no-irregular-whitespace": 2,
|
|
||||||
"no-unreachable": 2,
|
|
||||||
"valid-typeof": 2,
|
|
||||||
"consistent-return": 2,
|
|
||||||
"dot-notation": 2,
|
|
||||||
"eqeqeq": 2,
|
|
||||||
"no-fallthrough": 2,
|
|
||||||
"no-unused-expressions": 2,
|
|
||||||
"strict": 0,
|
|
||||||
"no-catch-shadow": 2,
|
|
||||||
"indent": [1, 2],
|
|
||||||
"brace-style": 1,
|
|
||||||
"comma-spacing": [1, {"before": false, "after": true}],
|
|
||||||
"comma-style": [1, "last"],
|
|
||||||
"no-mixed-spaces-and-tabs": 1,
|
|
||||||
"no-nested-ternary": 1,
|
|
||||||
"no-trailing-spaces": 1,
|
|
||||||
|
|
||||||
"react/jsx-wrap-multilines": 2,
|
|
||||||
"react/self-closing-comp": 2,
|
|
||||||
"react/prop-types": 2,
|
|
||||||
"react/no-multi-comp": 0,
|
|
||||||
|
|
||||||
"jsx-a11y/accessible-emoji": 1,
|
|
||||||
"jsx-a11y/anchor-has-content": 1,
|
|
||||||
"jsx-a11y/aria-activedescendant-has-tabindex": 1,
|
|
||||||
"jsx-a11y/aria-props": 1,
|
|
||||||
"jsx-a11y/aria-proptypes": 1,
|
|
||||||
"jsx-a11y/aria-role": 1,
|
|
||||||
"jsx-a11y/aria-unsupported-elements": 1,
|
|
||||||
"jsx-a11y/heading-has-content": 1,
|
|
||||||
"jsx-a11y/href-no-hash": 1,
|
|
||||||
"jsx-a11y/html-has-lang": 1,
|
|
||||||
"jsx-a11y/iframe-has-title": 1,
|
|
||||||
"jsx-a11y/img-has-alt": 1,
|
|
||||||
"jsx-a11y/img-redundant-alt": 1,
|
|
||||||
"jsx-a11y/label-has-for": 1,
|
|
||||||
"jsx-a11y/mouse-events-have-key-events": 1,
|
|
||||||
"jsx-a11y/no-access-key": 1,
|
|
||||||
"jsx-a11y/no-distracting-elements": 1,
|
|
||||||
"jsx-a11y/no-onchange": 1,
|
|
||||||
"jsx-a11y/no-redundant-roles": 1,
|
|
||||||
"jsx-a11y/onclick-has-focus": 1,
|
|
||||||
"jsx-a11y/onclick-has-role": 1,
|
|
||||||
"jsx-a11y/role-has-required-aria-props": 1,
|
|
||||||
"jsx-a11y/role-supports-aria-props": 1,
|
|
||||||
"jsx-a11y/scope": 1,
|
|
||||||
"jsx-a11y/tabindex-no-positive": 1
|
|
||||||
}
|
|
||||||
}
|
|
115
.eslintrc.yml
Normal file
115
.eslintrc.yml
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
env:
|
||||||
|
browser: true
|
||||||
|
node: false
|
||||||
|
es6: true
|
||||||
|
|
||||||
|
parser: babel-eslint
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- react
|
||||||
|
- jsx-a11y
|
||||||
|
|
||||||
|
parserOptions:
|
||||||
|
sourceType: module
|
||||||
|
ecmaFeatures:
|
||||||
|
arrowFunctions: true
|
||||||
|
jsx: true
|
||||||
|
destructuring: true
|
||||||
|
modules: true
|
||||||
|
spread: true
|
||||||
|
|
||||||
|
rules:
|
||||||
|
|
||||||
|
brace-style: warn
|
||||||
|
comma-dangle:
|
||||||
|
- error
|
||||||
|
- always-multiline
|
||||||
|
comma-spacing:
|
||||||
|
- warn
|
||||||
|
- before: false
|
||||||
|
after: true
|
||||||
|
comma-style:
|
||||||
|
- warn
|
||||||
|
- last
|
||||||
|
consistent-return: error
|
||||||
|
dot-notation: error
|
||||||
|
eqeqeq: error
|
||||||
|
indent:
|
||||||
|
- warn
|
||||||
|
- 2
|
||||||
|
jsx-quotes:
|
||||||
|
- error
|
||||||
|
- prefer-single
|
||||||
|
no-catch-shadow: error
|
||||||
|
no-cond-assign: error
|
||||||
|
no-console:
|
||||||
|
- warn
|
||||||
|
- allow:
|
||||||
|
- error
|
||||||
|
no-fallthrough: error
|
||||||
|
no-irregular-whitespace: error
|
||||||
|
no-mixed-spaces-and-tabs: warn
|
||||||
|
no-nested-ternary: warn
|
||||||
|
no-trailing-spaces: warn
|
||||||
|
no-unreachable: error
|
||||||
|
no-unused-expressions: error
|
||||||
|
object-curly-spacing:
|
||||||
|
- error
|
||||||
|
- always
|
||||||
|
padded-blocks:
|
||||||
|
- error
|
||||||
|
- classes: always
|
||||||
|
quotes:
|
||||||
|
- error
|
||||||
|
- single
|
||||||
|
semi: error
|
||||||
|
strict: off
|
||||||
|
valid-typeof: error
|
||||||
|
|
||||||
|
react/jsx-boolean-value: error
|
||||||
|
react/jsx-closing-bracket-location:
|
||||||
|
- error
|
||||||
|
- line-aligned
|
||||||
|
react/jsx-curly-spacing: error
|
||||||
|
react/jsx-equals-spacing: error
|
||||||
|
react/jsx-first-prop-new-line:
|
||||||
|
- error
|
||||||
|
- multiline-multiprop
|
||||||
|
react/jsx-indent:
|
||||||
|
- error
|
||||||
|
- 2
|
||||||
|
react/jsx-no-bind: error
|
||||||
|
react/jsx-no-duplicate-props: error
|
||||||
|
react/jsx-tag-spacing: error
|
||||||
|
react/jsx-wrap-multilines: error
|
||||||
|
react/no-multi-comp: off
|
||||||
|
react/no-string-refs: error
|
||||||
|
react/prop-types: error
|
||||||
|
react/self-closing-comp: error
|
||||||
|
|
||||||
|
jsx-a11y/accessible-emoji: warn
|
||||||
|
jsx-a11y/anchor-has-content: warn
|
||||||
|
jsx-a11y/aria-activedescendant-has-tabindex: warn
|
||||||
|
jsx-a11y/aria-props: warn
|
||||||
|
jsx-a11y/aria-proptypes: warn
|
||||||
|
jsx-a11y/aria-role: warn
|
||||||
|
jsx-a11y/aria-unsupported-elements: warn
|
||||||
|
jsx-a11y/heading-has-content: warn
|
||||||
|
jsx-a11y/href-no-hash: warn
|
||||||
|
jsx-a11y/html-has-lang: warn
|
||||||
|
jsx-a11y/iframe-has-title: warn
|
||||||
|
jsx-a11y/img-has-alt: warn
|
||||||
|
jsx-a11y/img-redundant-alt: warn
|
||||||
|
jsx-a11y/label-has-for: warn
|
||||||
|
jsx-a11y/mouse-events-have-key-events: warn
|
||||||
|
jsx-a11y/no-access-key: warn
|
||||||
|
jsx-a11y/no-distracting-elements: warn
|
||||||
|
jsx-a11y/no-onchange: warn
|
||||||
|
jsx-a11y/no-redundant-roles: warn
|
||||||
|
jsx-a11y/onclick-has-focus: warn
|
||||||
|
jsx-a11y/onclick-has-role: warn
|
||||||
|
jsx-a11y/role-has-required-aria-props: warn
|
||||||
|
jsx-a11y/role-supports-aria-props: warn
|
||||||
|
jsx-a11y/scope: warn
|
||||||
|
jsx-a11y/tabindex-no-positive: warn
|
15
.gitignore
vendored
15
.gitignore
vendored
@@ -19,10 +19,11 @@
|
|||||||
coverage
|
coverage
|
||||||
public/system
|
public/system
|
||||||
public/assets
|
public/assets
|
||||||
|
public/packs
|
||||||
.env
|
.env
|
||||||
.env.production
|
.env.production
|
||||||
node_modules/
|
node_modules/
|
||||||
neo4j/
|
build/
|
||||||
|
|
||||||
# Ignore Vagrant files
|
# Ignore Vagrant files
|
||||||
.vagrant/
|
.vagrant/
|
||||||
@@ -32,6 +33,7 @@ config/deploy/*
|
|||||||
|
|
||||||
# Ignore IDE files
|
# Ignore IDE files
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
# Ignore postgres + redis volume optionally created by docker-compose
|
# Ignore postgres + redis volume optionally created by docker-compose
|
||||||
postgres
|
postgres
|
||||||
@@ -43,3 +45,14 @@ redis
|
|||||||
# Ignore vim files
|
# Ignore vim files
|
||||||
*~
|
*~
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
# Ignore npm debug log
|
||||||
|
npm-debug.log
|
||||||
|
|
||||||
|
# Ignore yarn log files
|
||||||
|
yarn-error.log
|
||||||
|
yarn-debug.log
|
||||||
|
|
||||||
|
# Ignore Docker option files
|
||||||
|
docker-compose.override.yml
|
||||||
|
|
||||||
|
108
.haml-lint.yml
Normal file
108
.haml-lint.yml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# Whether to ignore frontmatter at the beginning of HAML documents for
|
||||||
|
# frameworks such as Jekyll/Middleman
|
||||||
|
skip_frontmatter: false
|
||||||
|
|
||||||
|
exclude:
|
||||||
|
- 'vendor/**/*'
|
||||||
|
- 'spec/**/*'
|
||||||
|
- 'lib/templates/**/*'
|
||||||
|
- 'app/views/kaminari/**/*'
|
||||||
|
|
||||||
|
linters:
|
||||||
|
AltText:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
ClassAttributeWithStaticValue:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
ClassesBeforeIds:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
ConsecutiveComments:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
ConsecutiveSilentScripts:
|
||||||
|
enabled: true
|
||||||
|
max_consecutive: 2
|
||||||
|
|
||||||
|
EmptyObjectReference:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
EmptyScript:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
FinalNewline:
|
||||||
|
enabled: true
|
||||||
|
present: true
|
||||||
|
|
||||||
|
HtmlAttributes:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
ImplicitDiv:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
LeadingCommentSpace:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
LineLength:
|
||||||
|
enabled: false
|
||||||
|
max: 80
|
||||||
|
|
||||||
|
MultilinePipe:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
MultilineScript:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
ObjectReferenceAttributes:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
RuboCop:
|
||||||
|
enabled: true
|
||||||
|
# These cops are incredibly noisy when it comes to HAML templates, so we
|
||||||
|
# ignore them.
|
||||||
|
ignored_cops:
|
||||||
|
- Lint/BlockAlignment
|
||||||
|
- Lint/EndAlignment
|
||||||
|
- Lint/Void
|
||||||
|
- Metrics/BlockLength
|
||||||
|
- Metrics/LineLength
|
||||||
|
- Style/AlignParameters
|
||||||
|
- Style/BlockNesting
|
||||||
|
- Style/ElseAlignment
|
||||||
|
- Style/EndOfLine
|
||||||
|
- Style/FileName
|
||||||
|
- Style/FinalNewline
|
||||||
|
- Style/FrozenStringLiteralComment
|
||||||
|
- Style/IfUnlessModifier
|
||||||
|
- Style/IndentationWidth
|
||||||
|
- Style/Next
|
||||||
|
- Style/TrailingBlankLines
|
||||||
|
- Style/TrailingWhitespace
|
||||||
|
- Style/WhileUntilModifier
|
||||||
|
|
||||||
|
RubyComments:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
SpaceBeforeScript:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
SpaceInsideHashAttributes:
|
||||||
|
enabled: true
|
||||||
|
style: space
|
||||||
|
|
||||||
|
Indentation:
|
||||||
|
enabled: true
|
||||||
|
character: space # or tab
|
||||||
|
|
||||||
|
TagName:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
TrailingWhitespace:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
UnnecessaryInterpolation:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
UnnecessaryStringOutput:
|
||||||
|
enabled: true
|
20
.nanoignore
Normal file
20
.nanoignore
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
.DS_Store
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
.bundle/
|
||||||
|
.cache/
|
||||||
|
config/deploy/*
|
||||||
|
coverage
|
||||||
|
docs/
|
||||||
|
.env
|
||||||
|
log/*.log
|
||||||
|
neo4j/
|
||||||
|
node_modules/
|
||||||
|
public/assets/
|
||||||
|
public/system/
|
||||||
|
spec/
|
||||||
|
storybook/
|
||||||
|
tmp/
|
||||||
|
.vagrant/
|
||||||
|
vendor/bundle/
|
8
.postcssrc.yml
Normal file
8
.postcssrc.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
plugins:
|
||||||
|
postcss-smart-import: {}
|
||||||
|
precss: {}
|
||||||
|
autoprefixer:
|
||||||
|
browsers:
|
||||||
|
- last 2 versions
|
||||||
|
- IE >= 11
|
||||||
|
- iOS >= 9
|
1
.profile
Normal file
1
.profile
Normal file
@@ -0,0 +1 @@
|
|||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/app/.apt/lib/x86_64-linux-gnu:/app/.apt/usr/lib/x86_64-linux-gnu/mesa:/app/.apt/usr/lib/x86_64-linux-gnu/pulseaudio
|
93
.rubocop.yml
93
.rubocop.yml
@@ -1,26 +1,35 @@
|
|||||||
Rails:
|
AllCops:
|
||||||
Enabled: true
|
TargetRubyVersion: 2.3
|
||||||
|
Exclude:
|
||||||
|
- 'spec/**/*'
|
||||||
|
- 'db/**/*'
|
||||||
|
- 'app/views/**/*'
|
||||||
|
- 'config/**/*'
|
||||||
|
- 'bin/*'
|
||||||
|
- 'Rakefile'
|
||||||
|
- 'node_modules/**/*'
|
||||||
|
- 'Vagrantfile'
|
||||||
|
- 'vendor/**/*'
|
||||||
|
|
||||||
Style/PerlBackrefs:
|
Bundler/OrderedGems:
|
||||||
AutoCorrect: false
|
|
||||||
|
|
||||||
Style/ClassAndModuleChildren:
|
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Metrics/BlockNesting:
|
Layout/AccessModifierIndentation:
|
||||||
Max: 2
|
EnforcedStyle: indent
|
||||||
|
|
||||||
Metrics/LineLength:
|
Layout/EmptyLineAfterMagicComment:
|
||||||
AllowURI: true
|
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Metrics/MethodLength:
|
Layout/SpaceInsideHashLiteralBraces:
|
||||||
CountComments: false
|
EnforcedStyle: space
|
||||||
Max: 10
|
|
||||||
|
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 100
|
Max: 100
|
||||||
|
|
||||||
|
Metrics/BlockLength:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/tasks/**/*'
|
||||||
|
|
||||||
Metrics/BlockNesting:
|
Metrics/BlockNesting:
|
||||||
Max: 3
|
Max: 3
|
||||||
|
|
||||||
@@ -31,22 +40,36 @@ Metrics/ClassLength:
|
|||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 15
|
Max: 15
|
||||||
|
|
||||||
|
Metrics/LineLength:
|
||||||
|
AllowURI: true
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
|
CountComments: false
|
||||||
Max: 55
|
Max: 55
|
||||||
|
|
||||||
Metrics/ModuleLength:
|
Metrics/ModuleLength:
|
||||||
CountComments: false
|
CountComments: false
|
||||||
Max: 200
|
Max: 200
|
||||||
|
|
||||||
Metrics/PerceivedComplexity:
|
|
||||||
Max: 10
|
|
||||||
|
|
||||||
Metrics/ParameterLists:
|
Metrics/ParameterLists:
|
||||||
Max: 4
|
Max: 4
|
||||||
CountKeywordArgs: true
|
CountKeywordArgs: true
|
||||||
|
|
||||||
Style/AccessModifierIndentation:
|
Metrics/PerceivedComplexity:
|
||||||
EnforcedStyle: indent
|
Max: 10
|
||||||
|
|
||||||
|
Rails:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Rails/HasAndBelongsToMany:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Rails/SkipsModelValidations:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/ClassAndModuleChildren:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
Style/CollectionMethods:
|
Style/CollectionMethods:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
@@ -62,29 +85,25 @@ Style/DoubleNegation:
|
|||||||
Style/FrozenStringLiteralComment:
|
Style/FrozenStringLiteralComment:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Style/SpaceInsideHashLiteralBraces:
|
Style/GuardClause:
|
||||||
EnforcedStyle: space
|
|
||||||
|
|
||||||
Style/TrailingCommaInLiteral:
|
|
||||||
EnforcedStyleForMultiline: 'comma'
|
|
||||||
|
|
||||||
Style/RegexpLiteral:
|
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/Lambda:
|
Style/Lambda:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Rails/HasAndBelongsToMany:
|
Style/PercentLiteralDelimiters:
|
||||||
|
PreferredDelimiters:
|
||||||
|
'%i': '()'
|
||||||
|
'%w': '()'
|
||||||
|
|
||||||
|
Style/PerlBackrefs:
|
||||||
|
AutoCorrect: false
|
||||||
|
|
||||||
|
Style/RegexpLiteral:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
AllCops:
|
Style/SymbolArray:
|
||||||
TargetRubyVersion: 2.3
|
Enabled: false
|
||||||
Exclude:
|
|
||||||
- 'spec/**/*'
|
Style/TrailingCommaInLiteral:
|
||||||
- 'db/**/*'
|
EnforcedStyleForMultiline: 'comma'
|
||||||
- 'app/views/**/*'
|
|
||||||
- 'config/**/*'
|
|
||||||
- 'bin/*'
|
|
||||||
- 'Rakefile'
|
|
||||||
- 'node_modules/**/*'
|
|
||||||
- 'Vagrantfile'
|
|
||||||
|
264
.scss-lint.yml
Normal file
264
.scss-lint.yml
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
# Linter Documentation:
|
||||||
|
# https://github.com/brigade/scss-lint/blob/v0.42.2/lib/scss_lint/linter/README.md
|
||||||
|
|
||||||
|
scss_files: 'app/javascript/styles/**/*.scss'
|
||||||
|
|
||||||
|
exclude:
|
||||||
|
- app/javascript/styles/reset.scss
|
||||||
|
|
||||||
|
linters:
|
||||||
|
# Reports when you use improper spacing around ! (the "bang") in !default,
|
||||||
|
# !global, !important, and !optional flags.
|
||||||
|
BangFormat:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Whether or not to prefer `border: 0` over `border: none`.
|
||||||
|
BorderZero:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Reports when you define a rule set using a selector with chained classes
|
||||||
|
# (a.k.a. adjoining classes).
|
||||||
|
ChainedClasses:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Prefer hexadecimal color codes over color keywords.
|
||||||
|
# (e.g. `color: green` is a color keyword)
|
||||||
|
ColorKeyword:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Prefer color literals (keywords or hexadecimal codes) to be used only in
|
||||||
|
# variable declarations. They should be referred to via variables everywhere
|
||||||
|
# else.
|
||||||
|
ColorVariable:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Which form of comments to prefer in CSS.
|
||||||
|
Comment:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Reports @debug statements (which you probably left behind accidentally).
|
||||||
|
DebugStatement:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Rule sets should be ordered as follows:
|
||||||
|
# - @extend declarations
|
||||||
|
# - @include declarations without inner @content
|
||||||
|
# - properties, @include declarations with inner @content
|
||||||
|
# - nested rule sets.
|
||||||
|
DeclarationOrder:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# `scss-lint:disable` control comments should be preceded by a comment
|
||||||
|
# explaining why these linters are being disabled for this file.
|
||||||
|
# See https://github.com/brigade/scss-lint#disabling-linters-via-source for
|
||||||
|
# more information.
|
||||||
|
DisableLinterReason:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Reports when you define the same property twice in a single rule set.
|
||||||
|
DuplicateProperty:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Separate rule, function, and mixin declarations with empty lines.
|
||||||
|
EmptyLineBetweenBlocks:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Reports when you have an empty rule set.
|
||||||
|
EmptyRule:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Reports when you have an @extend directive.
|
||||||
|
ExtendDirective:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Files should always have a final newline. This results in better diffs
|
||||||
|
# when adding lines to the file, since SCM systems such as git won't
|
||||||
|
# think that you touched the last line.
|
||||||
|
FinalNewline:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# HEX colors should use three-character values where possible.
|
||||||
|
HexLength:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# HEX color values should use lower-case colors to differentiate between
|
||||||
|
# letters and numbers, e.g. `#E3E3E3` vs. `#e3e3e3`.
|
||||||
|
HexNotation:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Avoid using ID selectors.
|
||||||
|
IdSelector:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# The basenames of @imported SCSS partials should not begin with an
|
||||||
|
# underscore and should not include the filename extension.
|
||||||
|
ImportPath:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Avoid using !important in properties. It is usually indicative of a
|
||||||
|
# misunderstanding of CSS specificity and can lead to brittle code.
|
||||||
|
ImportantRule:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Indentation should always be done in increments of 2 spaces.
|
||||||
|
Indentation:
|
||||||
|
enabled: true
|
||||||
|
width: 2
|
||||||
|
|
||||||
|
# Don't write leading zeros for numeric values with a decimal point.
|
||||||
|
LeadingZero:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Reports when you define the same selector twice in a single sheet.
|
||||||
|
MergeableSelector:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Functions, mixins, variables, and placeholders should be declared
|
||||||
|
# with all lowercase letters and hyphens instead of underscores.
|
||||||
|
NameFormat:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Avoid nesting selectors too deeply.
|
||||||
|
NestingDepth:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Always use placeholder selectors in @extend.
|
||||||
|
PlaceholderInExtend:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Sort properties in a strict order.
|
||||||
|
PropertySortOrder:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Reports when you use an unknown or disabled CSS property
|
||||||
|
# (ignoring vendor-prefixed properties).
|
||||||
|
PropertySpelling:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Configure which units are allowed for property values.
|
||||||
|
PropertyUnits:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Pseudo-elements, like ::before, and ::first-letter, should be declared
|
||||||
|
# with two colons. Pseudo-classes, like :hover and :first-child, should
|
||||||
|
# be declared with one colon.
|
||||||
|
PseudoElement:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Avoid qualifying elements in selectors (also known as "tag-qualifying").
|
||||||
|
QualifyingElement:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Don't write selectors with a depth of applicability greater than 3.
|
||||||
|
SelectorDepth:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Selectors should always use hyphenated-lowercase, rather than camelCase or
|
||||||
|
# snake_case.
|
||||||
|
SelectorFormat:
|
||||||
|
enabled: false
|
||||||
|
convention: hyphenated_lowercase
|
||||||
|
|
||||||
|
# Prefer the shortest shorthand form possible for properties that support it.
|
||||||
|
Shorthand:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Each property should have its own line, except in the special case of
|
||||||
|
# single line rulesets.
|
||||||
|
SingleLinePerProperty:
|
||||||
|
enabled: true
|
||||||
|
allow_single_line_rule_sets: true
|
||||||
|
|
||||||
|
# Split selectors onto separate lines after each comma, and have each
|
||||||
|
# individual selector occupy a single line.
|
||||||
|
SingleLinePerSelector:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Commas in lists should be followed by a space.
|
||||||
|
SpaceAfterComma:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Properties should be formatted with a single space separating the colon
|
||||||
|
# from the property's value.
|
||||||
|
SpaceAfterPropertyColon:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Properties should be formatted with no space between the name and the
|
||||||
|
# colon.
|
||||||
|
SpaceAfterPropertyName:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Variables should be formatted with a single space separating the colon
|
||||||
|
# from the variable's value.
|
||||||
|
SpaceAfterVariableColon:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Variables should be formatted with no space between the name and the
|
||||||
|
# colon.
|
||||||
|
SpaceAfterVariableName:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Operators should be formatted with a single space on both sides of an
|
||||||
|
# infix operator.
|
||||||
|
SpaceAroundOperator:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Opening braces should be preceded by a single space.
|
||||||
|
SpaceBeforeBrace:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Parentheses should not be padded with spaces.
|
||||||
|
SpaceBetweenParens:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Enforces that string literals should be written with a consistent form
|
||||||
|
# of quotes (single or double).
|
||||||
|
StringQuotes:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Property values, @extend, @include, and @import directives, and variable
|
||||||
|
# declarations should always end with a semicolon.
|
||||||
|
TrailingSemicolon:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Reports lines containing trailing whitespace.
|
||||||
|
TrailingWhitespace:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Don't write trailing zeros for numeric values with a decimal point.
|
||||||
|
TrailingZero:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Don't use the `all` keyword to specify transition properties.
|
||||||
|
TransitionAll:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Numeric values should not contain unnecessary fractional portions.
|
||||||
|
UnnecessaryMantissa:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Do not use parent selector references (&) when they would otherwise
|
||||||
|
# be unnecessary.
|
||||||
|
UnnecessaryParentReference:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# URLs should be valid and not contain protocols or domain names.
|
||||||
|
UrlFormat:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# URLs should always be enclosed within quotes.
|
||||||
|
UrlQuotes:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Properties, like color and font, are easier to read and maintain
|
||||||
|
# when defined using variables rather than literals.
|
||||||
|
VariableForProperty:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Avoid vendor prefixes. Or rather: don't write them yourself.
|
||||||
|
VendorPrefix:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Omit length units on zero values, e.g. `0px` vs. `0`.
|
||||||
|
ZeroUnit:
|
||||||
|
enabled: true
|
25
.travis.yml
25
.travis.yml
@@ -3,7 +3,9 @@ cache:
|
|||||||
bundler: true
|
bundler: true
|
||||||
yarn: true
|
yarn: true
|
||||||
directories:
|
directories:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
- public/assets
|
||||||
|
- public/packs
|
||||||
dist: trusty
|
dist: trusty
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
@@ -15,7 +17,10 @@ env:
|
|||||||
- LOCAL_DOMAIN=cb6e6126.ngrok.io
|
- LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||||
- LOCAL_HTTPS=true
|
- LOCAL_HTTPS=true
|
||||||
- RAILS_ENV=test
|
- RAILS_ENV=test
|
||||||
- CXX=g++-4.8
|
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true
|
||||||
|
- PARALLEL_TEST_PROCESSORS=2
|
||||||
|
- "PATH=$HOME:$PATH"
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
postgresql: 9.4
|
postgresql: 9.4
|
||||||
apt:
|
apt:
|
||||||
@@ -23,8 +28,10 @@ addons:
|
|||||||
- ubuntu-toolchain-r-test
|
- ubuntu-toolchain-r-test
|
||||||
- trusty-media
|
- trusty-media
|
||||||
packages:
|
packages:
|
||||||
- g++-4.8
|
|
||||||
- ffmpeg
|
- ffmpeg
|
||||||
|
- g++-6
|
||||||
|
- libprotobuf-dev
|
||||||
|
- protobuf-compiler
|
||||||
|
|
||||||
rvm:
|
rvm:
|
||||||
- 2.3.4
|
- 2.3.4
|
||||||
@@ -33,18 +40,18 @@ rvm:
|
|||||||
services:
|
services:
|
||||||
- redis-server
|
- redis-server
|
||||||
|
|
||||||
bundler_args: --without development production --retry=3 --jobs=3
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- nvm install
|
- nvm install
|
||||||
- npm install -g yarn
|
- npm install -g yarn
|
||||||
- bundle install
|
- bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16
|
||||||
- yarn install
|
- yarn install
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- bundle exec rails db:create db:migrate
|
- bundle exec rake parallel:create parallel:load_schema parallel:prepare
|
||||||
|
- bundle exec rails assets:precompile
|
||||||
|
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- bundle exec rspec
|
- travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec
|
||||||
- npm test
|
- npm test
|
||||||
- i18n-tasks unused
|
- bundle exec i18n-tasks unused
|
||||||
|
5
Aptfile
Normal file
5
Aptfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
protobuf-compiler
|
||||||
|
libprotobuf-dev
|
||||||
|
ffmpeg
|
||||||
|
libxdamage1
|
||||||
|
libxfixes3
|
2
Capfile
2
Capfile
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
require 'capistrano/setup'
|
require 'capistrano/setup'
|
||||||
require 'capistrano/deploy'
|
require 'capistrano/deploy'
|
||||||
require 'capistrano/scm/git'
|
require 'capistrano/scm/git'
|
||||||
@@ -8,7 +9,6 @@ require 'capistrano/rbenv'
|
|||||||
require 'capistrano/bundler'
|
require 'capistrano/bundler'
|
||||||
require 'capistrano/yarn'
|
require 'capistrano/yarn'
|
||||||
require 'capistrano/rails/assets'
|
require 'capistrano/rails/assets'
|
||||||
require 'capistrano/faster_assets'
|
|
||||||
require 'capistrano/rails/migrations'
|
require 'capistrano/rails/migrations'
|
||||||
|
|
||||||
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
|
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
|
||||||
|
52
Dockerfile
52
Dockerfile
@@ -3,42 +3,52 @@ FROM ruby:2.4.1-alpine
|
|||||||
LABEL maintainer="https://github.com/tootsuite/mastodon" \
|
LABEL maintainer="https://github.com/tootsuite/mastodon" \
|
||||||
description="A GNU Social-compatible microblogging server"
|
description="A GNU Social-compatible microblogging server"
|
||||||
|
|
||||||
ENV RAILS_ENV=production \
|
ENV UID=991 GID=991 \
|
||||||
NODE_ENV=production
|
RAILS_SERVE_STATIC_FILES=true \
|
||||||
|
RAILS_ENV=production NODE_ENV=production
|
||||||
|
|
||||||
EXPOSE 3000 4000
|
EXPOSE 3000 4000
|
||||||
|
|
||||||
WORKDIR /mastodon
|
WORKDIR /mastodon
|
||||||
|
|
||||||
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
|
|
||||||
|
|
||||||
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
|
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
|
||||||
&& BUILD_DEPS=" \
|
&& apk -U upgrade \
|
||||||
postgresql-dev \
|
&& apk add -t build-dependencies \
|
||||||
|
build-base \
|
||||||
libxml2-dev \
|
libxml2-dev \
|
||||||
libxslt-dev \
|
libxslt-dev \
|
||||||
|
postgresql-dev \
|
||||||
|
protobuf-dev \
|
||||||
python \
|
python \
|
||||||
build-base" \
|
&& apk add \
|
||||||
&& apk -U upgrade && apk add \
|
ca-certificates \
|
||||||
$BUILD_DEPS \
|
ffmpeg \
|
||||||
nodejs@edge \
|
file \
|
||||||
nodejs-npm@edge \
|
git \
|
||||||
|
imagemagick@edge \
|
||||||
libpq \
|
libpq \
|
||||||
libxml2 \
|
libxml2 \
|
||||||
libxslt \
|
libxslt \
|
||||||
ffmpeg \
|
nodejs-npm@edge \
|
||||||
file \
|
nodejs@edge \
|
||||||
imagemagick@edge \
|
protobuf \
|
||||||
ca-certificates \
|
su-exec \
|
||||||
|
tini \
|
||||||
&& npm install -g npm@3 && npm install -g yarn \
|
&& npm install -g npm@3 && npm install -g yarn \
|
||||||
&& bundle install --deployment --without test development \
|
|
||||||
&& yarn --ignore-optional \
|
|
||||||
&& yarn cache clean \
|
|
||||||
&& npm -g cache clean \
|
|
||||||
&& update-ca-certificates \
|
&& update-ca-certificates \
|
||||||
&& apk del $BUILD_DEPS \
|
|
||||||
&& rm -rf /tmp/* /var/cache/apk/*
|
&& rm -rf /tmp/* /var/cache/apk/*
|
||||||
|
|
||||||
|
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
|
||||||
|
|
||||||
|
RUN bundle install --deployment --without test development \
|
||||||
|
&& yarn --ignore-optional --pure-lockfile
|
||||||
|
|
||||||
COPY . /mastodon
|
COPY . /mastodon
|
||||||
|
|
||||||
VOLUME /mastodon/public/system /mastodon/public/assets
|
COPY docker_entrypoint.sh /usr/local/bin/run
|
||||||
|
|
||||||
|
RUN chmod +x /usr/local/bin/run
|
||||||
|
|
||||||
|
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/run"]
|
||||||
|
163
Gemfile
163
Gemfile
@@ -3,103 +3,104 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
ruby '>= 2.3.0', '< 2.5.0'
|
ruby '>= 2.3.0', '< 2.5.0'
|
||||||
|
|
||||||
gem 'pkg-config'
|
gem 'pkg-config', '~> 1.2'
|
||||||
|
|
||||||
gem 'rails', '~> 5.0.2'
|
gem 'puma', '~> 3.8'
|
||||||
gem 'sass-rails', '~> 5.0'
|
gem 'rails', '~> 5.1.0'
|
||||||
gem 'uglifier', '>= 1.3.0'
|
gem 'uglifier', '~> 3.2'
|
||||||
gem 'jquery-rails'
|
|
||||||
gem 'puma'
|
|
||||||
|
|
||||||
gem 'hamlit-rails'
|
gem 'hamlit-rails', '~> 0.2'
|
||||||
gem 'pg'
|
gem 'pg', '~> 0.20'
|
||||||
gem 'pghero'
|
gem 'pghero', '~> 1.7'
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails', '~> 2.2'
|
||||||
gem 'font-awesome-rails'
|
|
||||||
gem 'best_in_place', '~> 3.0.1'
|
|
||||||
|
|
||||||
|
gem 'aws-sdk', '~> 2.9'
|
||||||
gem 'paperclip', '~> 5.1'
|
gem 'paperclip', '~> 5.1'
|
||||||
gem 'paperclip-av-transcoder'
|
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||||
gem 'aws-sdk', '>= 2.0'
|
|
||||||
|
|
||||||
gem 'addressable'
|
gem 'addressable', '~> 2.5'
|
||||||
gem 'devise'
|
gem 'bootsnap'
|
||||||
gem 'devise-two-factor'
|
gem 'cld3', '~> 3.1'
|
||||||
gem 'doorkeeper'
|
gem 'devise', '~> 4.2'
|
||||||
gem 'fast_blank'
|
gem 'devise-two-factor', '~> 3.0'
|
||||||
gem 'goldfinger'
|
gem 'doorkeeper', '~> 4.2'
|
||||||
gem 'hiredis'
|
gem 'fast_blank', '~> 1.0'
|
||||||
gem 'htmlentities'
|
gem 'goldfinger', '~> 1.2'
|
||||||
gem 'http'
|
gem 'hiredis', '~> 0.6'
|
||||||
gem 'http_accept_language'
|
gem 'redis-namespace', '~> 1.5'
|
||||||
gem 'httplog'
|
gem 'htmlentities', '~> 4.3'
|
||||||
gem 'kaminari'
|
gem 'http', '~> 2.2'
|
||||||
gem 'link_header'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
gem 'local_time'
|
gem 'httplog', '~> 0.99'
|
||||||
gem 'nokogiri'
|
gem 'kaminari', '~> 1.0'
|
||||||
gem 'oj'
|
gem 'link_header', '~> 0.0'
|
||||||
gem 'ostatus2', '~> 1.1'
|
gem 'nokogiri', '~> 1.7'
|
||||||
gem 'ox'
|
gem 'oj', '~> 3.0'
|
||||||
gem 'rabl'
|
gem 'ostatus2', '~> 2.0'
|
||||||
gem 'rack-attack'
|
gem 'ox', '~> 2.5'
|
||||||
gem 'rack-cors', require: 'rack/cors'
|
gem 'pundit', '~> 1.1'
|
||||||
gem 'rack-timeout'
|
gem 'rabl', '~> 0.13'
|
||||||
gem 'rails-i18n'
|
gem 'rack-attack', '~> 5.0'
|
||||||
gem 'rails-settings-cached'
|
gem 'rack-cors', '~> 0.4', require: 'rack/cors'
|
||||||
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
|
gem 'rack-timeout', '~> 0.4'
|
||||||
gem 'rqrcode'
|
gem 'rails-i18n', '~> 5.0'
|
||||||
gem 'ruby-oembed', require: 'oembed'
|
gem 'rails-settings-cached', '~> 0.6'
|
||||||
gem 'sanitize'
|
gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis']
|
||||||
gem 'sidekiq'
|
gem 'rqrcode', '~> 0.10'
|
||||||
gem 'sidekiq-unique-jobs'
|
gem 'ruby-oembed', '~> 0.12', require: 'oembed'
|
||||||
gem 'simple-navigation'
|
gem 'sanitize', '~> 4.4'
|
||||||
gem 'simple_form'
|
gem 'sidekiq', '~> 5.0'
|
||||||
gem 'sprockets-rails', require: 'sprockets/railtie'
|
gem 'sidekiq-scheduler', '~> 2.1'
|
||||||
gem 'statsd-instrument'
|
gem 'sidekiq-unique-jobs', '~> 5.0'
|
||||||
gem 'twitter-text'
|
gem 'sidekiq-bulk', '~>0.1.1'
|
||||||
gem 'tzinfo-data'
|
gem 'simple-navigation', '~> 4.0'
|
||||||
gem 'whatlanguage'
|
gem 'simple_form', '~> 3.4'
|
||||||
|
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||||
gem 'react-rails'
|
gem 'statsd-instrument', '~> 2.1'
|
||||||
gem 'browserify-rails'
|
gem 'twitter-text', '~> 1.14'
|
||||||
gem 'autoprefixer-rails'
|
gem 'tzinfo-data', '~> 1.2017'
|
||||||
|
gem 'webpacker', '~> 1.2'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'rspec-rails'
|
gem 'fabrication', '~> 2.16'
|
||||||
gem 'pry-rails'
|
gem 'fuubar', '~> 2.2'
|
||||||
gem 'fuubar'
|
gem 'i18n-tasks', '~> 0.9', require: false
|
||||||
gem 'fabrication'
|
gem 'pry-rails', '~> 0.3'
|
||||||
gem 'i18n-tasks', '~> 0.9.6'
|
gem 'rspec-rails', '~> 3.6'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'capybara'
|
gem 'capybara', '~> 2.14'
|
||||||
gem 'faker'
|
gem 'climate_control', '~> 0.2'
|
||||||
gem 'microformats2'
|
gem 'faker', '~> 1.7'
|
||||||
gem 'rails-controller-testing'
|
gem 'microformats2', '~> 3.0'
|
||||||
gem 'rspec-sidekiq'
|
gem 'rails-controller-testing', '~> 1.0'
|
||||||
gem 'simplecov', require: false
|
gem 'rspec-sidekiq', '~> 3.0'
|
||||||
gem 'webmock'
|
gem 'simplecov', '~> 0.14', require: false
|
||||||
|
gem 'webmock', '~> 3.0'
|
||||||
|
gem 'parallel_tests', '~> 2.14'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
gem 'active_record_query_trace', '~> 1.5'
|
||||||
|
gem 'annotate', '~> 2.7'
|
||||||
|
gem 'better_errors', '~> 2.1'
|
||||||
|
gem 'binding_of_caller', '~> 0.7'
|
||||||
|
gem 'bullet', '~> 5.5'
|
||||||
|
gem 'letter_opener', '~> 1.4'
|
||||||
|
gem 'letter_opener_web', '~> 1.3'
|
||||||
gem 'rubocop', require: false
|
gem 'rubocop', require: false
|
||||||
gem 'better_errors'
|
gem 'brakeman', '~> 3.6', require: false
|
||||||
gem 'binding_of_caller'
|
gem 'bundler-audit', '~> 0.5', require: false
|
||||||
gem 'letter_opener'
|
gem 'scss_lint', '~> 0.53', require: false
|
||||||
gem 'letter_opener_web'
|
|
||||||
gem 'bullet'
|
|
||||||
gem 'active_record_query_trace'
|
|
||||||
|
|
||||||
gem 'capistrano', '3.8.0'
|
gem 'capistrano', '~> 3.8'
|
||||||
gem 'capistrano-rails'
|
gem 'capistrano-rails', '~> 1.2'
|
||||||
gem 'capistrano-rbenv'
|
gem 'capistrano-rbenv', '~> 2.1'
|
||||||
gem 'capistrano-yarn'
|
gem 'capistrano-yarn', '~> 2.0'
|
||||||
gem 'capistrano-faster-assets', '~> 1.0'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :production do
|
group :production do
|
||||||
gem 'rails_12factor'
|
gem 'lograge', '~> 0.5'
|
||||||
gem 'redis-rails'
|
gem 'redis-rails', '~> 5.0'
|
||||||
gem 'lograge'
|
|
||||||
end
|
end
|
||||||
|
486
Gemfile.lock
486
Gemfile.lock
@@ -1,40 +1,40 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (5.0.2)
|
actioncable (5.1.1)
|
||||||
actionpack (= 5.0.2)
|
actionpack (= 5.1.1)
|
||||||
nio4r (>= 1.2, < 3.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (~> 0.6.1)
|
websocket-driver (~> 0.6.1)
|
||||||
actionmailer (5.0.2)
|
actionmailer (5.1.1)
|
||||||
actionpack (= 5.0.2)
|
actionpack (= 5.1.1)
|
||||||
actionview (= 5.0.2)
|
actionview (= 5.1.1)
|
||||||
activejob (= 5.0.2)
|
activejob (= 5.1.1)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.0.2)
|
actionpack (5.1.1)
|
||||||
actionview (= 5.0.2)
|
actionview (= 5.1.1)
|
||||||
activesupport (= 5.0.2)
|
activesupport (= 5.1.1)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
rack-test (~> 0.6.3)
|
rack-test (~> 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.0.2)
|
actionview (5.1.1)
|
||||||
activesupport (= 5.0.2)
|
activesupport (= 5.1.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubis (~> 2.7.0)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||||
active_record_query_trace (1.5.4)
|
active_record_query_trace (1.5.4)
|
||||||
activejob (5.0.2)
|
activejob (5.1.1)
|
||||||
activesupport (= 5.0.2)
|
activesupport (= 5.1.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.0.2)
|
activemodel (5.1.1)
|
||||||
activesupport (= 5.0.2)
|
activesupport (= 5.1.1)
|
||||||
activerecord (5.0.2)
|
activerecord (5.1.1)
|
||||||
activemodel (= 5.0.2)
|
activemodel (= 5.1.1)
|
||||||
activesupport (= 5.0.2)
|
activesupport (= 5.1.1)
|
||||||
arel (~> 7.0)
|
arel (~> 8.0)
|
||||||
activesupport (5.0.2)
|
activesupport (5.1.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (~> 0.7)
|
i18n (~> 0.7)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
@@ -43,45 +43,41 @@ GEM
|
|||||||
public_suffix (~> 2.0, >= 2.0.2)
|
public_suffix (~> 2.0, >= 2.0.2)
|
||||||
airbrussh (1.2.0)
|
airbrussh (1.2.0)
|
||||||
sshkit (>= 1.6.1, != 1.7.0)
|
sshkit (>= 1.6.1, != 1.7.0)
|
||||||
arel (7.1.4)
|
annotate (2.7.2)
|
||||||
|
activerecord (>= 3.2, < 6.0)
|
||||||
|
rake (>= 10.4, < 13.0)
|
||||||
|
arel (8.0.0)
|
||||||
ast (2.3.0)
|
ast (2.3.0)
|
||||||
attr_encrypted (3.0.3)
|
attr_encrypted (3.0.3)
|
||||||
encryptor (~> 3.0.0)
|
encryptor (~> 3.0.0)
|
||||||
autoprefixer-rails (6.7.7.2)
|
|
||||||
execjs
|
|
||||||
av (0.9.0)
|
av (0.9.0)
|
||||||
cocaine (~> 0.5.3)
|
cocaine (~> 0.5.3)
|
||||||
aws-sdk (2.9.12)
|
aws-sdk (2.9.37)
|
||||||
aws-sdk-resources (= 2.9.12)
|
aws-sdk-resources (= 2.9.37)
|
||||||
aws-sdk-core (2.9.12)
|
aws-sdk-core (2.9.37)
|
||||||
aws-sigv4 (~> 1.0)
|
aws-sigv4 (~> 1.0)
|
||||||
jmespath (~> 1.0)
|
jmespath (~> 1.0)
|
||||||
aws-sdk-resources (2.9.12)
|
aws-sdk-resources (2.9.37)
|
||||||
aws-sdk-core (= 2.9.12)
|
aws-sdk-core (= 2.9.37)
|
||||||
aws-sigv4 (1.0.0)
|
aws-sigv4 (1.0.0)
|
||||||
babel-source (5.8.35)
|
|
||||||
babel-transpiler (0.7.0)
|
|
||||||
babel-source (>= 4.0, < 6)
|
|
||||||
execjs (~> 2.0)
|
|
||||||
bcrypt (3.1.11)
|
bcrypt (3.1.11)
|
||||||
best_in_place (3.0.3)
|
|
||||||
actionpack (>= 3.2)
|
|
||||||
railties (>= 3.2)
|
|
||||||
better_errors (2.1.1)
|
better_errors (2.1.1)
|
||||||
coderay (>= 1.0.0)
|
coderay (>= 1.0.0)
|
||||||
erubis (>= 2.6.6)
|
erubis (>= 2.6.6)
|
||||||
rack (>= 0.9.0)
|
rack (>= 0.9.0)
|
||||||
binding_of_caller (0.7.2)
|
binding_of_caller (0.7.2)
|
||||||
debug_inspector (>= 0.0.1)
|
debug_inspector (>= 0.0.1)
|
||||||
browserify-rails (4.1.0)
|
bootsnap (1.0.0)
|
||||||
addressable (>= 2.4.0)
|
msgpack (~> 1.0)
|
||||||
railties (>= 4.0.0, < 5.1)
|
brakeman (3.6.2)
|
||||||
sprockets (>= 3.6.0)
|
|
||||||
builder (3.2.3)
|
builder (3.2.3)
|
||||||
bullet (5.5.1)
|
bullet (5.5.1)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
uniform_notifier (~> 1.10.0)
|
uniform_notifier (~> 1.10.0)
|
||||||
capistrano (3.8.0)
|
bundler-audit (0.5.0)
|
||||||
|
bundler (~> 1.2)
|
||||||
|
thor (~> 0.18)
|
||||||
|
capistrano (3.8.1)
|
||||||
airbrussh (>= 1.0.0)
|
airbrussh (>= 1.0.0)
|
||||||
i18n
|
i18n
|
||||||
rake (>= 10.0.0)
|
rake (>= 10.0.0)
|
||||||
@@ -89,9 +85,7 @@ GEM
|
|||||||
capistrano-bundler (1.2.0)
|
capistrano-bundler (1.2.0)
|
||||||
capistrano (~> 3.1)
|
capistrano (~> 3.1)
|
||||||
sshkit (~> 1.2)
|
sshkit (~> 1.2)
|
||||||
capistrano-faster-assets (1.0.2)
|
capistrano-rails (1.3.0)
|
||||||
capistrano (>= 3.1)
|
|
||||||
capistrano-rails (1.2.3)
|
|
||||||
capistrano (~> 3.1)
|
capistrano (~> 3.1)
|
||||||
capistrano-bundler (~> 1.1)
|
capistrano-bundler (~> 1.1)
|
||||||
capistrano-rbenv (2.1.1)
|
capistrano-rbenv (2.1.1)
|
||||||
@@ -99,7 +93,7 @@ GEM
|
|||||||
sshkit (~> 1.3)
|
sshkit (~> 1.3)
|
||||||
capistrano-yarn (2.0.2)
|
capistrano-yarn (2.0.2)
|
||||||
capistrano (~> 3.0)
|
capistrano (~> 3.0)
|
||||||
capybara (2.13.0)
|
capybara (2.14.2)
|
||||||
addressable
|
addressable
|
||||||
mime-types (>= 1.16)
|
mime-types (>= 1.16)
|
||||||
nokogiri (>= 1.3.3)
|
nokogiri (>= 1.3.3)
|
||||||
@@ -107,28 +101,23 @@ GEM
|
|||||||
rack-test (>= 0.5.4)
|
rack-test (>= 0.5.4)
|
||||||
xpath (~> 2.0)
|
xpath (~> 2.0)
|
||||||
chunky_png (1.3.8)
|
chunky_png (1.3.8)
|
||||||
climate_control (0.1.0)
|
cld3 (3.1.2)
|
||||||
|
ffi (>= 1.1.0, < 1.10.0)
|
||||||
|
climate_control (0.2.0)
|
||||||
cocaine (0.5.8)
|
cocaine (0.5.8)
|
||||||
climate_control (>= 0.0.3, < 1.0)
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
coderay (1.1.1)
|
coderay (1.1.1)
|
||||||
coffee-rails (4.2.1)
|
|
||||||
coffee-script (>= 2.2.0)
|
|
||||||
railties (>= 4.0.0, < 5.2.x)
|
|
||||||
coffee-script (2.4.1)
|
|
||||||
coffee-script-source
|
|
||||||
execjs
|
|
||||||
coffee-script-source (1.12.2)
|
|
||||||
colorize (0.8.1)
|
colorize (0.8.1)
|
||||||
concurrent-ruby (1.0.5)
|
concurrent-ruby (1.0.5)
|
||||||
connection_pool (2.2.1)
|
connection_pool (2.2.1)
|
||||||
crack (0.4.3)
|
crack (0.4.3)
|
||||||
safe_yaml (~> 1.0.0)
|
safe_yaml (~> 1.0.0)
|
||||||
crass (1.0.2)
|
crass (1.0.2)
|
||||||
debug_inspector (0.0.2)
|
debug_inspector (0.0.3)
|
||||||
devise (4.2.1)
|
devise (4.3.0)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
railties (>= 4.1.0, < 5.1)
|
railties (>= 4.1.0, < 5.2)
|
||||||
responders
|
responders
|
||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
devise-two-factor (3.0.0)
|
devise-two-factor (3.0.0)
|
||||||
@@ -141,25 +130,27 @@ GEM
|
|||||||
docile (1.1.5)
|
docile (1.1.5)
|
||||||
domain_name (0.5.20170404)
|
domain_name (0.5.20170404)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
doorkeeper (4.2.5)
|
doorkeeper (4.2.6)
|
||||||
railties (>= 4.2)
|
railties (>= 4.2)
|
||||||
dotenv (2.2.0)
|
dotenv (2.2.1)
|
||||||
dotenv-rails (2.2.0)
|
dotenv-rails (2.2.1)
|
||||||
dotenv (= 2.2.0)
|
dotenv (= 2.2.1)
|
||||||
railties (>= 3.2, < 5.1)
|
railties (>= 3.2, < 5.2)
|
||||||
easy_translate (0.5.0)
|
easy_translate (0.5.0)
|
||||||
json
|
json
|
||||||
thread
|
thread
|
||||||
thread_safe
|
thread_safe
|
||||||
encryptor (3.0.0)
|
encryptor (3.0.0)
|
||||||
|
erubi (1.6.0)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
|
et-orbi (1.0.4)
|
||||||
|
tzinfo
|
||||||
execjs (2.7.0)
|
execjs (2.7.0)
|
||||||
fabrication (2.16.1)
|
fabrication (2.16.1)
|
||||||
faker (1.7.3)
|
faker (1.7.3)
|
||||||
i18n (~> 0.5)
|
i18n (~> 0.5)
|
||||||
fast_blank (1.0.0)
|
fast_blank (1.0.0)
|
||||||
font-awesome-rails (4.7.0.1)
|
ffi (1.9.18)
|
||||||
railties (>= 3.2, < 5.1)
|
|
||||||
fuubar (2.2.0)
|
fuubar (2.2.0)
|
||||||
rspec-core (~> 3.0)
|
rspec-core (~> 3.0)
|
||||||
ruby-progressbar (~> 1.4)
|
ruby-progressbar (~> 1.4)
|
||||||
@@ -178,7 +169,7 @@ GEM
|
|||||||
activesupport (>= 4.0.1)
|
activesupport (>= 4.0.1)
|
||||||
hamlit (>= 1.2.0)
|
hamlit (>= 1.2.0)
|
||||||
railties (>= 4.0.1)
|
railties (>= 4.0.1)
|
||||||
hashdiff (0.3.2)
|
hashdiff (0.3.4)
|
||||||
highline (1.7.8)
|
highline (1.7.8)
|
||||||
hiredis (0.6.1)
|
hiredis (0.6.1)
|
||||||
htmlentities (4.3.4)
|
htmlentities (4.3.4)
|
||||||
@@ -189,14 +180,14 @@ GEM
|
|||||||
http_parser.rb (~> 0.6.0)
|
http_parser.rb (~> 0.6.0)
|
||||||
http-cookie (1.0.3)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
http-form_data (1.0.1)
|
http-form_data (1.0.3)
|
||||||
http_accept_language (2.1.0)
|
http_accept_language (2.1.0)
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.6.0)
|
||||||
httplog (0.99.3)
|
httplog (0.99.3)
|
||||||
colorize
|
colorize
|
||||||
rack
|
rack
|
||||||
i18n (0.8.1)
|
i18n (0.8.4)
|
||||||
i18n-tasks (0.9.13)
|
i18n-tasks (0.9.15)
|
||||||
activesupport (>= 4.0.2)
|
activesupport (>= 4.0.2)
|
||||||
ast (>= 2.1.0)
|
ast (>= 2.1.0)
|
||||||
easy_translate (>= 0.5.0)
|
easy_translate (>= 0.5.0)
|
||||||
@@ -207,10 +198,6 @@ GEM
|
|||||||
rainbow (~> 2.2)
|
rainbow (~> 2.2)
|
||||||
terminal-table (>= 1.5.1)
|
terminal-table (>= 1.5.1)
|
||||||
jmespath (1.3.1)
|
jmespath (1.3.1)
|
||||||
jquery-rails (4.3.1)
|
|
||||||
rails-dom-testing (>= 1, < 3)
|
|
||||||
railties (>= 4.2.0)
|
|
||||||
thor (>= 0.14, < 2.0)
|
|
||||||
json (2.1.0)
|
json (2.1.0)
|
||||||
kaminari (1.0.1)
|
kaminari (1.0.1)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
@@ -233,44 +220,43 @@ GEM
|
|||||||
letter_opener (~> 1.0)
|
letter_opener (~> 1.0)
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
link_header (0.0.8)
|
link_header (0.0.8)
|
||||||
local_time (1.0.3)
|
lograge (0.5.1)
|
||||||
coffee-rails
|
actionpack (>= 4, < 5.2)
|
||||||
lograge (0.4.1)
|
activesupport (>= 4, < 5.2)
|
||||||
actionpack (>= 4, < 5.1)
|
railties (>= 4, < 5.2)
|
||||||
activesupport (>= 4, < 5.1)
|
|
||||||
railties (>= 4, < 5.1)
|
|
||||||
loofah (2.0.3)
|
loofah (2.0.3)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.6.5)
|
mail (2.6.6)
|
||||||
mime-types (>= 1.16, < 4)
|
mime-types (>= 1.16, < 4)
|
||||||
method_source (0.8.2)
|
method_source (0.8.2)
|
||||||
microformats2 (2.1.0)
|
microformats2 (3.1.0)
|
||||||
activesupport
|
|
||||||
json
|
json
|
||||||
nokogiri
|
nokogiri
|
||||||
mime-types (3.1)
|
mime-types (3.1)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2016.0521)
|
mime-types-data (3.2016.0521)
|
||||||
mimemagic (0.3.2)
|
mimemagic (0.3.2)
|
||||||
mini_portile2 (2.1.0)
|
mini_portile2 (2.2.0)
|
||||||
minitest (5.10.1)
|
minitest (5.10.2)
|
||||||
|
msgpack (1.1.0)
|
||||||
|
multi_json (1.12.1)
|
||||||
net-scp (1.2.1)
|
net-scp (1.2.1)
|
||||||
net-ssh (>= 2.6.5)
|
net-ssh (>= 2.6.5)
|
||||||
net-ssh (4.1.0)
|
net-ssh (4.1.0)
|
||||||
nio4r (2.0.0)
|
nio4r (2.1.0)
|
||||||
nokogiri (1.7.1)
|
nokogiri (1.8.0)
|
||||||
mini_portile2 (~> 2.1.0)
|
mini_portile2 (~> 2.2.0)
|
||||||
nokogumbo (1.4.10)
|
nokogumbo (1.4.13)
|
||||||
nokogiri
|
nokogiri
|
||||||
oj (3.0.2)
|
oj (3.1.0)
|
||||||
openssl (2.0.3)
|
openssl (2.0.3)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ostatus2 (1.1.0)
|
ostatus2 (2.0.1)
|
||||||
addressable (~> 2.4)
|
addressable (~> 2.4)
|
||||||
http (~> 2.0)
|
http (~> 2.0)
|
||||||
nokogiri (~> 1.6)
|
nokogiri (~> 1.6)
|
||||||
openssl (~> 2.0)
|
openssl (~> 2.0)
|
||||||
ox (2.4.13)
|
ox (2.5.0)
|
||||||
paperclip (5.1.0)
|
paperclip (5.1.0)
|
||||||
activemodel (>= 4.2.0)
|
activemodel (>= 4.2.0)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
@@ -280,12 +266,15 @@ GEM
|
|||||||
paperclip-av-transcoder (0.6.4)
|
paperclip-av-transcoder (0.6.4)
|
||||||
av (~> 0.9.0)
|
av (~> 0.9.0)
|
||||||
paperclip (>= 2.5.2)
|
paperclip (>= 2.5.2)
|
||||||
|
parallel (1.11.2)
|
||||||
|
parallel_tests (2.14.1)
|
||||||
|
parallel
|
||||||
parser (2.4.0.0)
|
parser (2.4.0.0)
|
||||||
ast (~> 2.2)
|
ast (~> 2.2)
|
||||||
pg (0.20.0)
|
pg (0.20.0)
|
||||||
pghero (1.6.5)
|
pghero (1.7.0)
|
||||||
activerecord
|
activerecord
|
||||||
pkg-config (1.2.0)
|
pkg-config (1.2.3)
|
||||||
powerpack (0.1.1)
|
powerpack (0.1.1)
|
||||||
pry (0.10.4)
|
pry (0.10.4)
|
||||||
coderay (~> 1.1.0)
|
coderay (~> 1.1.0)
|
||||||
@@ -294,64 +283,55 @@ GEM
|
|||||||
pry-rails (0.3.6)
|
pry-rails (0.3.6)
|
||||||
pry (>= 0.10.4)
|
pry (>= 0.10.4)
|
||||||
public_suffix (2.0.5)
|
public_suffix (2.0.5)
|
||||||
puma (3.8.2)
|
puma (3.9.1)
|
||||||
|
pundit (1.1.0)
|
||||||
|
activesupport (>= 3.0.0)
|
||||||
rabl (0.13.1)
|
rabl (0.13.1)
|
||||||
activesupport (>= 2.3.14)
|
activesupport (>= 2.3.14)
|
||||||
rack (2.0.1)
|
rack (2.0.3)
|
||||||
rack-attack (5.0.1)
|
rack-attack (5.0.1)
|
||||||
rack
|
rack
|
||||||
rack-cors (0.4.1)
|
rack-cors (0.4.1)
|
||||||
rack-protection (1.5.3)
|
rack-protection (2.0.0)
|
||||||
rack
|
rack
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rack-timeout (0.4.2)
|
rack-timeout (0.4.2)
|
||||||
rails (5.0.2)
|
rails (5.1.1)
|
||||||
actioncable (= 5.0.2)
|
actioncable (= 5.1.1)
|
||||||
actionmailer (= 5.0.2)
|
actionmailer (= 5.1.1)
|
||||||
actionpack (= 5.0.2)
|
actionpack (= 5.1.1)
|
||||||
actionview (= 5.0.2)
|
actionview (= 5.1.1)
|
||||||
activejob (= 5.0.2)
|
activejob (= 5.1.1)
|
||||||
activemodel (= 5.0.2)
|
activemodel (= 5.1.1)
|
||||||
activerecord (= 5.0.2)
|
activerecord (= 5.1.1)
|
||||||
activesupport (= 5.0.2)
|
activesupport (= 5.1.1)
|
||||||
bundler (>= 1.3.0, < 2.0)
|
bundler (>= 1.3.0, < 2.0)
|
||||||
railties (= 5.0.2)
|
railties (= 5.1.1)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.1)
|
rails-controller-testing (1.0.2)
|
||||||
actionpack (~> 5.x)
|
actionpack (~> 5.x, >= 5.0.1)
|
||||||
actionview (~> 5.x)
|
actionview (~> 5.x, >= 5.0.1)
|
||||||
activesupport (~> 5.x)
|
activesupport (~> 5.x)
|
||||||
rails-dom-testing (2.0.2)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0, < 6.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (~> 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.0.3)
|
rails-html-sanitizer (1.0.3)
|
||||||
loofah (~> 2.0)
|
loofah (~> 2.0)
|
||||||
rails-i18n (5.0.3)
|
rails-i18n (5.0.4)
|
||||||
i18n (~> 0.7)
|
i18n (~> 0.7)
|
||||||
railties (~> 5.0)
|
railties (~> 5.0)
|
||||||
rails-settings-cached (0.6.5)
|
rails-settings-cached (0.6.5)
|
||||||
rails (>= 4.2.0)
|
rails (>= 4.2.0)
|
||||||
rails_12factor (0.0.3)
|
railties (5.1.1)
|
||||||
rails_serve_static_assets
|
actionpack (= 5.1.1)
|
||||||
rails_stdout_logging
|
activesupport (= 5.1.1)
|
||||||
rails_serve_static_assets (0.0.5)
|
|
||||||
rails_stdout_logging (0.0.5)
|
|
||||||
railties (5.0.2)
|
|
||||||
actionpack (= 5.0.2)
|
|
||||||
activesupport (= 5.0.2)
|
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.18.1, < 2.0)
|
thor (>= 0.18.1, < 2.0)
|
||||||
rainbow (2.2.2)
|
rainbow (2.2.2)
|
||||||
rake
|
rake
|
||||||
rake (12.0.0)
|
rake (12.0.0)
|
||||||
react-rails (1.11.0)
|
|
||||||
babel-transpiler (>= 0.7.0)
|
|
||||||
connection_pool
|
|
||||||
execjs
|
|
||||||
railties (>= 3.2)
|
|
||||||
tilt
|
|
||||||
redis (3.3.3)
|
redis (3.3.3)
|
||||||
redis-actionpack (5.0.1)
|
redis-actionpack (5.0.1)
|
||||||
actionpack (>= 4.0, < 6)
|
actionpack (>= 4.0, < 6)
|
||||||
@@ -360,6 +340,8 @@ GEM
|
|||||||
redis-activesupport (5.0.2)
|
redis-activesupport (5.0.2)
|
||||||
activesupport (>= 3, < 6)
|
activesupport (>= 3, < 6)
|
||||||
redis-store (~> 1.3.0)
|
redis-store (~> 1.3.0)
|
||||||
|
redis-namespace (1.5.3)
|
||||||
|
redis (~> 3.0, >= 3.0.4)
|
||||||
redis-rack (2.0.2)
|
redis-rack (2.0.2)
|
||||||
rack (>= 1.5, < 3)
|
rack (>= 1.5, < 3)
|
||||||
redis-store (>= 1.2, < 1.4)
|
redis-store (>= 1.2, < 1.4)
|
||||||
@@ -369,32 +351,34 @@ GEM
|
|||||||
redis-store (>= 1.2, < 2)
|
redis-store (>= 1.2, < 2)
|
||||||
redis-store (1.3.0)
|
redis-store (1.3.0)
|
||||||
redis (>= 2.2)
|
redis (>= 2.2)
|
||||||
responders (2.3.0)
|
responders (2.4.0)
|
||||||
railties (>= 4.2.0, < 5.1)
|
actionpack (>= 4.2.0, < 5.3)
|
||||||
|
railties (>= 4.2.0, < 5.3)
|
||||||
rotp (2.1.2)
|
rotp (2.1.2)
|
||||||
rqrcode (0.10.1)
|
rqrcode (0.10.1)
|
||||||
chunky_png (~> 1.0)
|
chunky_png (~> 1.0)
|
||||||
rspec-core (3.5.4)
|
rspec-core (3.6.0)
|
||||||
rspec-support (~> 3.5.0)
|
rspec-support (~> 3.6.0)
|
||||||
rspec-expectations (3.5.0)
|
rspec-expectations (3.6.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.5.0)
|
rspec-support (~> 3.6.0)
|
||||||
rspec-mocks (3.5.0)
|
rspec-mocks (3.6.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.5.0)
|
rspec-support (~> 3.6.0)
|
||||||
rspec-rails (3.5.2)
|
rspec-rails (3.6.0)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
railties (>= 3.0)
|
railties (>= 3.0)
|
||||||
rspec-core (~> 3.5.0)
|
rspec-core (~> 3.6.0)
|
||||||
rspec-expectations (~> 3.5.0)
|
rspec-expectations (~> 3.6.0)
|
||||||
rspec-mocks (~> 3.5.0)
|
rspec-mocks (~> 3.6.0)
|
||||||
rspec-support (~> 3.5.0)
|
rspec-support (~> 3.6.0)
|
||||||
rspec-sidekiq (3.0.0)
|
rspec-sidekiq (3.0.1)
|
||||||
rspec-core (~> 3.0, >= 3.0.0)
|
rspec-core (~> 3.0, >= 3.0.0)
|
||||||
sidekiq (>= 2.4.0)
|
sidekiq (>= 2.4.0)
|
||||||
rspec-support (3.5.0)
|
rspec-support (3.6.0)
|
||||||
rubocop (0.48.1)
|
rubocop (0.49.1)
|
||||||
|
parallel (~> 1.10)
|
||||||
parser (>= 2.3.3.1, < 3.0)
|
parser (>= 2.3.3.1, < 3.0)
|
||||||
powerpack (~> 0.1)
|
powerpack (~> 0.1)
|
||||||
rainbow (>= 1.99.1, < 3.0)
|
rainbow (>= 1.99.1, < 3.0)
|
||||||
@@ -402,36 +386,43 @@ GEM
|
|||||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||||
ruby-oembed (0.12.0)
|
ruby-oembed (0.12.0)
|
||||||
ruby-progressbar (1.8.1)
|
ruby-progressbar (1.8.1)
|
||||||
|
rufus-scheduler (3.4.2)
|
||||||
|
et-orbi (~> 1.0)
|
||||||
safe_yaml (1.0.4)
|
safe_yaml (1.0.4)
|
||||||
sanitize (4.4.0)
|
sanitize (4.5.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.4.4)
|
nokogiri (>= 1.4.4)
|
||||||
nokogumbo (~> 1.4.1)
|
nokogumbo (~> 1.4.1)
|
||||||
sass (3.4.23)
|
sass (3.4.24)
|
||||||
sass-rails (5.0.6)
|
scss_lint (0.53.0)
|
||||||
railties (>= 4.0.0, < 6)
|
rake (>= 0.9, < 13)
|
||||||
sass (~> 3.1)
|
sass (~> 3.4.20)
|
||||||
sprockets (>= 2.8, < 4.0)
|
sidekiq (5.0.2)
|
||||||
sprockets-rails (>= 2.0, < 4.0)
|
|
||||||
tilt (>= 1.1, < 3)
|
|
||||||
sidekiq (4.2.10)
|
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
connection_pool (~> 2.2, >= 2.2.0)
|
connection_pool (~> 2.2, >= 2.2.0)
|
||||||
rack-protection (>= 1.5.0)
|
rack-protection (>= 1.5.0)
|
||||||
redis (~> 3.2, >= 3.2.1)
|
redis (~> 3.3, >= 3.3.3)
|
||||||
sidekiq-unique-jobs (5.0.0)
|
sidekiq-bulk (0.1.1)
|
||||||
sidekiq (>= 4.0)
|
activesupport
|
||||||
thor
|
sidekiq
|
||||||
|
sidekiq-scheduler (2.1.5)
|
||||||
|
redis (~> 3)
|
||||||
|
rufus-scheduler (~> 3.2)
|
||||||
|
sidekiq (>= 3)
|
||||||
|
tilt (>= 1.4.0)
|
||||||
|
sidekiq-unique-jobs (5.0.8)
|
||||||
|
sidekiq (>= 4.0, <= 6.0)
|
||||||
|
thor (~> 0)
|
||||||
simple-navigation (4.0.5)
|
simple-navigation (4.0.5)
|
||||||
activesupport (>= 2.3.2)
|
activesupport (>= 2.3.2)
|
||||||
simple_form (3.4.0)
|
simple_form (3.5.0)
|
||||||
actionpack (> 4, < 5.1)
|
actionpack (> 4, < 5.2)
|
||||||
activemodel (> 4, < 5.1)
|
activemodel (> 4, < 5.2)
|
||||||
simplecov (0.14.1)
|
simplecov (0.14.1)
|
||||||
docile (~> 1.1.0)
|
docile (~> 1.1.0)
|
||||||
json (>= 1.8, < 3)
|
json (>= 1.8, < 3)
|
||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.0)
|
simplecov-html (0.10.1)
|
||||||
slop (3.6.0)
|
slop (3.6.0)
|
||||||
sprockets (3.7.1)
|
sprockets (3.7.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
@@ -445,8 +436,8 @@ GEM
|
|||||||
net-ssh (>= 2.8.0)
|
net-ssh (>= 2.8.0)
|
||||||
statsd-instrument (2.1.2)
|
statsd-instrument (2.1.2)
|
||||||
temple (0.8.0)
|
temple (0.8.0)
|
||||||
terminal-table (1.7.3)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
thor (0.19.4)
|
thor (0.19.4)
|
||||||
thread (0.2.2)
|
thread (0.2.2)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
@@ -462,7 +453,7 @@ GEM
|
|||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.7.4)
|
unf_ext (0.0.7.4)
|
||||||
unicode-display_width (1.1.3)
|
unicode-display_width (1.2.1)
|
||||||
uniform_notifier (1.10.0)
|
uniform_notifier (1.10.0)
|
||||||
warden (1.2.7)
|
warden (1.2.7)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
@@ -470,102 +461,107 @@ GEM
|
|||||||
addressable (>= 2.3.6)
|
addressable (>= 2.3.6)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff
|
hashdiff
|
||||||
|
webpacker (1.2)
|
||||||
|
activesupport (>= 4.2)
|
||||||
|
multi_json (~> 1.2)
|
||||||
|
railties (>= 4.2)
|
||||||
websocket-driver (0.6.5)
|
websocket-driver (0.6.5)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.2)
|
websocket-extensions (0.1.2)
|
||||||
whatlanguage (1.0.6)
|
xpath (2.1.0)
|
||||||
xpath (2.0.0)
|
|
||||||
nokogiri (~> 1.3)
|
nokogiri (~> 1.3)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
active_record_query_trace
|
active_record_query_trace (~> 1.5)
|
||||||
addressable
|
addressable (~> 2.5)
|
||||||
autoprefixer-rails
|
annotate (~> 2.7)
|
||||||
aws-sdk (>= 2.0)
|
aws-sdk (~> 2.9)
|
||||||
best_in_place (~> 3.0.1)
|
better_errors (~> 2.1)
|
||||||
better_errors
|
binding_of_caller (~> 0.7)
|
||||||
binding_of_caller
|
bootsnap
|
||||||
browserify-rails
|
brakeman (~> 3.6)
|
||||||
bullet
|
bullet (~> 5.5)
|
||||||
capistrano (= 3.8.0)
|
bundler-audit (~> 0.5)
|
||||||
capistrano-faster-assets (~> 1.0)
|
capistrano (~> 3.8)
|
||||||
capistrano-rails
|
capistrano-rails (~> 1.2)
|
||||||
capistrano-rbenv
|
capistrano-rbenv (~> 2.1)
|
||||||
capistrano-yarn
|
capistrano-yarn (~> 2.0)
|
||||||
capybara
|
capybara (~> 2.14)
|
||||||
devise
|
cld3 (~> 3.1)
|
||||||
devise-two-factor
|
climate_control (~> 0.2)
|
||||||
doorkeeper
|
devise (~> 4.2)
|
||||||
dotenv-rails
|
devise-two-factor (~> 3.0)
|
||||||
fabrication
|
doorkeeper (~> 4.2)
|
||||||
faker
|
dotenv-rails (~> 2.2)
|
||||||
fast_blank
|
fabrication (~> 2.16)
|
||||||
font-awesome-rails
|
faker (~> 1.7)
|
||||||
fuubar
|
fast_blank (~> 1.0)
|
||||||
goldfinger
|
fuubar (~> 2.2)
|
||||||
hamlit-rails
|
goldfinger (~> 1.2)
|
||||||
hiredis
|
hamlit-rails (~> 0.2)
|
||||||
htmlentities
|
hiredis (~> 0.6)
|
||||||
http
|
htmlentities (~> 4.3)
|
||||||
http_accept_language
|
http (~> 2.2)
|
||||||
httplog
|
http_accept_language (~> 2.1)
|
||||||
i18n-tasks (~> 0.9.6)
|
httplog (~> 0.99)
|
||||||
jquery-rails
|
i18n-tasks (~> 0.9)
|
||||||
kaminari
|
kaminari (~> 1.0)
|
||||||
letter_opener
|
letter_opener (~> 1.4)
|
||||||
letter_opener_web
|
letter_opener_web (~> 1.3)
|
||||||
link_header
|
link_header (~> 0.0)
|
||||||
local_time
|
lograge (~> 0.5)
|
||||||
lograge
|
microformats2 (~> 3.0)
|
||||||
microformats2
|
nokogiri (~> 1.7)
|
||||||
nokogiri
|
oj (~> 3.0)
|
||||||
oj
|
ostatus2 (~> 2.0)
|
||||||
ostatus2 (~> 1.1)
|
ox (~> 2.5)
|
||||||
ox
|
|
||||||
paperclip (~> 5.1)
|
paperclip (~> 5.1)
|
||||||
paperclip-av-transcoder
|
paperclip-av-transcoder (~> 0.6)
|
||||||
pg
|
parallel_tests (~> 2.14)
|
||||||
pghero
|
pg (~> 0.20)
|
||||||
pkg-config
|
pghero (~> 1.7)
|
||||||
pry-rails
|
pkg-config (~> 1.2)
|
||||||
puma
|
pry-rails (~> 0.3)
|
||||||
rabl
|
puma (~> 3.8)
|
||||||
rack-attack
|
pundit (~> 1.1)
|
||||||
rack-cors
|
rabl (~> 0.13)
|
||||||
rack-timeout
|
rack-attack (~> 5.0)
|
||||||
rails (~> 5.0.2)
|
rack-cors (~> 0.4)
|
||||||
rails-controller-testing
|
rack-timeout (~> 0.4)
|
||||||
rails-i18n
|
rails (~> 5.1.0)
|
||||||
rails-settings-cached
|
rails-controller-testing (~> 1.0)
|
||||||
rails_12factor
|
rails-i18n (~> 5.0)
|
||||||
react-rails
|
rails-settings-cached (~> 0.6)
|
||||||
redis (~> 3.2)
|
redis (~> 3.3)
|
||||||
redis-rails
|
redis-namespace (~> 1.5)
|
||||||
rqrcode
|
redis-rails (~> 5.0)
|
||||||
rspec-rails
|
rqrcode (~> 0.10)
|
||||||
rspec-sidekiq
|
rspec-rails (~> 3.6)
|
||||||
|
rspec-sidekiq (~> 3.0)
|
||||||
rubocop
|
rubocop
|
||||||
ruby-oembed
|
ruby-oembed (~> 0.12)
|
||||||
sanitize
|
sanitize (~> 4.4)
|
||||||
sass-rails (~> 5.0)
|
scss_lint (~> 0.53)
|
||||||
sidekiq
|
sidekiq (~> 5.0)
|
||||||
sidekiq-unique-jobs
|
sidekiq-bulk (~> 0.1.1)
|
||||||
simple-navigation
|
sidekiq-scheduler (~> 2.1)
|
||||||
simple_form
|
sidekiq-unique-jobs (~> 5.0)
|
||||||
simplecov
|
simple-navigation (~> 4.0)
|
||||||
sprockets-rails
|
simple_form (~> 3.4)
|
||||||
statsd-instrument
|
simplecov (~> 0.14)
|
||||||
twitter-text
|
sprockets-rails (~> 3.2)
|
||||||
tzinfo-data
|
statsd-instrument (~> 2.1)
|
||||||
uglifier (>= 1.3.0)
|
twitter-text (~> 1.14)
|
||||||
webmock
|
tzinfo-data (~> 1.2017)
|
||||||
whatlanguage
|
uglifier (~> 3.2)
|
||||||
|
webmock (~> 3.0)
|
||||||
|
webpacker (~> 1.2)
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
ruby 2.4.1p111
|
ruby 2.4.1p111
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.14.6
|
1.15.1
|
||||||
|
2
Procfile
2
Procfile
@@ -1,2 +1,2 @@
|
|||||||
web: bundle exec puma -C config/puma.rb
|
web: bundle exec puma -C config/puma.rb
|
||||||
worker: bundle exec sidekiq -q default -q push -q pull -q mailers
|
worker: bundle exec sidekiq
|
||||||
|
4
Procfile.dev
Normal file
4
Procfile.dev
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
web: PORT=3000 bundle exec puma -C config/puma.rb
|
||||||
|
sidekiq: PORT=3000 bundle exec sidekiq
|
||||||
|
stream: PORT=4000 yarn run start
|
||||||
|
webpack: ./bin/webpack-dev-server --host 0.0.0.0
|
@@ -13,7 +13,7 @@ An alternative implementation of the GNU social project. Based on [ActivityStrea
|
|||||||
|
|
||||||
Click on the screenshot to watch a demo of the UI:
|
Click on the screenshot to watch a demo of the UI:
|
||||||
|
|
||||||
[][youtube_demo]
|
[][youtube_demo]
|
||||||
|
|
||||||
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
|
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
|
||||||
|
|
||||||
@@ -48,6 +48,10 @@ If you would like, you can [support the development of this project on Patreon][
|
|||||||
- **Deployable via Docker**
|
- **Deployable via Docker**
|
||||||
You don't need to mess with dependencies and configuration if you want to try Mastodon, if you have Docker and Docker Compose the deployment is extremely easy
|
You don't need to mess with dependencies and configuration if you want to try Mastodon, if you have Docker and Docker Compose the deployment is extremely easy
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Please follow the [development guide](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Development-guide.md) from the documentation repository.
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
There are guides in the documentation repository for [deploying on various platforms](https://github.com/tootsuite/documentation#running-mastodon).
|
There are guides in the documentation repository for [deploying on various platforms](https://github.com/tootsuite/documentation#running-mastodon).
|
||||||
|
30
Vagrantfile
vendored
30
Vagrantfile
vendored
@@ -1,6 +1,8 @@
|
|||||||
# -*- mode: ruby -*-
|
# -*- mode: ruby -*-
|
||||||
# vi: set ft=ruby :
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
|
ENV["PORT"] ||= "3000"
|
||||||
|
|
||||||
$provision = <<SCRIPT
|
$provision = <<SCRIPT
|
||||||
|
|
||||||
cd /vagrant # This is where the host folder/repo is mounted
|
cd /vagrant # This is where the host folder/repo is mounted
|
||||||
@@ -10,10 +12,10 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
|||||||
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
|
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
|
||||||
|
|
||||||
# Add repo for NodeJS
|
# Add repo for NodeJS
|
||||||
curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
|
curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -
|
||||||
|
|
||||||
# Add firewall rule to redirect 80 to 3000 and save
|
# Add firewall rule to redirect 80 to PORT and save
|
||||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
|
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}
|
||||||
echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections
|
echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections
|
||||||
echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections
|
echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections
|
||||||
sudo apt-get install iptables-persistent -y
|
sudo apt-get install iptables-persistent -y
|
||||||
@@ -31,12 +33,13 @@ sudo apt-get install \
|
|||||||
redis-tools \
|
redis-tools \
|
||||||
postgresql \
|
postgresql \
|
||||||
postgresql-contrib \
|
postgresql-contrib \
|
||||||
|
protobuf-compiler \
|
||||||
yarn \
|
yarn \
|
||||||
|
libprotobuf-dev \
|
||||||
libreadline-dev \
|
libreadline-dev \
|
||||||
-y
|
-y
|
||||||
|
|
||||||
# Install rvm
|
# Install rvm
|
||||||
cd /vagrant
|
|
||||||
read RUBY_VERSION < .ruby-version
|
read RUBY_VERSION < .ruby-version
|
||||||
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
|
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
|
||||||
curl -sSL https://get.rvm.io | bash -s stable --ruby=$RUBY_VERSION
|
curl -sSL https://get.rvm.io | bash -s stable --ruby=$RUBY_VERSION
|
||||||
@@ -47,22 +50,23 @@ sudo -u postgres createuser -U postgres vagrant -s
|
|||||||
sudo -u postgres createdb -U postgres mastodon_development
|
sudo -u postgres createdb -U postgres mastodon_development
|
||||||
|
|
||||||
# Install gems and node modules
|
# Install gems and node modules
|
||||||
gem install bundler
|
gem install bundler foreman
|
||||||
bundle install
|
bundle install
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
# Build Mastodon
|
# Build Mastodon
|
||||||
export $(cat ".env.vagrant" | xargs)
|
export $(cat ".env.vagrant" | xargs)
|
||||||
bundle exec rails db:setup
|
bundle exec rails db:setup
|
||||||
bundle exec rails assets:precompile
|
|
||||||
|
# Configure automatic loading of environment variable
|
||||||
|
echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile
|
||||||
|
|
||||||
SCRIPT
|
SCRIPT
|
||||||
|
|
||||||
$start = <<SCRIPT
|
$start = <<SCRIPT
|
||||||
|
|
||||||
cd /vagrant
|
echo 'To start server'
|
||||||
export $(cat ".env.vagrant" | xargs)
|
echo ' $ vagrant ssh -c "cd /vagrant && foreman start"'
|
||||||
rails s -d -b 0.0.0.0
|
|
||||||
|
|
||||||
SCRIPT
|
SCRIPT
|
||||||
|
|
||||||
@@ -74,7 +78,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||||||
|
|
||||||
config.vm.provider :virtualbox do |vb|
|
config.vm.provider :virtualbox do |vb|
|
||||||
vb.name = "mastodon"
|
vb.name = "mastodon"
|
||||||
vb.customize ["modifyvm", :id, "--memory", "1024"]
|
vb.customize ["modifyvm", :id, "--memory", "2048"]
|
||||||
|
|
||||||
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
|
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
|
||||||
# https://github.com/mitchellh/vagrant/issues/1172
|
# https://github.com/mitchellh/vagrant/issues/1172
|
||||||
@@ -104,8 +108,10 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||||||
config.vm.synced_folder ".", "/vagrant"
|
config.vm.synced_folder ".", "/vagrant"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Otherwise, you can access the site at http://localhost:3000
|
# Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080
|
||||||
config.vm.network :forwarded_port, guest: 80, host: 3000
|
config.vm.network :forwarded_port, guest: 3000, host: 3000
|
||||||
|
config.vm.network :forwarded_port, guest: 4000, host: 4000
|
||||||
|
config.vm.network :forwarded_port, guest: 8080, host: 8080
|
||||||
|
|
||||||
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
|
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
|
||||||
config.vm.provision :shell, inline: $provision, privileged: false
|
config.vm.provision :shell, inline: $provision, privileged: false
|
||||||
|
3
app.json
3
app.json
@@ -94,6 +94,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"buildpacks": [
|
"buildpacks": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/heroku/heroku-buildpack-apt"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "heroku/nodejs"
|
"url": "heroku/nodejs"
|
||||||
},
|
},
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 244 KiB |
@@ -1,15 +0,0 @@
|
|||||||
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
|
||||||
// listed below.
|
|
||||||
//
|
|
||||||
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
|
||||||
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
|
||||||
//
|
|
||||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
|
||||||
// compiled file.
|
|
||||||
//
|
|
||||||
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
|
||||||
// about supported directives.
|
|
||||||
//
|
|
||||||
//= require jquery2
|
|
||||||
//= require jquery_ujs
|
|
||||||
//= require components
|
|
@@ -1,9 +0,0 @@
|
|||||||
//= require jquery2
|
|
||||||
//= require jquery_ujs
|
|
||||||
//= require extras
|
|
||||||
//= require best_in_place
|
|
||||||
//= require local_time
|
|
||||||
|
|
||||||
$(function () {
|
|
||||||
$(".best_in_place").best_in_place();
|
|
||||||
});
|
|
@@ -1,15 +0,0 @@
|
|||||||
//= require_self
|
|
||||||
//= require react_ujs
|
|
||||||
|
|
||||||
window.React = require('react');
|
|
||||||
window.ReactDOM = require('react-dom');
|
|
||||||
window.Perf = require('react-addons-perf');
|
|
||||||
|
|
||||||
if (!window.Intl) {
|
|
||||||
require('intl');
|
|
||||||
require('intl/locale-data/jsonp/en.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
//= require_tree ./components
|
|
||||||
|
|
||||||
window.Mastodon = require('./components/containers/mastodon');
|
|
@@ -1,63 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class Avatar extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.state = {
|
|
||||||
hovering: false
|
|
||||||
};
|
|
||||||
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
|
||||||
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMouseEnter () {
|
|
||||||
this.setState({ hovering: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMouseLeave () {
|
|
||||||
this.setState({ hovering: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { src, size, staticSrc, animate } = this.props;
|
|
||||||
const { hovering } = this.state;
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
...this.props.style,
|
|
||||||
width: `${size}px`,
|
|
||||||
height: `${size}px`,
|
|
||||||
backgroundSize: `${size}px ${size}px`
|
|
||||||
};
|
|
||||||
|
|
||||||
if (hovering || animate) {
|
|
||||||
style.backgroundImage = `url(${src})`;
|
|
||||||
} else {
|
|
||||||
style.backgroundImage = `url(${staticSrc})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className='account__avatar'
|
|
||||||
onMouseEnter={this.handleMouseEnter}
|
|
||||||
onMouseLeave={this.handleMouseLeave}
|
|
||||||
style={style}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Avatar.propTypes = {
|
|
||||||
src: PropTypes.string.isRequired,
|
|
||||||
staticSrc: PropTypes.string,
|
|
||||||
size: PropTypes.number.isRequired,
|
|
||||||
style: PropTypes.object,
|
|
||||||
animate: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
Avatar.defaultProps = {
|
|
||||||
animate: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Avatar;
|
|
@@ -1,49 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class Button extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.handleClick = this.handleClick.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick (e) {
|
|
||||||
if (!this.props.disabled) {
|
|
||||||
this.props.onClick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const style = {
|
|
||||||
display: this.props.block ? 'block' : 'inline-block',
|
|
||||||
width: this.props.block ? '100%' : 'auto',
|
|
||||||
padding: `0 ${this.props.size / 2.25}px`,
|
|
||||||
height: `${this.props.size}px`,
|
|
||||||
lineHeight: `${this.props.size}px`
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button className={`button ${this.props.secondary ? 'button-secondary' : ''}`} disabled={this.props.disabled} onClick={this.handleClick} style={{ ...style, ...this.props.style }}>
|
|
||||||
{this.props.text || this.props.children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Button.propTypes = {
|
|
||||||
text: PropTypes.node,
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
block: PropTypes.bool,
|
|
||||||
secondary: PropTypes.bool,
|
|
||||||
size: PropTypes.number,
|
|
||||||
style: PropTypes.object,
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
Button.defaultProps = {
|
|
||||||
size: 36
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Button;
|
|
@@ -1,56 +0,0 @@
|
|||||||
import { Motion, spring } from 'react-motion';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class ColumnCollapsable extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.state = {
|
|
||||||
collapsed: true
|
|
||||||
};
|
|
||||||
|
|
||||||
this.handleToggleCollapsed = this.handleToggleCollapsed.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleToggleCollapsed () {
|
|
||||||
const currentState = this.state.collapsed;
|
|
||||||
|
|
||||||
this.setState({ collapsed: !currentState });
|
|
||||||
|
|
||||||
if (!currentState && this.props.onCollapse) {
|
|
||||||
this.props.onCollapse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { icon, title, fullHeight, children } = this.props;
|
|
||||||
const { collapsed } = this.state;
|
|
||||||
const collapsedClassName = collapsed ? 'collapsable-collapsed' : 'collapsable';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='column-collapsable'>
|
|
||||||
<div role='button' tabIndex='0' title={`${title}`} className={`column-icon ${collapsedClassName}`} onClick={this.handleToggleCollapsed}>
|
|
||||||
<i className={`fa fa-${icon}`} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(collapsed ? 0 : 100), height: spring(collapsed ? 0 : fullHeight, collapsed ? undefined : { stiffness: 150, damping: 9 }) }}>
|
|
||||||
{({ opacity, height }) =>
|
|
||||||
<div style={{ overflow: height === fullHeight ? 'auto' : 'hidden', height: `${height}px`, opacity: opacity / 100, maxHeight: '70vh' }}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Motion>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnCollapsable.propTypes = {
|
|
||||||
icon: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
fullHeight: PropTypes.number.isRequired,
|
|
||||||
children: PropTypes.node,
|
|
||||||
onCollapse: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ColumnCollapsable;
|
|
@@ -1,78 +0,0 @@
|
|||||||
import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class DropdownMenu extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.state = {
|
|
||||||
direction: 'left'
|
|
||||||
};
|
|
||||||
this.setRef = this.setRef.bind(this);
|
|
||||||
this.renderItem = this.renderItem.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
setRef (c) {
|
|
||||||
this.dropdown = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick (i, e) {
|
|
||||||
const { action } = this.props.items[i];
|
|
||||||
|
|
||||||
if (typeof action === 'function') {
|
|
||||||
e.preventDefault();
|
|
||||||
action();
|
|
||||||
this.dropdown.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderItem (item, i) {
|
|
||||||
if (item === null) {
|
|
||||||
return <li key={ 'sep' + i } className='dropdown__sep' />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { text, action, href = '#' } = item;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li className='dropdown__content-list-item' key={ text + i }>
|
|
||||||
<a href={href} target='_blank' rel='noopener' onClick={this.handleClick.bind(this, i)} className='dropdown__content-list-link'>
|
|
||||||
{text}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { icon, items, size, direction, ariaLabel } = this.props;
|
|
||||||
const directionClass = (direction === "left") ? "dropdown__left" : "dropdown__right";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown ref={this.setRef}>
|
|
||||||
<DropdownTrigger className='icon-button' style={{ fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` }} aria-label={ariaLabel}>
|
|
||||||
<i className={ `fa fa-fw fa-${icon} dropdown__icon` } aria-hidden={true} />
|
|
||||||
</DropdownTrigger>
|
|
||||||
|
|
||||||
<DropdownContent className={directionClass}>
|
|
||||||
<ul className='dropdown__content-list'>
|
|
||||||
{items.map(this.renderItem)}
|
|
||||||
</ul>
|
|
||||||
</DropdownContent>
|
|
||||||
</Dropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DropdownMenu.propTypes = {
|
|
||||||
icon: PropTypes.string.isRequired,
|
|
||||||
items: PropTypes.array.isRequired,
|
|
||||||
size: PropTypes.number.isRequired,
|
|
||||||
direction: PropTypes.string,
|
|
||||||
ariaLabel: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
DropdownMenu.defaultProps = {
|
|
||||||
ariaLabel: "Menu"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DropdownMenu;
|
|
@@ -1,14 +0,0 @@
|
|||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const LoadMore = ({ onClick }) => (
|
|
||||||
<a href="#" className='load-more' role='button' onClick={onClick}>
|
|
||||||
<FormattedMessage id='status.load_more' defaultMessage='Load more' />
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
|
|
||||||
LoadMore.propTypes = {
|
|
||||||
onClick: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LoadMore;
|
|
@@ -1,36 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class Permalink extends React.Component {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.handleClick = this.handleClick.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick (e) {
|
|
||||||
if (e.button === 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.context.router.push(this.props.to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { href, children, className, ...other } = this.props;
|
|
||||||
|
|
||||||
return <a href={href} onClick={this.handleClick} {...other} className={'permalink ' + className}>{children}</a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Permalink.contextTypes = {
|
|
||||||
router: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
Permalink.propTypes = {
|
|
||||||
className: PropTypes.string,
|
|
||||||
href: PropTypes.string.isRequired,
|
|
||||||
to: PropTypes.string.isRequired,
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Permalink;
|
|
@@ -1,19 +0,0 @@
|
|||||||
import { injectIntl, FormattedRelative } from 'react-intl';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const RelativeTimestamp = ({ intl, timestamp }) => {
|
|
||||||
const date = new Date(timestamp);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<time dateTime={timestamp} title={intl.formatDate(date, { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' })}>
|
|
||||||
<FormattedRelative value={date} />
|
|
||||||
</time>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
RelativeTimestamp.propTypes = {
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
timestamp: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default injectIntl(RelativeTimestamp);
|
|
@@ -1,121 +0,0 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Avatar from './avatar';
|
|
||||||
import RelativeTimestamp from './relative_timestamp';
|
|
||||||
import DisplayName from './display_name';
|
|
||||||
import MediaGallery from './media_gallery';
|
|
||||||
import VideoPlayer from './video_player';
|
|
||||||
import AttachmentList from './attachment_list';
|
|
||||||
import StatusContent from './status_content';
|
|
||||||
import StatusActionBar from './status_action_bar';
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import emojify from '../emoji';
|
|
||||||
import escapeTextContentForBrowser from 'escape-html';
|
|
||||||
|
|
||||||
class Status extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.handleClick = this.handleClick.bind(this);
|
|
||||||
this.handleAccountClick = this.handleAccountClick.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick () {
|
|
||||||
const { status } = this.props;
|
|
||||||
this.context.router.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleAccountClick (id, e) {
|
|
||||||
if (e.button === 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.context.router.push(`/accounts/${id}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
let media = '';
|
|
||||||
const { status, ...other } = this.props;
|
|
||||||
|
|
||||||
if (status === null) {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
|
||||||
let displayName = status.getIn(['account', 'display_name']);
|
|
||||||
|
|
||||||
if (displayName.length === 0) {
|
|
||||||
displayName = status.getIn(['account', 'username']);
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) };
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='status__wrapper'>
|
|
||||||
<div className='status__prepend'>
|
|
||||||
<div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-retweet status__prepend-icon' /></div>
|
|
||||||
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name muted'><strong dangerouslySetInnerHTML={displayNameHTML} /></a> }} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Status {...other} wrapped={true} status={status.get('reblog')} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.get('media_attachments').size > 0 && !this.props.muted) {
|
|
||||||
if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
|
|
||||||
|
|
||||||
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
|
||||||
media = <VideoPlayer media={status.getIn(['media_attachments', 0])} sensitive={status.get('sensitive')} onOpenVideo={this.props.onOpenVideo} />;
|
|
||||||
} else {
|
|
||||||
media = <MediaGallery media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} autoPlayGif={this.props.autoPlayGif} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={this.props.muted ? 'status muted' : 'status'}>
|
|
||||||
<div className='status__info'>
|
|
||||||
<div className='status__info-time'>
|
|
||||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name'>
|
|
||||||
<div className='status__avatar'>
|
|
||||||
<Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DisplayName account={status.get('account')} />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<StatusContent status={status} onClick={this.handleClick} />
|
|
||||||
|
|
||||||
{media}
|
|
||||||
|
|
||||||
<StatusActionBar {...this.props} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Status.contextTypes = {
|
|
||||||
router: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
Status.propTypes = {
|
|
||||||
status: ImmutablePropTypes.map,
|
|
||||||
wrapped: PropTypes.bool,
|
|
||||||
onReply: PropTypes.func,
|
|
||||||
onFavourite: PropTypes.func,
|
|
||||||
onReblog: PropTypes.func,
|
|
||||||
onDelete: PropTypes.func,
|
|
||||||
onOpenMedia: PropTypes.func,
|
|
||||||
onOpenVideo: PropTypes.func,
|
|
||||||
onBlock: PropTypes.func,
|
|
||||||
me: PropTypes.number,
|
|
||||||
boostModal: PropTypes.bool,
|
|
||||||
autoPlayGif: PropTypes.bool,
|
|
||||||
muted: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Status;
|
|
@@ -1,137 +0,0 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import IconButton from './icon_button';
|
|
||||||
import DropdownMenu from './dropdown_menu';
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
|
||||||
mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
|
|
||||||
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
|
|
||||||
block: { id: 'account.block', defaultMessage: 'Block @{name}' },
|
|
||||||
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
|
||||||
replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
|
|
||||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
|
||||||
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
|
||||||
report: { id: 'status.report', defaultMessage: 'Report @{name}' }
|
|
||||||
});
|
|
||||||
|
|
||||||
class StatusActionBar extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.handleReplyClick = this.handleReplyClick.bind(this);
|
|
||||||
this.handleFavouriteClick = this.handleFavouriteClick.bind(this);
|
|
||||||
this.handleReblogClick = this.handleReblogClick.bind(this);
|
|
||||||
this.handleDeleteClick = this.handleDeleteClick.bind(this);
|
|
||||||
this.handleMentionClick = this.handleMentionClick.bind(this);
|
|
||||||
this.handleMuteClick = this.handleMuteClick.bind(this);
|
|
||||||
this.handleBlockClick = this.handleBlockClick.bind(this);
|
|
||||||
this.handleOpen = this.handleOpen.bind(this);
|
|
||||||
this.handleReport = this.handleReport.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleReplyClick () {
|
|
||||||
this.props.onReply(this.props.status, this.context.router);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleFavouriteClick () {
|
|
||||||
this.props.onFavourite(this.props.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleReblogClick (e) {
|
|
||||||
this.props.onReblog(this.props.status, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDeleteClick () {
|
|
||||||
this.props.onDelete(this.props.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMentionClick () {
|
|
||||||
this.props.onMention(this.props.status.get('account'), this.context.router);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMuteClick () {
|
|
||||||
this.props.onMute(this.props.status.get('account'));
|
|
||||||
}
|
|
||||||
|
|
||||||
handleBlockClick () {
|
|
||||||
this.props.onBlock(this.props.status.get('account'));
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOpen () {
|
|
||||||
this.context.router.push(`/statuses/${this.props.status.get('id')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleReport () {
|
|
||||||
this.props.onReport(this.props.status);
|
|
||||||
this.context.router.push('/report');
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { status, me, intl } = this.props;
|
|
||||||
const reblog_disabled = status.get('visibility') === 'private' || status.get('visibility') === 'direct';
|
|
||||||
let menu = [];
|
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
|
|
||||||
menu.push(null);
|
|
||||||
|
|
||||||
if (status.getIn(['account', 'id']) === me) {
|
|
||||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
|
||||||
} else {
|
|
||||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
|
||||||
menu.push(null);
|
|
||||||
menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
|
||||||
}
|
|
||||||
|
|
||||||
let reblogIcon = 'retweet';
|
|
||||||
if (status.get('visibility') === 'direct') reblogIcon = 'envelope';
|
|
||||||
else if (status.get('visibility') === 'private') reblogIcon = 'lock';
|
|
||||||
let reply_icon;
|
|
||||||
let reply_title;
|
|
||||||
if (status.get('in_reply_to_id', null) === null) {
|
|
||||||
reply_icon = "reply";
|
|
||||||
reply_title = intl.formatMessage(messages.reply);
|
|
||||||
} else {
|
|
||||||
reply_icon = "reply-all";
|
|
||||||
reply_title = intl.formatMessage(messages.replyAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='status__action-bar'>
|
|
||||||
<div className='status__action-bar-button-wrapper'><IconButton title={reply_title} icon={reply_icon} onClick={this.handleReplyClick} /></div>
|
|
||||||
<div className='status__action-bar-button-wrapper'><IconButton disabled={reblog_disabled} active={status.get('reblogged')} title={reblog_disabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} /></div>
|
|
||||||
<div className='status__action-bar-button-wrapper'><IconButton animate={true} active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} className='star-icon' /></div>
|
|
||||||
|
|
||||||
<div className='status__action-bar-dropdown'>
|
|
||||||
<DropdownMenu items={menu} icon='ellipsis-h' size={18} direction="right" ariaLabel="More"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusActionBar.contextTypes = {
|
|
||||||
router: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
StatusActionBar.propTypes = {
|
|
||||||
status: ImmutablePropTypes.map.isRequired,
|
|
||||||
onReply: PropTypes.func,
|
|
||||||
onFavourite: PropTypes.func,
|
|
||||||
onReblog: PropTypes.func,
|
|
||||||
onDelete: PropTypes.func,
|
|
||||||
onMention: PropTypes.func,
|
|
||||||
onMute: PropTypes.func,
|
|
||||||
onBlock: PropTypes.func,
|
|
||||||
onReport: PropTypes.func,
|
|
||||||
me: PropTypes.number.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default injectIntl(StatusActionBar);
|
|
@@ -1,318 +0,0 @@
|
|||||||
import { Provider } from 'react-redux';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import configureStore from '../store/configureStore';
|
|
||||||
import {
|
|
||||||
refreshTimelineSuccess,
|
|
||||||
updateTimeline,
|
|
||||||
deleteFromTimelines,
|
|
||||||
refreshTimeline,
|
|
||||||
connectTimeline,
|
|
||||||
disconnectTimeline
|
|
||||||
} from '../actions/timelines';
|
|
||||||
import { showOnboardingOnce } from '../actions/onboarding';
|
|
||||||
import { updateNotifications, refreshNotifications } from '../actions/notifications';
|
|
||||||
import createBrowserHistory from 'history/lib/createBrowserHistory';
|
|
||||||
import {
|
|
||||||
applyRouterMiddleware,
|
|
||||||
useRouterHistory,
|
|
||||||
Router,
|
|
||||||
Route,
|
|
||||||
IndexRedirect,
|
|
||||||
IndexRoute
|
|
||||||
} from 'react-router';
|
|
||||||
import { useScroll } from 'react-router-scroll';
|
|
||||||
import UI from '../features/ui';
|
|
||||||
import Status from '../features/status';
|
|
||||||
import GettingStarted from '../features/getting_started';
|
|
||||||
import PublicTimeline from '../features/public_timeline';
|
|
||||||
import CommunityTimeline from '../features/community_timeline';
|
|
||||||
import AccountTimeline from '../features/account_timeline';
|
|
||||||
import HomeTimeline from '../features/home_timeline';
|
|
||||||
import Compose from '../features/compose';
|
|
||||||
import Followers from '../features/followers';
|
|
||||||
import Following from '../features/following';
|
|
||||||
import Reblogs from '../features/reblogs';
|
|
||||||
import Favourites from '../features/favourites';
|
|
||||||
import HashtagTimeline from '../features/hashtag_timeline';
|
|
||||||
import Notifications from '../features/notifications';
|
|
||||||
import FollowRequests from '../features/follow_requests';
|
|
||||||
import GenericNotFound from '../features/generic_not_found';
|
|
||||||
import FavouritedStatuses from '../features/favourited_statuses';
|
|
||||||
import Blocks from '../features/blocks';
|
|
||||||
import Mutes from '../features/mutes';
|
|
||||||
import Report from '../features/report';
|
|
||||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
|
||||||
import ar from 'react-intl/locale-data/ar';
|
|
||||||
import en from 'react-intl/locale-data/en';
|
|
||||||
import de from 'react-intl/locale-data/de';
|
|
||||||
import eo from 'react-intl/locale-data/eo';
|
|
||||||
import es from 'react-intl/locale-data/es';
|
|
||||||
import fa from 'react-intl/locale-data/fa';
|
|
||||||
import fi from 'react-intl/locale-data/fi';
|
|
||||||
import fr from 'react-intl/locale-data/fr';
|
|
||||||
import hu from 'react-intl/locale-data/hu';
|
|
||||||
import it from 'react-intl/locale-data/it';
|
|
||||||
import ja from 'react-intl/locale-data/ja';
|
|
||||||
import pt from 'react-intl/locale-data/pt';
|
|
||||||
import nl from 'react-intl/locale-data/nl';
|
|
||||||
import no from 'react-intl/locale-data/no';
|
|
||||||
import ru from 'react-intl/locale-data/ru';
|
|
||||||
import uk from 'react-intl/locale-data/uk';
|
|
||||||
import zh from 'react-intl/locale-data/zh';
|
|
||||||
import bg from 'react-intl/locale-data/bg';
|
|
||||||
import id from 'react-intl/locale-data/id';
|
|
||||||
import { localeData as zh_hk } from '../locales/zh-hk';
|
|
||||||
import { localeData as zh_cn } from '../locales/zh-cn';
|
|
||||||
import pt_br from '../locales/pt-br';
|
|
||||||
import getMessagesForLocale from '../locales';
|
|
||||||
import { hydrateStore } from '../actions/store';
|
|
||||||
import createStream from '../stream';
|
|
||||||
|
|
||||||
const store = configureStore();
|
|
||||||
const initialState = JSON.parse(document.getElementById("initial-state").textContent);
|
|
||||||
store.dispatch(hydrateStore(initialState));
|
|
||||||
|
|
||||||
const browserHistory = useRouterHistory(createBrowserHistory)({
|
|
||||||
basename: '/web'
|
|
||||||
});
|
|
||||||
|
|
||||||
addLocaleData([
|
|
||||||
...en,
|
|
||||||
...ar,
|
|
||||||
...de,
|
|
||||||
...eo,
|
|
||||||
...es,
|
|
||||||
...fa,
|
|
||||||
...fi,
|
|
||||||
...fr,
|
|
||||||
...hu,
|
|
||||||
...it,
|
|
||||||
...ja,
|
|
||||||
...pt,
|
|
||||||
...pt_br,
|
|
||||||
...nl,
|
|
||||||
...no,
|
|
||||||
...ru,
|
|
||||||
...uk,
|
|
||||||
...zh,
|
|
||||||
...zh_hk,
|
|
||||||
...zh_cn,
|
|
||||||
...bg,
|
|
||||||
...id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const getTopWhenReplacing = (previous, { location }) => location && location.action === 'REPLACE' && [0, 0];
|
|
||||||
|
|
||||||
const hiddenColumnContainerStyle = {
|
|
||||||
position: 'absolute',
|
|
||||||
left: '0',
|
|
||||||
top: '0',
|
|
||||||
visibility: 'hidden'
|
|
||||||
};
|
|
||||||
|
|
||||||
class Container extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
renderedPersistents: [],
|
|
||||||
unrenderedPersistents: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount () {
|
|
||||||
this.unlistenHistory = null;
|
|
||||||
|
|
||||||
this.setState(() => {
|
|
||||||
return {
|
|
||||||
mountImpersistent: false,
|
|
||||||
renderedPersistents: [],
|
|
||||||
unrenderedPersistents: [
|
|
||||||
{pathname: '/timelines/home', component: HomeTimeline},
|
|
||||||
{pathname: '/timelines/public', component: PublicTimeline},
|
|
||||||
{pathname: '/timelines/public/local', component: CommunityTimeline},
|
|
||||||
|
|
||||||
{pathname: '/notifications', component: Notifications},
|
|
||||||
{pathname: '/favourites', component: FavouritedStatuses}
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}, () => {
|
|
||||||
if (this.unlistenHistory) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.unlistenHistory = browserHistory.listen(location => {
|
|
||||||
const pathname = location.pathname.replace(/\/$/, '').toLowerCase();
|
|
||||||
|
|
||||||
this.setState(oldState => {
|
|
||||||
let persistentMatched = false;
|
|
||||||
|
|
||||||
const newState = {
|
|
||||||
renderedPersistents: oldState.renderedPersistents.map(persistent => {
|
|
||||||
const givenMatched = persistent.pathname === pathname;
|
|
||||||
|
|
||||||
if (givenMatched) {
|
|
||||||
persistentMatched = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
hidden: !givenMatched,
|
|
||||||
pathname: persistent.pathname,
|
|
||||||
component: persistent.component
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!persistentMatched) {
|
|
||||||
newState.unrenderedPersistents = [];
|
|
||||||
|
|
||||||
oldState.unrenderedPersistents.forEach(persistent => {
|
|
||||||
if (persistent.pathname === pathname) {
|
|
||||||
persistentMatched = true;
|
|
||||||
|
|
||||||
newState.renderedPersistents.push({
|
|
||||||
hidden: false,
|
|
||||||
pathname: persistent.pathname,
|
|
||||||
component: persistent.component
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
newState.unrenderedPersistents.push(persistent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
newState.mountImpersistent = !persistentMatched;
|
|
||||||
|
|
||||||
return newState;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
if (this.unlistenHistory) {
|
|
||||||
this.unlistenHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.unlistenHistory = "done";
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
// Hide some components rather than unmounting them to allow to show again
|
|
||||||
// quickly and keep the view state such as the scrolled offset.
|
|
||||||
const persistentsView = this.state.renderedPersistents.map((persistent) =>
|
|
||||||
<div aria-hidden={persistent.hidden} key={persistent.pathname} className='mastodon-column-container' style={persistent.hidden ? hiddenColumnContainerStyle : null}>
|
|
||||||
<persistent.component shouldUpdateScroll={persistent.hidden ? Function.prototype : getTopWhenReplacing} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<UI>
|
|
||||||
{this.state.mountImpersistent && this.props.children}
|
|
||||||
{persistentsView}
|
|
||||||
</UI>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Container.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
class Mastodon extends React.Component {
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { locale } = this.props;
|
|
||||||
const streamingAPIBaseURL = store.getState().getIn(['meta', 'streaming_api_base_url']);
|
|
||||||
const accessToken = store.getState().getIn(['meta', 'access_token']);
|
|
||||||
|
|
||||||
this.subscription = createStream(streamingAPIBaseURL, accessToken, 'user', {
|
|
||||||
|
|
||||||
connected () {
|
|
||||||
store.dispatch(connectTimeline('home'));
|
|
||||||
},
|
|
||||||
|
|
||||||
disconnected () {
|
|
||||||
store.dispatch(disconnectTimeline('home'));
|
|
||||||
},
|
|
||||||
|
|
||||||
received (data) {
|
|
||||||
switch(data.event) {
|
|
||||||
case 'update':
|
|
||||||
store.dispatch(updateTimeline('home', JSON.parse(data.payload)));
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
store.dispatch(deleteFromTimelines(data.payload));
|
|
||||||
break;
|
|
||||||
case 'notification':
|
|
||||||
store.dispatch(updateNotifications(JSON.parse(data.payload), getMessagesForLocale(locale), locale));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
reconnected () {
|
|
||||||
store.dispatch(connectTimeline('home'));
|
|
||||||
store.dispatch(refreshTimeline('home'));
|
|
||||||
store.dispatch(refreshNotifications());
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Desktop notifications
|
|
||||||
if (typeof window.Notification !== 'undefined' && Notification.permission === 'default') {
|
|
||||||
Notification.requestPermission();
|
|
||||||
}
|
|
||||||
|
|
||||||
store.dispatch(showOnboardingOnce());
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
if (typeof this.subscription !== 'undefined') {
|
|
||||||
this.subscription.close();
|
|
||||||
this.subscription = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { locale } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IntlProvider locale={locale} messages={getMessagesForLocale(locale)}>
|
|
||||||
<Provider store={store}>
|
|
||||||
<Router history={browserHistory} render={applyRouterMiddleware(useScroll())}>
|
|
||||||
<Route path='/' component={Container}>
|
|
||||||
<IndexRedirect to="/getting-started" />
|
|
||||||
|
|
||||||
<Route path='getting-started' component={GettingStarted} />
|
|
||||||
<Route path='timelines/tag/:id' component={HashtagTimeline} />
|
|
||||||
|
|
||||||
<Route path='statuses/new' component={Compose} />
|
|
||||||
<Route path='statuses/:statusId' component={Status} />
|
|
||||||
<Route path='statuses/:statusId/reblogs' component={Reblogs} />
|
|
||||||
<Route path='statuses/:statusId/favourites' component={Favourites} />
|
|
||||||
|
|
||||||
<Route path='accounts/:accountId' component={AccountTimeline} />
|
|
||||||
<Route path='accounts/:accountId/followers' component={Followers} />
|
|
||||||
<Route path='accounts/:accountId/following' component={Following} />
|
|
||||||
|
|
||||||
<Route path='follow_requests' component={FollowRequests} />
|
|
||||||
<Route path='blocks' component={Blocks} />
|
|
||||||
<Route path='mutes' component={Mutes} />
|
|
||||||
<Route path='report' component={Report} />
|
|
||||||
|
|
||||||
<Route path='*' component={GenericNotFound} />
|
|
||||||
</Route>
|
|
||||||
</Router>
|
|
||||||
</Provider>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Mastodon.propTypes = {
|
|
||||||
locale: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Mastodon;
|
|
@@ -1,81 +0,0 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import InnerHeader from '../../account/components/header';
|
|
||||||
import ActionBar from '../../account/components/action_bar';
|
|
||||||
import MissingIndicator from '../../../components/missing_indicator';
|
|
||||||
|
|
||||||
class Header extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.handleFollow = this.handleFollow.bind(this);
|
|
||||||
this.handleBlock = this.handleBlock.bind(this);
|
|
||||||
this.handleMention = this.handleMention.bind(this);
|
|
||||||
this.handleReport = this.handleReport.bind(this);
|
|
||||||
this.handleMute = this.handleMute.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleFollow () {
|
|
||||||
this.props.onFollow(this.props.account);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleBlock () {
|
|
||||||
this.props.onBlock(this.props.account);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMention () {
|
|
||||||
this.props.onMention(this.props.account, this.context.router);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleReport () {
|
|
||||||
this.props.onReport(this.props.account);
|
|
||||||
this.context.router.push('/report');
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMute() {
|
|
||||||
this.props.onMute(this.props.account);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { account, me } = this.props;
|
|
||||||
|
|
||||||
if (account === null) {
|
|
||||||
return <MissingIndicator />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='account-timeline__header'>
|
|
||||||
<InnerHeader
|
|
||||||
account={account}
|
|
||||||
me={me}
|
|
||||||
onFollow={this.handleFollow}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ActionBar
|
|
||||||
account={account}
|
|
||||||
me={me}
|
|
||||||
onBlock={this.handleBlock}
|
|
||||||
onMention={this.handleMention}
|
|
||||||
onReport={this.handleReport}
|
|
||||||
onMute={this.handleMute}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Header.propTypes = {
|
|
||||||
account: ImmutablePropTypes.map,
|
|
||||||
me: PropTypes.number.isRequired,
|
|
||||||
onFollow: PropTypes.func.isRequired,
|
|
||||||
onBlock: PropTypes.func.isRequired,
|
|
||||||
onMention: PropTypes.func.isRequired,
|
|
||||||
onReport: PropTypes.func.isRequired,
|
|
||||||
onMute: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
Header.contextTypes = {
|
|
||||||
router: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Header;
|
|
@@ -1,95 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import StatusListContainer from '../ui/containers/status_list_container';
|
|
||||||
import Column from '../ui/components/column';
|
|
||||||
import {
|
|
||||||
refreshTimeline,
|
|
||||||
updateTimeline,
|
|
||||||
deleteFromTimelines,
|
|
||||||
connectTimeline,
|
|
||||||
disconnectTimeline
|
|
||||||
} from '../../actions/timelines';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
|
|
||||||
import createStream from '../../stream';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
title: { id: 'column.community', defaultMessage: 'Local timeline' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
hasUnread: state.getIn(['timelines', 'community', 'unread']) > 0,
|
|
||||||
streamingAPIBaseURL: state.getIn(['meta', 'streaming_api_base_url']),
|
|
||||||
accessToken: state.getIn(['meta', 'access_token'])
|
|
||||||
});
|
|
||||||
|
|
||||||
let subscription;
|
|
||||||
|
|
||||||
class CommunityTimeline extends React.PureComponent {
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
const { dispatch, streamingAPIBaseURL, accessToken } = this.props;
|
|
||||||
|
|
||||||
dispatch(refreshTimeline('community'));
|
|
||||||
|
|
||||||
if (typeof subscription !== 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
subscription = createStream(streamingAPIBaseURL, accessToken, 'public:local', {
|
|
||||||
|
|
||||||
connected () {
|
|
||||||
dispatch(connectTimeline('community'));
|
|
||||||
},
|
|
||||||
|
|
||||||
reconnected () {
|
|
||||||
dispatch(connectTimeline('community'));
|
|
||||||
},
|
|
||||||
|
|
||||||
disconnected () {
|
|
||||||
dispatch(disconnectTimeline('community'));
|
|
||||||
},
|
|
||||||
|
|
||||||
received (data) {
|
|
||||||
switch(data.event) {
|
|
||||||
case 'update':
|
|
||||||
dispatch(updateTimeline('community', JSON.parse(data.payload)));
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
dispatch(deleteFromTimelines(data.payload));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
// if (typeof subscription !== 'undefined') {
|
|
||||||
// subscription.close();
|
|
||||||
// subscription = null;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { intl, hasUnread } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Column icon='users' active={hasUnread} heading={intl.formatMessage(messages.title)}>
|
|
||||||
<ColumnBackButtonSlim />
|
|
||||||
<StatusListContainer {...this.props} scrollKey='community_timeline' type='community' emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />} />
|
|
||||||
</Column>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
CommunityTimeline.propTypes = {
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
streamingAPIBaseURL: PropTypes.string.isRequired,
|
|
||||||
accessToken: PropTypes.string.isRequired,
|
|
||||||
hasUnread: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(injectIntl(CommunityTimeline));
|
|
@@ -1,16 +0,0 @@
|
|||||||
import Avatar from '../../../components/avatar';
|
|
||||||
import DisplayName from '../../../components/display_name';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
|
|
||||||
const AutosuggestAccount = ({ account }) => (
|
|
||||||
<div className='autosuggest-account'>
|
|
||||||
<div className='autosuggest-account-icon'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={18} /></div>
|
|
||||||
<DisplayName account={account} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
AutosuggestAccount.propTypes = {
|
|
||||||
account: ImmutablePropTypes.map.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AutosuggestAccount;
|
|
@@ -1,15 +0,0 @@
|
|||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import DisplayName from '../../../components/display_name';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
|
|
||||||
const AutosuggestStatus = ({ status }) => (
|
|
||||||
<div className='autosuggest-status'>
|
|
||||||
<FormattedMessage id='search.status_by' defaultMessage='Status by {name}' values={{ name: <strong>@{status.getIn(['account', 'acct'])}</strong> }} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
AutosuggestStatus.propTypes = {
|
|
||||||
status: ImmutablePropTypes.map.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AutosuggestStatus;
|
|
@@ -1,60 +0,0 @@
|
|||||||
import IconButton from '../../../components/icon_button';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
upload: { id: 'upload_button.label', defaultMessage: 'Add media' }
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const iconStyle = {
|
|
||||||
height: null,
|
|
||||||
lineHeight: '27px'
|
|
||||||
}
|
|
||||||
|
|
||||||
class UploadButton extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.handleChange = this.handleChange.bind(this);
|
|
||||||
this.handleClick = this.handleClick.bind(this);
|
|
||||||
this.setRef = this.setRef.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleChange (e) {
|
|
||||||
if (e.target.files.length > 0) {
|
|
||||||
this.props.onSelectFile(e.target.files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick () {
|
|
||||||
this.fileElement.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
setRef (c) {
|
|
||||||
this.fileElement = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
|
|
||||||
const { intl, resetFileKey, disabled } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='compose-form__upload-button'>
|
|
||||||
<IconButton icon='camera' title={intl.formatMessage(messages.upload)} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle}/>
|
|
||||||
<input key={resetFileKey} ref={this.setRef} type='file' multiple={false} onChange={this.handleChange} disabled={disabled} style={{ display: 'none' }} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
UploadButton.propTypes = {
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
onSelectFile: PropTypes.func.isRequired,
|
|
||||||
style: PropTypes.object,
|
|
||||||
resetFileKey: PropTypes.number,
|
|
||||||
intl: PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default injectIntl(UploadButton);
|
|
@@ -1,44 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import Permalink from '../../../components/permalink';
|
|
||||||
import Avatar from '../../../components/avatar';
|
|
||||||
import DisplayName from '../../../components/display_name';
|
|
||||||
import emojify from '../../../emoji';
|
|
||||||
import IconButton from '../../../components/icon_button';
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
|
||||||
reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => {
|
|
||||||
const content = { __html: emojify(account.get('note')) };
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='account-authorize__wrapper'>
|
|
||||||
<div className='account-authorize'>
|
|
||||||
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
|
|
||||||
<div className='account-authorize__avatar'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div>
|
|
||||||
<DisplayName account={account} />
|
|
||||||
</Permalink>
|
|
||||||
|
|
||||||
<div className='account__header__content' dangerouslySetInnerHTML={content} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='account--panel'>
|
|
||||||
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div>
|
|
||||||
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
AccountAuthorize.propTypes = {
|
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
|
||||||
onAuthorize: PropTypes.func.isRequired,
|
|
||||||
onReject: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default injectIntl(AccountAuthorize);
|
|
@@ -1,66 +0,0 @@
|
|||||||
import Column from '../ui/components/column';
|
|
||||||
import ColumnLink from '../ui/components/column_link';
|
|
||||||
import ColumnSubheading from '../ui/components/column_subheading';
|
|
||||||
import { Link } from 'react-router';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
|
||||||
public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
|
||||||
navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation'},
|
|
||||||
settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings'},
|
|
||||||
community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
|
||||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
|
||||||
sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
|
||||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
|
||||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
|
||||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
|
||||||
info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
me: state.getIn(['accounts', state.getIn(['meta', 'me'])])
|
|
||||||
});
|
|
||||||
|
|
||||||
const GettingStarted = ({ intl, me }) => {
|
|
||||||
let followRequests = '';
|
|
||||||
|
|
||||||
if (me.get('locked')) {
|
|
||||||
followRequests = <ColumnLink icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile={true}>
|
|
||||||
<div className='getting-started__wrapper'>
|
|
||||||
<ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)}/>
|
|
||||||
<ColumnLink icon='users' hideOnMobile={true} text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />
|
|
||||||
<ColumnLink icon='globe' hideOnMobile={true} text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />
|
|
||||||
<ColumnLink icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
|
|
||||||
{followRequests}
|
|
||||||
<ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
|
|
||||||
<ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
|
|
||||||
<ColumnSubheading text={intl.formatMessage(messages.settings_subheading)}/>
|
|
||||||
<ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
|
|
||||||
<ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
|
|
||||||
<ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<div className='static-content getting-started'>
|
|
||||||
<p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Column>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
GettingStarted.propTypes = {
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
me: ImmutablePropTypes.map.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(injectIntl(GettingStarted));
|
|
@@ -1,89 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import StatusListContainer from '../ui/containers/status_list_container';
|
|
||||||
import Column from '../ui/components/column';
|
|
||||||
import {
|
|
||||||
refreshTimeline,
|
|
||||||
updateTimeline,
|
|
||||||
deleteFromTimelines
|
|
||||||
} from '../../actions/timelines';
|
|
||||||
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import createStream from '../../stream';
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
hasUnread: state.getIn(['timelines', 'tag', 'unread']) > 0,
|
|
||||||
streamingAPIBaseURL: state.getIn(['meta', 'streaming_api_base_url']),
|
|
||||||
accessToken: state.getIn(['meta', 'access_token'])
|
|
||||||
});
|
|
||||||
|
|
||||||
class HashtagTimeline extends React.PureComponent {
|
|
||||||
|
|
||||||
_subscribe (dispatch, id) {
|
|
||||||
const { streamingAPIBaseURL, accessToken } = this.props;
|
|
||||||
|
|
||||||
this.subscription = createStream(streamingAPIBaseURL, accessToken, `hashtag&tag=${id}`, {
|
|
||||||
|
|
||||||
received (data) {
|
|
||||||
switch(data.event) {
|
|
||||||
case 'update':
|
|
||||||
dispatch(updateTimeline('tag', JSON.parse(data.payload)));
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
dispatch(deleteFromTimelines(data.payload));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_unsubscribe () {
|
|
||||||
if (typeof this.subscription !== 'undefined') {
|
|
||||||
this.subscription.close();
|
|
||||||
this.subscription = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
const { dispatch } = this.props;
|
|
||||||
const { id } = this.props.params;
|
|
||||||
|
|
||||||
dispatch(refreshTimeline('tag', id));
|
|
||||||
this._subscribe(dispatch, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
|
||||||
if (nextProps.params.id !== this.props.params.id) {
|
|
||||||
this.props.dispatch(refreshTimeline('tag', nextProps.params.id));
|
|
||||||
this._unsubscribe();
|
|
||||||
this._subscribe(this.props.dispatch, nextProps.params.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
this._unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { id, hasUnread } = this.props.params;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Column icon='hashtag' active={hasUnread} heading={id}>
|
|
||||||
<ColumnBackButtonSlim />
|
|
||||||
<StatusListContainer scrollKey='hashtag_timeline' type='tag' id={id} emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />} />
|
|
||||||
</Column>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
HashtagTimeline.propTypes = {
|
|
||||||
params: PropTypes.object.isRequired,
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
streamingAPIBaseURL: PropTypes.string.isRequired,
|
|
||||||
accessToken: PropTypes.string.isRequired,
|
|
||||||
hasUnread: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(HashtagTimeline);
|
|
@@ -1,50 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
import ColumnCollapsable from '../../../components/column_collapsable';
|
|
||||||
import SettingToggle from '../../notifications/components/setting_toggle';
|
|
||||||
import SettingText from './setting_text';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
|
|
||||||
settings: { id: 'home.settings', defaultMessage: 'Column settings' }
|
|
||||||
});
|
|
||||||
|
|
||||||
class ColumnSettings extends React.PureComponent {
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { settings, onChange, onSave, intl } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ColumnCollapsable icon='sliders' title={intl.formatMessage(messages.settings)} fullHeight={209} onCollapse={onSave}>
|
|
||||||
<div className='column-settings__outer'>
|
|
||||||
<span className='column-settings__section'><FormattedMessage id='home.column_settings.basic' defaultMessage='Basic' /></span>
|
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
|
||||||
<SettingToggle settings={settings} settingKey={['shows', 'reblog']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_reblogs' defaultMessage='Show boosts' />} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
|
||||||
<SettingToggle settings={settings} settingKey={['shows', 'reply']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_replies' defaultMessage='Show replies' />} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
|
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
|
||||||
<SettingText settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ColumnCollapsable>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnSettings.propTypes = {
|
|
||||||
settings: ImmutablePropTypes.map.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
onSave: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default injectIntl(ColumnSettings);
|
|
@@ -1,37 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import StatusListContainer from '../ui/containers/status_list_container';
|
|
||||||
import Column from '../ui/components/column';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
import ColumnSettingsContainer from './containers/column_settings_container';
|
|
||||||
import { Link } from 'react-router';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
title: { id: 'column.home', defaultMessage: 'Home' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0
|
|
||||||
});
|
|
||||||
|
|
||||||
class HomeTimeline extends React.PureComponent {
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { intl, hasUnread } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Column icon='home' active={hasUnread} heading={intl.formatMessage(messages.title)}>
|
|
||||||
<ColumnSettingsContainer />
|
|
||||||
<StatusListContainer {...this.props} scrollKey='home_timeline' type='home' emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage="You aren't following anyone yet. Visit {public} or use search to get started and meet other users." values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />} />
|
|
||||||
</Column>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
HomeTimeline.propTypes = {
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
hasUnread: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(injectIntl(HomeTimeline));
|
|
@@ -1,26 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
clear: { id: 'notifications.clear', defaultMessage: 'Clear notifications' }
|
|
||||||
});
|
|
||||||
|
|
||||||
class ClearColumnButton extends React.Component {
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { intl } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div role='button' title={intl.formatMessage(messages.clear)} className='column-icon column-icon-clear' tabIndex='0' onClick={this.props.onClick}>
|
|
||||||
<i className='fa fa-eraser' />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearColumnButton.propTypes = {
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default injectIntl(ClearColumnButton);
|
|
@@ -1,70 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
import ColumnCollapsable from '../../../components/column_collapsable';
|
|
||||||
import SettingToggle from './setting_toggle';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
settings: { id: 'notifications.settings', defaultMessage: 'Column settings' }
|
|
||||||
});
|
|
||||||
|
|
||||||
class ColumnSettings extends React.PureComponent {
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { settings, intl, onChange, onSave } = this.props;
|
|
||||||
|
|
||||||
const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
|
|
||||||
const showStr = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
|
|
||||||
const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ColumnCollapsable icon='sliders' title={intl.formatMessage(messages.settings)} fullHeight={616} onCollapse={onSave}>
|
|
||||||
<div className='column-settings__outer'>
|
|
||||||
<span className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
|
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
|
||||||
<SettingToggle settings={settings} settingKey={['alerts', 'follow']} onChange={onChange} label={alertStr} />
|
|
||||||
<SettingToggle settings={settings} settingKey={['shows', 'follow']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingToggle settings={settings} settingKey={['sounds', 'follow']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span>
|
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
|
||||||
<SettingToggle settings={settings} settingKey={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
|
|
||||||
<SettingToggle settings={settings} settingKey={['shows', 'favourite']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingToggle settings={settings} settingKey={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
|
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
|
||||||
<SettingToggle settings={settings} settingKey={['alerts', 'mention']} onChange={onChange} label={alertStr} />
|
|
||||||
<SettingToggle settings={settings} settingKey={['shows', 'mention']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingToggle settings={settings} settingKey={['sounds', 'mention']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className='column-settings__section'><FormattedMessage id='notifications.column_settings.reblog' defaultMessage='Boosts:' /></span>
|
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
|
||||||
<SettingToggle settings={settings} settingKey={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
|
|
||||||
<SettingToggle settings={settings} settingKey={['shows', 'reblog']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingToggle settings={settings} settingKey={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ColumnCollapsable>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnSettings.propTypes = {
|
|
||||||
settings: ImmutablePropTypes.map.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
onSave: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.shape({
|
|
||||||
formatMessage: PropTypes.func.isRequired
|
|
||||||
}).isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default injectIntl(ColumnSettings);
|
|
@@ -1,20 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import Toggle from 'react-toggle';
|
|
||||||
|
|
||||||
const SettingToggle = ({ settings, settingKey, label, onChange, htmlFor = '' }) => (
|
|
||||||
<label htmlFor={htmlFor} className='setting-toggle__label'>
|
|
||||||
<Toggle checked={settings.getIn(settingKey)} onChange={(e) => onChange(settingKey, e.target.checked)} />
|
|
||||||
<span className='setting-toggle'>{label}</span>
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
|
|
||||||
SettingToggle.propTypes = {
|
|
||||||
settings: ImmutablePropTypes.map.isRequired,
|
|
||||||
settingKey: PropTypes.array.isRequired,
|
|
||||||
label: PropTypes.node.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
htmlFor: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SettingToggle;
|
|
@@ -1,95 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import StatusListContainer from '../ui/containers/status_list_container';
|
|
||||||
import Column from '../ui/components/column';
|
|
||||||
import {
|
|
||||||
refreshTimeline,
|
|
||||||
updateTimeline,
|
|
||||||
deleteFromTimelines,
|
|
||||||
connectTimeline,
|
|
||||||
disconnectTimeline
|
|
||||||
} from '../../actions/timelines';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
|
|
||||||
import createStream from '../../stream';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
title: { id: 'column.public', defaultMessage: 'Federated timeline' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
hasUnread: state.getIn(['timelines', 'public', 'unread']) > 0,
|
|
||||||
streamingAPIBaseURL: state.getIn(['meta', 'streaming_api_base_url']),
|
|
||||||
accessToken: state.getIn(['meta', 'access_token'])
|
|
||||||
});
|
|
||||||
|
|
||||||
let subscription;
|
|
||||||
|
|
||||||
class PublicTimeline extends React.PureComponent {
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
const { dispatch, streamingAPIBaseURL, accessToken } = this.props;
|
|
||||||
|
|
||||||
dispatch(refreshTimeline('public'));
|
|
||||||
|
|
||||||
if (typeof subscription !== 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
subscription = createStream(streamingAPIBaseURL, accessToken, 'public', {
|
|
||||||
|
|
||||||
connected () {
|
|
||||||
dispatch(connectTimeline('public'));
|
|
||||||
},
|
|
||||||
|
|
||||||
reconnected () {
|
|
||||||
dispatch(connectTimeline('public'));
|
|
||||||
},
|
|
||||||
|
|
||||||
disconnected () {
|
|
||||||
dispatch(disconnectTimeline('public'));
|
|
||||||
},
|
|
||||||
|
|
||||||
received (data) {
|
|
||||||
switch(data.event) {
|
|
||||||
case 'update':
|
|
||||||
dispatch(updateTimeline('public', JSON.parse(data.payload)));
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
dispatch(deleteFromTimelines(data.payload));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
// if (typeof subscription !== 'undefined') {
|
|
||||||
// subscription.close();
|
|
||||||
// subscription = null;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { intl, hasUnread } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Column icon='globe' active={hasUnread} heading={intl.formatMessage(messages.title)}>
|
|
||||||
<ColumnBackButtonSlim />
|
|
||||||
<StatusListContainer {...this.props} type='public' scrollKey='public_timeline' emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other instances to fill it up' />} />
|
|
||||||
</Column>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
PublicTimeline.propTypes = {
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
streamingAPIBaseURL: PropTypes.string.isRequired,
|
|
||||||
accessToken: PropTypes.string.isRequired,
|
|
||||||
hasUnread: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(injectIntl(PublicTimeline));
|
|
@@ -1,82 +0,0 @@
|
|||||||
import ColumnHeader from './column_header';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const easingOutQuint = (x, t, b, c, d) => c*((t=t/d-1)*t*t*t*t + 1) + b;
|
|
||||||
|
|
||||||
const scrollTop = (node) => {
|
|
||||||
const startTime = Date.now();
|
|
||||||
const offset = node.scrollTop;
|
|
||||||
const targetY = -offset;
|
|
||||||
const duration = 1000;
|
|
||||||
let interrupt = false;
|
|
||||||
|
|
||||||
const step = () => {
|
|
||||||
const elapsed = Date.now() - startTime;
|
|
||||||
const percentage = elapsed / duration;
|
|
||||||
|
|
||||||
if (percentage > 1 || interrupt) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.scrollTop = easingOutQuint(0, elapsed, offset, targetY, duration);
|
|
||||||
requestAnimationFrame(step);
|
|
||||||
};
|
|
||||||
|
|
||||||
step();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
interrupt = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class Column extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.handleHeaderClick = this.handleHeaderClick.bind(this);
|
|
||||||
this.handleWheel = this.handleWheel.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleHeaderClick () {
|
|
||||||
const scrollable = ReactDOM.findDOMNode(this).querySelector('.scrollable');
|
|
||||||
if (!scrollable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._interruptScrollAnimation = scrollTop(scrollable);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleWheel () {
|
|
||||||
if (typeof this._interruptScrollAnimation !== 'undefined') {
|
|
||||||
this._interruptScrollAnimation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { heading, icon, children, active, hideHeadingOnMobile } = this.props;
|
|
||||||
|
|
||||||
let columnHeaderId = null
|
|
||||||
let header = '';
|
|
||||||
|
|
||||||
if (heading) {
|
|
||||||
columnHeaderId = heading.replace(/ /g, '-')
|
|
||||||
header = <ColumnHeader icon={icon} active={active} type={heading} onClick={this.handleHeaderClick} hideOnMobile={hideHeadingOnMobile} columnHeaderId={columnHeaderId}/>;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div role='region' aria-labelledby={columnHeaderId} className='column' onWheel={this.handleWheel}>
|
|
||||||
{header}
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Column.propTypes = {
|
|
||||||
heading: PropTypes.string,
|
|
||||||
icon: PropTypes.string,
|
|
||||||
children: PropTypes.node,
|
|
||||||
active: PropTypes.bool,
|
|
||||||
hideHeadingOnMobile: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Column;
|
|
@@ -1,19 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class ColumnsArea extends React.PureComponent {
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<div className='columns-area'>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnsArea.propTypes = {
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ColumnsArea;
|
|
@@ -1,50 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
import Button from '../../../components/button';
|
|
||||||
|
|
||||||
class ConfirmationModal extends React.PureComponent {
|
|
||||||
|
|
||||||
constructor (props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this.handleClick = this.handleClick.bind(this);
|
|
||||||
this.handleCancel = this.handleCancel.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick () {
|
|
||||||
this.props.onClose();
|
|
||||||
this.props.onConfirm();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCancel (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.props.onClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { intl, message, confirm, onConfirm, onClose } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='modal-root__modal confirmation-modal'>
|
|
||||||
<div className='confirmation-modal__container'>
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='confirmation-modal__action-bar'>
|
|
||||||
<div><a href='#' onClick={this.handleCancel}><FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' /></a></div>
|
|
||||||
<Button text={confirm} onClick={this.handleClick} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfirmationModal.propTypes = {
|
|
||||||
message: PropTypes.node.isRequired,
|
|
||||||
confirm: PropTypes.string.isRequired,
|
|
||||||
onClose: PropTypes.func.isRequired,
|
|
||||||
onConfirm: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default injectIntl(ConfirmationModal);
|
|
@@ -1,33 +0,0 @@
|
|||||||
import Link from 'http-link-header';
|
|
||||||
import querystring from 'querystring';
|
|
||||||
|
|
||||||
Link.parseAttrs = (link, parts) => {
|
|
||||||
let match = null
|
|
||||||
let attr = ''
|
|
||||||
let value = ''
|
|
||||||
let attrs = ''
|
|
||||||
|
|
||||||
let uriAttrs = /<(.*)>;\s*(.*)/gi.exec(parts)
|
|
||||||
|
|
||||||
if(uriAttrs) {
|
|
||||||
attrs = uriAttrs[2]
|
|
||||||
link = Link.parseParams(link, uriAttrs[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
while(match = Link.attrPattern.exec(attrs)) { // eslint-disable-line no-cond-assign
|
|
||||||
attr = match[1].toLowerCase()
|
|
||||||
value = match[4] || match[3] || match[2]
|
|
||||||
|
|
||||||
if( /\*$/.test(attr)) {
|
|
||||||
Link.setAttr(link, attr, Link.parseExtendedValue(value))
|
|
||||||
} else if(/%/.test(value)) {
|
|
||||||
Link.setAttr(link, attr, querystring.decode(value))
|
|
||||||
} else {
|
|
||||||
Link.setAttr(link, attr, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return link
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Link;
|
|
@@ -1,120 +0,0 @@
|
|||||||
/**
|
|
||||||
* ملاحظة للمساهمين و المساهمات :
|
|
||||||
* لجعل مهمة المساهمين الآخرين أسهل، رجاءا تذكر :
|
|
||||||
* 1. إضافة سلسلة جديدة هنا؛ و
|
|
||||||
* 2. لإزالة السلاسل القديمة التي لم تعد هناك حاجة إليها. و
|
|
||||||
* 3. لفرز السلاسل تبعا للأبجدية
|
|
||||||
* شكر!
|
|
||||||
*/
|
|
||||||
const ar = {
|
|
||||||
"account.block": "حظر @{name}",
|
|
||||||
"account.disclaimer": "هذا المستخدم من مثيل خادم آخر. قد يكون هذا الرقم أكبر.",
|
|
||||||
"account.edit_profile": "تعديل الملف الشخصي",
|
|
||||||
"account.follow": "إتبع",
|
|
||||||
"account.followers": "المتابعون",
|
|
||||||
"account.follows_you": "يتابعك",
|
|
||||||
"account.follows": "يتبع",
|
|
||||||
"account.mention": "أُذكُر @{name}",
|
|
||||||
"account.mute": "أكتم @{name}",
|
|
||||||
"account.posts": "المشاركات",
|
|
||||||
"account.report": "أبلغ عن @{name}",
|
|
||||||
"account.requested": "في انتظار الموافقة",
|
|
||||||
"account.unblock": "إلغاء الحظر عن @{name}",
|
|
||||||
"account.unfollow": "إلغاء المتابعة",
|
|
||||||
"account.unmute": "إلغاء الكتم عن @{name}",
|
|
||||||
"boost_modal.combo": "يمكنك الضغط على {combo} لتخطي هذا مرة أخرى",
|
|
||||||
"column_back_button.label": "العودة",
|
|
||||||
"column.blocks": "الحسابات المحجوبة",
|
|
||||||
"column.community": "الخيط العام المحلي",
|
|
||||||
"column.favourites": "المفضلة",
|
|
||||||
"column.follow_requests": "طلبات المتابعة",
|
|
||||||
"column.home": "الرئيسية",
|
|
||||||
"column.mutes": "الحسابات المكتومة",
|
|
||||||
"column.notifications": "الإشعارات",
|
|
||||||
"column.public": "الخيط العام الموحد",
|
|
||||||
"compose_form.placeholder": "ماذا يدور في ذهنك ؟",
|
|
||||||
"compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
|
|
||||||
"compose_form.publish": "بَوِّق",
|
|
||||||
"compose_form.sensitive": "ضع علامة حساس على الوسائط",
|
|
||||||
"compose_form.spoiler_placeholder": "تنبيه عن المحتوى",
|
|
||||||
"compose_form.spoiler": "إخفاء النص وراء التحذير",
|
|
||||||
"emoji_button.label": "إيموجي",
|
|
||||||
"emoji_button.search": "بحث ...",
|
|
||||||
"emoji_button.people": "أشخاص",
|
|
||||||
"emoji_button.nature": "طبيعة",
|
|
||||||
"emoji_button.food": "أكل و شرب",
|
|
||||||
"emoji_button.activity": "أنشطة",
|
|
||||||
"emoji_button.travel": "أماكن و أسفار",
|
|
||||||
"emoji_button.objects": "أشياء",
|
|
||||||
"emoji_button.symbols": "رموز",
|
|
||||||
"emoji_button.flags": "أعلام",
|
|
||||||
"empty_column.community": "الخيط العام المحلي فارغ. قم بتحرير شيء ما كبداية.",
|
|
||||||
"empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
|
|
||||||
"empty_column.home.public_timeline": "الخيط العام",
|
|
||||||
"empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
|
|
||||||
"empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
|
|
||||||
"empty_column.public": "لا يوجد شيء هنا ! قم بتحرير شيء ما بشكل عام، أو اتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام.",
|
|
||||||
"follow_request.authorize": "ترخيص",
|
|
||||||
"follow_request.reject": "رفض",
|
|
||||||
"getting_started.apps": "عدة تطبيقات مختلفة متوفرة",
|
|
||||||
"getting_started.heading": "إستعدّ للبدء",
|
|
||||||
"getting_started.about_addressing": "يمكنك متابعة الأشخاص إذا كنت تعرف اسم المستخدم الخاص بهم والنطاق الذي هم عليه عن طريق إدخال عنوان شبيه بالبريد الإلكتروني في الحقل المخصص للبحث.",
|
|
||||||
"getting_started.about_shortcuts": "إذا كان المستخدم المستهدف في نفس النطاق الذي تستخدمه، فإسم المستخدم وحده يكفي. وتنطبق نفس القاعدة على ذكر الأشخاص في المنشورات و التبويقات.",
|
|
||||||
"getting_started.open_source_notice": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على GitHub {github}. {apps}.",
|
|
||||||
"home.column_settings.advanced": "متقدمة",
|
|
||||||
"home.column_settings.basic": "أساسية",
|
|
||||||
"home.column_settings.filter_regex": "تصفية حسب التعبيرات العادية",
|
|
||||||
"home.column_settings.show_reblogs": "عرض الترقيات",
|
|
||||||
"home.column_settings.show_replies": "عرض الردود",
|
|
||||||
"home.settings": "إعدادات العمود",
|
|
||||||
"lightbox.close": "إغلاق",
|
|
||||||
"loading_indicator.label": "تحميل ...",
|
|
||||||
"media_gallery.toggle_visible": "Toggle visibility",
|
|
||||||
"missing_indicator.label": "تعذر العثور عليه",
|
|
||||||
"navigation_bar.blocks": "الحسابات المحجوبة",
|
|
||||||
"navigation_bar.community_timeline": "الخيط العام المحلي",
|
|
||||||
"navigation_bar.edit_profile": "تعديل الملف الشخصي",
|
|
||||||
"navigation_bar.preferences": "التفضيلات",
|
|
||||||
"navigation_bar.community_timeline": "الخيط العام المحلي",
|
|
||||||
"navigation_bar.public_timeline": "الخيط العام الموحد",
|
|
||||||
"navigation_bar.logout": "خروج",
|
|
||||||
"reply_indicator.cancel": "إلغاء",
|
|
||||||
"search.placeholder": "ابحث",
|
|
||||||
"search.account": "حساب",
|
|
||||||
"search.hashtag": "وسم",
|
|
||||||
"status.mention": "أذكُر @{name}",
|
|
||||||
"status.delete": "إحذف",
|
|
||||||
"status.reply": "ردّ",
|
|
||||||
"status.reblog": "رَقِّي",
|
|
||||||
"status.favourite": "أضف إلى المفضلة",
|
|
||||||
"status.reblogged_by": "{name} رقى",
|
|
||||||
"status.sensitive_warning": "محتوى حساس",
|
|
||||||
"status.sensitive_toggle": "اضغط للعرض",
|
|
||||||
"status.show_more": "أظهر المزيد",
|
|
||||||
"status.show_less": "إعرض أقلّ",
|
|
||||||
"status.open": "وسع هذه المشاركة",
|
|
||||||
"status.report": "إبلِغ عن @{name}",
|
|
||||||
"tabs_bar.compose": "تحرير",
|
|
||||||
"tabs_bar.home": "الرئيسية",
|
|
||||||
"tabs_bar.mentions": "الإشارات",
|
|
||||||
"tabs_bar.public": "الخيط العام الموحد",
|
|
||||||
"tabs_bar.notifications": "الإشعارات",
|
|
||||||
"upload_button.label": "إضافة وسائط",
|
|
||||||
"upload_form.undo": "إلغاء",
|
|
||||||
"notification.follow": "{name} يتبعك",
|
|
||||||
"notification.favourite": "{name} أعجب بمنشورك",
|
|
||||||
"notification.reblog": "{name} قام بترقية تبويقك",
|
|
||||||
"notification.mention": "{name} ذكرك",
|
|
||||||
"notifications.column_settings.alert": "إشعارات سطح المكتب",
|
|
||||||
"notifications.column_settings.show": "إعرِضها في عمود",
|
|
||||||
"notifications.column_settings.follow": "متابعُون جُدُد :",
|
|
||||||
"notifications.column_settings.favourite": "المُفَضَّلة :",
|
|
||||||
"notifications.column_settings.mention": "الإشارات :",
|
|
||||||
"notifications.column_settings.reblog": "الترقيّات:",
|
|
||||||
"video_player.toggle_sound": "تبديل الصوت",
|
|
||||||
"video_player.toggle_visible": "إظهار / إخفاء الفيديو",
|
|
||||||
"video_player.expand": "توسيع الفيديو",
|
|
||||||
"video_player.video_error": "تعذر تشغيل الفيديو",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ar;
|
|
@@ -1,68 +0,0 @@
|
|||||||
const bg = {
|
|
||||||
"column_back_button.label": "Назад",
|
|
||||||
"lightbox.close": "Затвори",
|
|
||||||
"loading_indicator.label": "Зареждане...",
|
|
||||||
"status.mention": "Споменаване",
|
|
||||||
"status.delete": "Изтриване",
|
|
||||||
"status.reply": "Отговор",
|
|
||||||
"status.reblog": "Споделяне",
|
|
||||||
"status.favourite": "Предпочитани",
|
|
||||||
"status.reblogged_by": "{name} сподели",
|
|
||||||
"status.sensitive_warning": "Деликатно съдържание",
|
|
||||||
"status.sensitive_toggle": "Покажи",
|
|
||||||
"video_player.toggle_sound": "Звук",
|
|
||||||
"account.mention": "Споменаване",
|
|
||||||
"account.edit_profile": "Редактирай профила си",
|
|
||||||
"account.unblock": "Не блокирай",
|
|
||||||
"account.unfollow": "Не следвай",
|
|
||||||
"account.block": "Блокирай",
|
|
||||||
"account.follow": "Последвай",
|
|
||||||
"account.posts": "Публикации",
|
|
||||||
"account.follows": "Следвам",
|
|
||||||
"account.followers": "Последователи",
|
|
||||||
"account.follows_you": "Твой последовател",
|
|
||||||
"account.requested": "В очакване на одобрение",
|
|
||||||
"getting_started.heading": "Първи стъпки",
|
|
||||||
"getting_started.about_addressing": "Можеш да последваш потребител, ако знаеш потребителското му име и домейна, на който се намира, като в полето за търсене ги въведеш по този начин: име@домейн",
|
|
||||||
"getting_started.about_shortcuts": "Ако с търсения потребител се намирате на един и същ домейн, достатъчно е да въведеш само името. Същото важи и за споменаване на хора в публикации.",
|
|
||||||
"getting_started.about_developer": "Можеш да потърсиш разработчика на този проект като: Gargron@mastodon.social",
|
|
||||||
"getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.",
|
|
||||||
"column.home": "Начало",
|
|
||||||
"column.mentions": "Споменавания",
|
|
||||||
"column.public": "Публичен канал",
|
|
||||||
"column.notifications": "Известия",
|
|
||||||
"tabs_bar.compose": "Съставяне",
|
|
||||||
"tabs_bar.home": "Начало",
|
|
||||||
"tabs_bar.mentions": "Споменавания",
|
|
||||||
"tabs_bar.public": "Публичен канал",
|
|
||||||
"tabs_bar.notifications": "Известия",
|
|
||||||
"compose_form.placeholder": "Какво си мислиш?",
|
|
||||||
"compose_form.publish": "Раздумай",
|
|
||||||
"compose_form.sensitive": "Отбележи съдържанието като деликатно",
|
|
||||||
"compose_form.spoiler": "Скрий текста зад предупреждение",
|
|
||||||
"compose_form.private": "Отбележи като поверително",
|
|
||||||
"compose_form.privacy_disclaimer": "Поверителни публикации ще бъдат изпратени до споменатите потребители на {domains}. Доверяваш ли се на {domainsCount, plural, one {that server} other {those servers}}, че няма да издаде твоята публикация?",
|
|
||||||
"compose_form.unlisted": "Не показвай в публичния канал",
|
|
||||||
"navigation_bar.edit_profile": "Редактирай профил",
|
|
||||||
"navigation_bar.preferences": "Предпочитания",
|
|
||||||
"navigation_bar.public_timeline": "Публичен канал",
|
|
||||||
"navigation_bar.logout": "Излизане",
|
|
||||||
"reply_indicator.cancel": "Отказ",
|
|
||||||
"search.placeholder": "Търсене",
|
|
||||||
"search.account": "Акаунт",
|
|
||||||
"search.hashtag": "Хаштаг",
|
|
||||||
"upload_button.label": "Добави медия",
|
|
||||||
"upload_form.undo": "Отмяна",
|
|
||||||
"notification.follow": "{name} те последва",
|
|
||||||
"notification.favourite": "{name} хареса твоята публикация",
|
|
||||||
"notification.reblog": "{name} сподели твоята публикация",
|
|
||||||
"notification.mention": "{name} те спомена",
|
|
||||||
"notifications.column_settings.alert": "Десктоп известия",
|
|
||||||
"notifications.column_settings.show": "Покажи в колона",
|
|
||||||
"notifications.column_settings.follow": "Нови последователи:",
|
|
||||||
"notifications.column_settings.favourite": "Предпочитани:",
|
|
||||||
"notifications.column_settings.mention": "Споменавания:",
|
|
||||||
"notifications.column_settings.reblog": "Споделяния:",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default bg;
|
|
@@ -1,68 +0,0 @@
|
|||||||
const eo = {
|
|
||||||
"column_back_button.label": "Reveni",
|
|
||||||
"lightbox.close": "Fermi",
|
|
||||||
"loading_indicator.label": "Ŝarĝanta...",
|
|
||||||
"status.mention": "Mencii @{name}",
|
|
||||||
"status.delete": "Forigi",
|
|
||||||
"status.reply": "Respondi",
|
|
||||||
"status.reblog": "Diskonigi",
|
|
||||||
"status.favourite": "Favori",
|
|
||||||
"status.reblogged_by": "{name} diskonigita",
|
|
||||||
"status.sensitive_warning": "Tikla enhavo",
|
|
||||||
"status.sensitive_toggle": "Alklaki por vidi",
|
|
||||||
"video_player.toggle_sound": "Aktivigi sonojn",
|
|
||||||
"account.mention": "Mencii @{name}",
|
|
||||||
"account.edit_profile": "Redakti la profilon",
|
|
||||||
"account.unblock": "Malbloki @{name}",
|
|
||||||
"account.unfollow": "Malsekvi",
|
|
||||||
"account.block": "Bloki @{name}",
|
|
||||||
"account.follow": "Sekvi",
|
|
||||||
"account.posts": "Mesaĝoj",
|
|
||||||
"account.follows": "Sekvatoj",
|
|
||||||
"account.followers": "Sekvantoj",
|
|
||||||
"account.follows_you": "Sekvas vin",
|
|
||||||
"account.requested": "Atendas aprobon",
|
|
||||||
"getting_started.heading": "Por komenci",
|
|
||||||
"getting_started.about_addressing": "Vi povas sekvi homojn se vi konas la uzantnomon kaj domajnon tajpinte retpoŝtecan adreson en la serĉilon.",
|
|
||||||
"getting_started.about_shortcuts": "Se la celita uzanto troviĝas en la sama domajno de vi, uzi nur la uzantnomon sufiĉos. La sama regulo validas por mencii aliajn uzantojn en mesaĝo.",
|
|
||||||
"getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en github je {github}. {apps}.",
|
|
||||||
"column.home": "Hejmo",
|
|
||||||
"column.community": "Loka tempolinio",
|
|
||||||
"column.public": "Fratara tempolinio",
|
|
||||||
"column.notifications": "Sciigoj",
|
|
||||||
"tabs_bar.compose": "Ekskribi",
|
|
||||||
"tabs_bar.home": "Hejmo",
|
|
||||||
"tabs_bar.mentions": "Sciigoj",
|
|
||||||
"tabs_bar.public": "Fratara tempolinio",
|
|
||||||
"tabs_bar.notifications": "Sciigoj",
|
|
||||||
"compose_form.placeholder": "Pri kio vi pensas?",
|
|
||||||
"compose_form.publish": "Hup",
|
|
||||||
"compose_form.sensitive": "Marki ke la enhavo estas tikla",
|
|
||||||
"compose_form.spoiler": "Kaŝi la tekston malantaŭ averto",
|
|
||||||
"compose_form.private": "Marki ke la enhavo estas privata",
|
|
||||||
"compose_form.privacy_disclaimer": "Via privata mesaĝo estos sendita nur al menciitaj uzantoj en {domains}. Ĉu vi fidas {domainsCount, plural, one {tiun servilon} other {tiujn servilojn}}? Mesaĝa privateco funkcias nur en aperaĵoj de Mastodon. Se {domains} {domainsCount, plural, one {ne estas aperaĵo de Mastodon} other {ne estas aperaĵoj de Mastodon}}, estos neniu indiko ke via mesaĝo estas privata, kaj ĝi povus esti diskonigita aŭ videbligita al necelitaj ricevantoj.",
|
|
||||||
"compose_form.unlisted": "Ne afiŝi en publikaj tempolinioj",
|
|
||||||
"navigation_bar.edit_profile": "Redakti la profilon",
|
|
||||||
"navigation_bar.preferences": "Preferoj",
|
|
||||||
"navigation_bar.community_timeline": "Loka tempolinio",
|
|
||||||
"navigation_bar.public_timeline": "Fratara tempolinio",
|
|
||||||
"navigation_bar.logout": "Elsaluti",
|
|
||||||
"reply_indicator.cancel": "Rezigni",
|
|
||||||
"search.placeholder": "Serĉi",
|
|
||||||
"search.account": "Konto",
|
|
||||||
"search.hashtag": "Kradvorto",
|
|
||||||
"upload_button.label": "Aldoni enhavaĵon",
|
|
||||||
"upload_form.undo": "Malfari",
|
|
||||||
"notification.follow": "{name} sekvis vin",
|
|
||||||
"notification.favourite": "{name} favoris vian mesaĝon",
|
|
||||||
"notification.reblog": "{name} diskonigis vian mesaĝon",
|
|
||||||
"notification.mention": "{name} menciis vin",
|
|
||||||
"notifications.column_settings.alert": "Retumilaj atentigoj",
|
|
||||||
"notifications.column_settings.show": "Montri en kolono",
|
|
||||||
"notifications.column_settings.follow": "Novaj sekvantoj:",
|
|
||||||
"notifications.column_settings.favourite": "Favoroj:",
|
|
||||||
"notifications.column_settings.mention": "Mencioj:",
|
|
||||||
"notifications.column_settings.reblog": "Diskonigoj:",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default eo;
|
|
@@ -1,93 +0,0 @@
|
|||||||
const es = {
|
|
||||||
"column_back_button.label": "Atrás",
|
|
||||||
"lightbox.close": "Cerrar",
|
|
||||||
"loading_indicator.label": "Cargando...",
|
|
||||||
"status.mention": "Mencionar",
|
|
||||||
"status.delete": "Borrar",
|
|
||||||
"status.reply": "Responder",
|
|
||||||
"status.reblog": "Retoot",
|
|
||||||
"status.favourite": "Favorito",
|
|
||||||
"status.reblogged_by": "Retooteado por {name}",
|
|
||||||
"status.sensitive_warning": "Contenido sensible",
|
|
||||||
"status.sensitive_toggle": "Click para ver",
|
|
||||||
"status.show_more": "Mostrar más",
|
|
||||||
"status.show_less": "Mostrar menos",
|
|
||||||
"status.open": "Expandir estado",
|
|
||||||
"status.report": "Reportar",
|
|
||||||
"video_player.toggle_sound": "Act/Desac. sonido",
|
|
||||||
"account.mention": "Mencionar",
|
|
||||||
"account.edit_profile": "Editar perfil",
|
|
||||||
"account.unblock": "Desbloquear",
|
|
||||||
"account.unfollow": "Dejar de seguir",
|
|
||||||
"account.mute": "Silenciar",
|
|
||||||
"account.block": "Bloquear",
|
|
||||||
"account.follow": "Seguir",
|
|
||||||
"account.posts": "Publicaciones",
|
|
||||||
"account.follows": "Seguir",
|
|
||||||
"account.followers": "Seguidores",
|
|
||||||
"account.follows_you": "Te sigue",
|
|
||||||
"account.requested": "Esperando aprobación",
|
|
||||||
"getting_started.heading": "Primeros pasos",
|
|
||||||
"getting_started.about_addressing": "Puedes seguir a gente si conoces su nombre de usuario y el dominio en el que están registrados, introduciendo algo similar a una dirección de correo electrónico en el formulario en la parte superior de la barra lateral.",
|
|
||||||
"getting_started.about_shortcuts": "Si el usuario que buscas está en el mismo dominio que tú, simplemente funcionará introduciendo el nombre de usuario. La misma regla se aplica para mencionar a usuarios.",
|
|
||||||
"getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}. {apps}.",
|
|
||||||
"column.home": "Inicio",
|
|
||||||
"column.community": "Historia local",
|
|
||||||
"column.public": "Historia federada",
|
|
||||||
"column.notifications": "Notificaciones",
|
|
||||||
"column.blocks": "Usuarios bloqueados",
|
|
||||||
"column.favourites": "Favoritos",
|
|
||||||
"column.follow_requests": "Solicitudes para seguirte",
|
|
||||||
"column.mutes": "Usuarios silenciados",
|
|
||||||
"tabs_bar.compose": "Redactar",
|
|
||||||
"tabs_bar.home": "Inicio",
|
|
||||||
"tabs_bar.mentions": "Menciones",
|
|
||||||
"tabs_bar.public": "Público",
|
|
||||||
"tabs_bar.notifications": "Notificaciones",
|
|
||||||
"compose_form.placeholder": "¿En qué estás pensando?",
|
|
||||||
"compose_form.publish": "Tootear",
|
|
||||||
"compose_form.sensitive": "Marcar contenido como sensible",
|
|
||||||
"compose_form.spoiler": "Ocultar texto tras advertencia",
|
|
||||||
"compose_form.spoiler_placeholder": "Advertencia de contenido",
|
|
||||||
"composer_form.private": "Marcar como privado",
|
|
||||||
"composer_form.privacy_disclaimer": "Tu estado se mostrará a los usuarios mencionados en {domains}. Tu estado podrá ser visto en otras instancias, quizás no quieras que tu estado sea visto por otros usuarios.",
|
|
||||||
"compose_form.unlisted": "No mostrar en la historia federada",
|
|
||||||
"navigation_bar.edit_profile": "Editar perfil",
|
|
||||||
"navigation_bar.preferences": "Preferencias",
|
|
||||||
"navigation_bar.community_timeline": "Historia local",
|
|
||||||
"navigation_bar.public_timeline": "Historia federada",
|
|
||||||
"navigation_bar.favourites": "Favoritos",
|
|
||||||
"navigation_bar.blocks": "Usuarios bloqueados",
|
|
||||||
"navigation_bar.info": "Información adicional",
|
|
||||||
"navigation_bar.logout": "Cerrar sesión",
|
|
||||||
"navigation_bar.follow_requests": "Solicitudes para seguirte",
|
|
||||||
"navigation_bar.mutes": "Usuarios silenciados",
|
|
||||||
"reply_indicator.cancel": "Cancelar",
|
|
||||||
"search.placeholder": "Buscar",
|
|
||||||
"search.account": "Cuenta",
|
|
||||||
"search.hashtag": "Etiqueta",
|
|
||||||
"upload_button.label": "Subir multimedia",
|
|
||||||
"upload_form.undo": "Deshacer",
|
|
||||||
"notification.follow": "{name} te empezó a seguir",
|
|
||||||
"notification.favourite": "{name} marcó tu estado como favorito",
|
|
||||||
"notification.reblog": "{name} ha retooteado tu estado",
|
|
||||||
"notification.mention": "{name} te ha mencionado",
|
|
||||||
"notifications.column_settings.alert": "Notificaciones de escritorio",
|
|
||||||
"notifications.column_settings.show": "Mostrar en columna",
|
|
||||||
"notifications.column_settings.follow": "Nuevos seguidores:",
|
|
||||||
"notifications.column_settings.favourite": "Favoritos:",
|
|
||||||
"notifications.column_settings.mention": "Menciones:",
|
|
||||||
"notifications.column_settings.reblog": "Retoots:",
|
|
||||||
"emoji_button.label": "Insertar emoji",
|
|
||||||
"privacy.public.short": "Público",
|
|
||||||
"privacy.public.long": "Mostrar en la historia federada",
|
|
||||||
"privacy.unlisted.short": "Sin federar",
|
|
||||||
"privacy.unlisted.long": "No mostrar en la historia federada",
|
|
||||||
"privacy.private.short": "Privado",
|
|
||||||
"privacy.private.long": "Sólo mostrar a seguidores",
|
|
||||||
"privacy.direct.short": "Directo",
|
|
||||||
"privacy.direct.long": "Sólo mostrar a los usuarios mencionados",
|
|
||||||
"privacy.change": "Ajustar privacidad"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default es;
|
|
@@ -1,68 +0,0 @@
|
|||||||
const fi = {
|
|
||||||
"column_back_button.label": "Takaisin",
|
|
||||||
"lightbox.close": "Sulje",
|
|
||||||
"loading_indicator.label": "Ladataan...",
|
|
||||||
"status.mention": "Mainitse @{name}",
|
|
||||||
"status.delete": "Poista",
|
|
||||||
"status.reply": "Vastaa",
|
|
||||||
"status.reblog": "Buustaa",
|
|
||||||
"status.favourite": "Tykkää",
|
|
||||||
"status.reblogged_by": "{name} buustasi",
|
|
||||||
"status.sensitive_warning": "Arkaluontoista sisältöä",
|
|
||||||
"status.sensitive_toggle": "Klikkaa nähdäksesi",
|
|
||||||
"video_player.toggle_sound": "Äänet päälle/pois",
|
|
||||||
"account.mention": "Mainitse @{name}",
|
|
||||||
"account.edit_profile": "Muokkaa",
|
|
||||||
"account.unblock": "Salli @{name}",
|
|
||||||
"account.unfollow": "Lopeta seuraaminen",
|
|
||||||
"account.block": "Estä @{name}",
|
|
||||||
"account.follow": "Seuraa",
|
|
||||||
"account.posts": "Postit",
|
|
||||||
"account.follows": "Seuraa",
|
|
||||||
"account.followers": "Seuraajia",
|
|
||||||
"account.follows_you": "Seuraa sinua",
|
|
||||||
"account.requested": "Odottaa hyväksyntää",
|
|
||||||
"getting_started.heading": "Aloitus",
|
|
||||||
"getting_started.about_addressing": "Voit seurata ihmisiä jos tiedät heidän käyttäjänimensä ja domainin missä he ovat syöttämällä e-mail-esque osoitteen Etsi kenttään.",
|
|
||||||
"getting_started.about_shortcuts": "Jos etsimäsi henkilö on samassa domainissa kuin sinä, pelkkä käyttäjänimi kelpaa. Sama pätee kun mainitset ihmisiä statuksessasi",
|
|
||||||
"getting_started.open_source_notice": "Mastodon Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}. {apps}.",
|
|
||||||
"column.home": "Koti",
|
|
||||||
"column.community": "Paikallinen aikajana",
|
|
||||||
"column.public": "Yleinen aikajana",
|
|
||||||
"column.notifications": "Ilmoitukset",
|
|
||||||
"tabs_bar.compose": "Luo",
|
|
||||||
"tabs_bar.home": "Koti",
|
|
||||||
"tabs_bar.mentions": "Maininnat",
|
|
||||||
"tabs_bar.public": "Yleinen aikajana",
|
|
||||||
"tabs_bar.notifications": "Ilmoitukset",
|
|
||||||
"compose_form.placeholder": "Mitä sinulla on mielessä?",
|
|
||||||
"compose_form.publish": "Toot",
|
|
||||||
"compose_form.sensitive": "Merkitse media herkäksi",
|
|
||||||
"compose_form.spoiler": "Piiloita teksti varoituksen taakse",
|
|
||||||
"compose_form.private": "Merkitse yksityiseksi",
|
|
||||||
"compose_form.privacy_disclaimer": "Sinun yksityinen status toimitetaan mainitsemallesi käyttäjille domaineissa {domains}. Luotatko {domainsCount, plural, one {tähän palvelimeen} other {näihin palvelimiin}}? Postauksen yksityisyys toimii van Mastodon palvelimilla. Jos {domains} {domainsCount, plural, one {ei ole Mastodon palvelin} other {eivät ole Mastodon palvelin}}, viestiin ei tule Yksityinen-merkintää, ja sitä voidaan boostata tai muuten tehdä näkyväksi muille vastaanottajille.",
|
|
||||||
"compose_form.unlisted": "Älä näytä yleisillä aikajanoilla",
|
|
||||||
"navigation_bar.edit_profile": "Muokkaa profiilia",
|
|
||||||
"navigation_bar.preferences": "Ominaisuudet",
|
|
||||||
"navigation_bar.community_timeline": "Paikallinen aikajana",
|
|
||||||
"navigation_bar.public_timeline": "Yleinen aikajana",
|
|
||||||
"navigation_bar.logout": "Kirjaudu ulos",
|
|
||||||
"reply_indicator.cancel": "Peruuta",
|
|
||||||
"search.placeholder": "Hae",
|
|
||||||
"search.account": "Tili",
|
|
||||||
"search.hashtag": "Hashtag",
|
|
||||||
"upload_button.label": "Lisää mediaa",
|
|
||||||
"upload_form.undo": "Peru",
|
|
||||||
"notification.follow": "{name} seurasi sinua",
|
|
||||||
"notification.favourite": "{name} tykkäsi statuksestasi",
|
|
||||||
"notification.reblog": "{name} buustasi statustasi",
|
|
||||||
"notification.mention": "{name} mainitsi sinut",
|
|
||||||
"notifications.column_settings.alert": "Työpöytä ilmoitukset",
|
|
||||||
"notifications.column_settings.show": "Näytä sarakkeessa",
|
|
||||||
"notifications.column_settings.follow": "Uusia seuraajia:",
|
|
||||||
"notifications.column_settings.favourite": "Tykkäyksiä:",
|
|
||||||
"notifications.column_settings.mention": "Mainintoja:",
|
|
||||||
"notifications.column_settings.reblog": "Buusteja:",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default fi;
|
|
@@ -1,57 +0,0 @@
|
|||||||
const hu = {
|
|
||||||
"column_back_button.label": "Vissza",
|
|
||||||
"lightbox.close": "Bezárás",
|
|
||||||
"loading_indicator.label": "Betöltés...",
|
|
||||||
"status.mention": "Említés",
|
|
||||||
"status.delete": "Törlés",
|
|
||||||
"status.reply": "Válasz",
|
|
||||||
"status.reblog": "Reblog",
|
|
||||||
"status.favourite": "Kedvenc",
|
|
||||||
"status.reblogged_by": "{name} reblogolta",
|
|
||||||
"status.sensitive_warning": "Érzékeny tartalom",
|
|
||||||
"status.sensitive_toggle": "Katt a megtekintéshez",
|
|
||||||
"video_player.toggle_sound": "Hang kapcsolása",
|
|
||||||
"account.mention": "Említés",
|
|
||||||
"account.edit_profile": "Profil szerkesztése",
|
|
||||||
"account.unblock": "Blokkolás levétele",
|
|
||||||
"account.unfollow": "Követés abbahagyása",
|
|
||||||
"account.block": "Blokkolás",
|
|
||||||
"account.follow": "Követés",
|
|
||||||
"account.posts": "Posts",
|
|
||||||
"account.follows": "Követve",
|
|
||||||
"account.followers": "Követők",
|
|
||||||
"account.follows_you": "Követnek téged",
|
|
||||||
"getting_started.heading": "Első lépések",
|
|
||||||
"getting_started.about_addressing": "Követhetsz embereket felhasználónevük és a doménjük ismeretében, amennyiben megadod ezt az e-mail-szerű címet az oldalsáv tetején lévő rubrikában.",
|
|
||||||
"getting_started.about_shortcuts": "Ha a célzott személy azonos doménen tartózkodik, a felhasználónév elegendő. Ugyanez érvényes mikor személyeket említesz az állapotokban.",
|
|
||||||
"getting_started.about_developer": "A projekt fejlesztője követhető, mint Gargron@mastodon.social",
|
|
||||||
"column.home": "Kezdőlap",
|
|
||||||
"column.mentions": "Említések",
|
|
||||||
"column.public": "Nyilvános",
|
|
||||||
"column.notifications": "Értesítések",
|
|
||||||
"tabs_bar.compose": "Összeállítás",
|
|
||||||
"tabs_bar.home": "Kezdőlap",
|
|
||||||
"tabs_bar.mentions": "Említések",
|
|
||||||
"tabs_bar.public": "Nyilvános",
|
|
||||||
"tabs_bar.notifications": "Notifications",
|
|
||||||
"compose_form.placeholder": "Mire gondolsz?",
|
|
||||||
"compose_form.publish": "Tülk!",
|
|
||||||
"compose_form.sensitive": "Tartalom érzékenynek jelölése",
|
|
||||||
"compose_form.unlisted": "Listázatlan mód",
|
|
||||||
"navigation_bar.edit_profile": "Profil szerkesztése",
|
|
||||||
"navigation_bar.preferences": "Beállítások",
|
|
||||||
"navigation_bar.public_timeline": "Nyilvános időfolyam",
|
|
||||||
"navigation_bar.logout": "Kijelentkezés",
|
|
||||||
"reply_indicator.cancel": "Mégsem",
|
|
||||||
"search.placeholder": "Keresés",
|
|
||||||
"search.account": "Fiók",
|
|
||||||
"search.hashtag": "Hashtag",
|
|
||||||
"upload_button.label": "Média hozzáadása",
|
|
||||||
"upload_form.undo": "Mégsem",
|
|
||||||
"notification.follow": "{name} követ téged",
|
|
||||||
"notification.favourite": "{name} kedvencnek jelölte az állapotod",
|
|
||||||
"notification.reblog": "{name} reblogolta az állapotod",
|
|
||||||
"notification.mention": "{name} megemlített"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default hu;
|
|
@@ -1,55 +0,0 @@
|
|||||||
import ar from './ar';
|
|
||||||
import en from './en';
|
|
||||||
import de from './de';
|
|
||||||
import es from './es';
|
|
||||||
import fa from './fa';
|
|
||||||
import hr from './hr';
|
|
||||||
import hu from './hu';
|
|
||||||
import io from './io';
|
|
||||||
import it from './it';
|
|
||||||
import fr from './fr';
|
|
||||||
import nl from './nl';
|
|
||||||
import no from './no';
|
|
||||||
import oc from './oc';
|
|
||||||
import pt from './pt';
|
|
||||||
import pt_br from './pt-br';
|
|
||||||
import uk from './uk';
|
|
||||||
import fi from './fi';
|
|
||||||
import eo from './eo';
|
|
||||||
import ru from './ru';
|
|
||||||
import ja from './ja';
|
|
||||||
import zh_hk from './zh-hk';
|
|
||||||
import zh_cn from './zh-cn';
|
|
||||||
import bg from './bg';
|
|
||||||
import id from './id';
|
|
||||||
|
|
||||||
const locales = {
|
|
||||||
ar,
|
|
||||||
en,
|
|
||||||
de,
|
|
||||||
es,
|
|
||||||
fa,
|
|
||||||
hr,
|
|
||||||
hu,
|
|
||||||
io,
|
|
||||||
it,
|
|
||||||
fr,
|
|
||||||
nl,
|
|
||||||
no,
|
|
||||||
oc,
|
|
||||||
pt,
|
|
||||||
'pt-BR': pt_br,
|
|
||||||
uk,
|
|
||||||
fi,
|
|
||||||
eo,
|
|
||||||
ru,
|
|
||||||
ja,
|
|
||||||
'zh-HK': zh_hk,
|
|
||||||
'zh-CN': zh_cn,
|
|
||||||
bg,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function getMessagesForLocale (locale) {
|
|
||||||
return locales[locale];
|
|
||||||
};
|
|
@@ -1,128 +0,0 @@
|
|||||||
const oc = {
|
|
||||||
"column_back_button.label": "Tornar",
|
|
||||||
"lightbox.close": "Tampar",
|
|
||||||
"loading_indicator.label": "Cargament…",
|
|
||||||
"status.mention": "Mencionar",
|
|
||||||
"status.delete": "Escafar",
|
|
||||||
"status.reply": "Respondre",
|
|
||||||
"status.reblog": "Partejar",
|
|
||||||
"status.favourite": "Apondre als favorits",
|
|
||||||
"status.reblogged_by": "{name} a partejat :",
|
|
||||||
"status.sensitive_warning": "Contengut embarrassant",
|
|
||||||
"status.sensitive_toggle": "Clicar per mostrar",
|
|
||||||
"status.show_more": "Desplegar",
|
|
||||||
"status.show_less": "Tornar plegar",
|
|
||||||
"status.open": "Desplegar aqueste estatut",
|
|
||||||
"status.report": "Senhalar @{name}",
|
|
||||||
"status.load_more": "Cargar mai",
|
|
||||||
"status.media_hidden": "Mèdia rescondut",
|
|
||||||
"video_player.toggle_sound": "Activar/Desactivar lo son",
|
|
||||||
"video_player.toggle_visible": "Mostrar/Rescondre la vidèo",
|
|
||||||
"account.mention": "Mencionar",
|
|
||||||
"account.edit_profile": "Modificar lo perfil",
|
|
||||||
"account.unblock": "Desblocar",
|
|
||||||
"account.unfollow": "Quitar de sègre",
|
|
||||||
"account.block": "Blocar",
|
|
||||||
"account.mute": "Rescondre",
|
|
||||||
"account.unmute": "Quitar de rescondre",
|
|
||||||
"account.follow": "Sègre",
|
|
||||||
"account.posts": "Estatuts",
|
|
||||||
"account.follows": "Abonaments",
|
|
||||||
"account.followers": "Abonats",
|
|
||||||
"account.follows_you": "Vos sèc",
|
|
||||||
"account.requested": "Invitacion mandada",
|
|
||||||
"account.report": "Senhalar",
|
|
||||||
"account.disclaimer": "Aqueste compte es sus una autra instància. Los nombres pòdon èsser mai grandes.",
|
|
||||||
"getting_started.heading": "Per començar",
|
|
||||||
"getting_started.about_addressing": "Podètz sègre los estatuts de qualqu’un en picant son identificant e lo domeni de l’instància separat amb un @ coma una adreça de corrièl dins lo camp de recèrca.",
|
|
||||||
"getting_started.about_shortcuts": "S’aquesta persona emplega la meteissa instància que vos l’identifican basta. Atal foncionan tanben las mencions dins vòstres estatuts.",
|
|
||||||
"getting_started.about_developer": "Per sègre lo desvolopaire d’aqueste projècte : Gargron@mastodon.social",
|
|
||||||
"getting_started.open_source_notice": "Mastodon es un logicial liure. Podètz contribuir e mandar vòstres comentaris e rapòrt de bug via{github} sus GitHub.",
|
|
||||||
"column.home": "Acuèlh",
|
|
||||||
"column.community": "Fil public local",
|
|
||||||
"column.public": "Fil public global",
|
|
||||||
"column.notifications": "Notificacions",
|
|
||||||
"column.blocks": "Personas blocadas",
|
|
||||||
"column.favourites": "Favorits",
|
|
||||||
"column.follow_requests": "Demandas d’abonament",
|
|
||||||
"empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualqu’un per començar una conversacion.",
|
|
||||||
"empty_column.public": "I a pas res aquí ! Escribètz quicòm de public, o seguètz de personas d’autras instàncias per garnir lo fil public.",
|
|
||||||
"empty_column.home": "Pel moment segètz pas segun. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
|
|
||||||
"empty_column.home.public_timeline": "lo fil public",
|
|
||||||
"empty_column.community": "Lo fil public local es void. Escribètz quicòm per lo garnir !",
|
|
||||||
"empty_column.hashtag": "I a pas encara de contengut ligat a aqueste hashtag",
|
|
||||||
"tabs_bar.compose": "Compausar",
|
|
||||||
"tabs_bar.home": "Acuèlh",
|
|
||||||
"tabs_bar.mentions": "Mencions",
|
|
||||||
"tabs_bar.public": "Fil public global",
|
|
||||||
"tabs_bar.notifications": "Notifications",
|
|
||||||
"tabs_bar.local_timeline": "Fil public local",
|
|
||||||
"tabs_bar.federated_timeline": "Fil public global",
|
|
||||||
"compose_form.placeholder": "A de qué pensatz ?",
|
|
||||||
"compose_form.publish": "Tut",
|
|
||||||
"compose_form.sensitive": "Marcar lo mèdia coma embarrassant",
|
|
||||||
"compose_form.spoiler": "Rescondre lo tèxte darrièr un avertiment",
|
|
||||||
"compose_form.spoiler_placeholder": "Avertiment",
|
|
||||||
"compose_form.private": "Far venir privat",
|
|
||||||
"compose_form.privacy_disclaimer": "Vòstre estatut privat serà enviat a las personas mencionadas sus {domains}. Vos fisatz d’aqueste{domainsCount, plural, one { servidor} other {s servidors}} per divulgar pas vòstre estatut ? Los estatuts privats foncionan pas que sus las instàncias a Mastodons. Se {domains} {domainsCount, plural, one {es pas una instància a Mastodon} other {son pas d'instàncias a Mastodon}}, i aurà pas d’indicacion disent que vòstre estatut es privat e poirà èsser partejat o èsser visible a de mond pas prevists",
|
|
||||||
"compose_form.unlisted": "Mostrar pas dins los fils publics",
|
|
||||||
"emoji_button.label": "Inserir un emoji",
|
|
||||||
"navigation_bar.edit_profile": "Modificar lo perfil",
|
|
||||||
"navigation_bar.preferences": "Preferéncias",
|
|
||||||
"navigation_bar.community_timeline": "Fil public local",
|
|
||||||
"navigation_bar.public_timeline": "Fil public global",
|
|
||||||
"navigation_bar.blocks": "Personas blocadas",
|
|
||||||
"navigation_bar.favourites": "Favorits",
|
|
||||||
"navigation_bar.info": "Mai informacions",
|
|
||||||
"navigation_bar.logout": "Desconnexion",
|
|
||||||
"navigation_bar.follow_requests": "Demandas d'abonament",
|
|
||||||
"reply_indicator.cancel": "Anullar",
|
|
||||||
"search.placeholder": "Recercar",
|
|
||||||
"search.account": "Compte",
|
|
||||||
"search.hashtag": "Mot-clau",
|
|
||||||
"search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
|
|
||||||
"search.status_by": "Estatuts de {name}",
|
|
||||||
"upload_button.label": "Apondre un mèdia",
|
|
||||||
"upload_form.undo": "Anullar",
|
|
||||||
"upload_progress.label": "Mandadís…",
|
|
||||||
"upload_area.title": "Lisatz e depausatz per mandar",
|
|
||||||
"notification.follow": "{name} vos sèc.",
|
|
||||||
"notification.favourite": "{name} a apondut a sos favorits :",
|
|
||||||
"notification.reblog": "{name} a partejat vòstre estatut :",
|
|
||||||
"notification.mention": "{name} vos a mencionat :",
|
|
||||||
"notifications.column_settings.alert": "Notificacions localas",
|
|
||||||
"notifications.column_settings.show": "Mostrar dins la colomna",
|
|
||||||
"notifications.column_settings.sound": "Emetre un son",
|
|
||||||
"notifications.column_settings.follow": "Nòus abonats :",
|
|
||||||
"notifications.column_settings.favourite": "Favorits :",
|
|
||||||
"notifications.column_settings.mention": "Mencions :",
|
|
||||||
"notifications.column_settings.reblog": "Partatges :",
|
|
||||||
"notifications.clear": "Levar",
|
|
||||||
"notifications.clear_confirmation": "Volètz vertadièrament levar totas vòstras las notificacions ?",
|
|
||||||
"notifications.settings": "Paramètres de la colomna",
|
|
||||||
"privacy.public.short": "Public",
|
|
||||||
"privacy.public.long": "Mostrar dins los fils publics",
|
|
||||||
"privacy.unlisted.short": "Pas-listat",
|
|
||||||
"privacy.unlisted.long": "Mostrar pas dins los fils publics",
|
|
||||||
"privacy.private.short": "Privat",
|
|
||||||
"privacy.private.long": "Mostrar pas qu'a vòstres abonats",
|
|
||||||
"privacy.direct.short": "Dirècte",
|
|
||||||
"privacy.direct.long": "Mostrar pas qu'a las personas mencionadas",
|
|
||||||
"privacy.change": "Ajustar la confidencialitat del messatge",
|
|
||||||
"media_gallery.toggle_visible": "Modificar la visibilitat",
|
|
||||||
"missing_indicator.label": "Pas trobat",
|
|
||||||
"follow_request.authorize": "Autorizar",
|
|
||||||
"follow_request.reject": "Regetar",
|
|
||||||
"home.settings": "Paramètres de la colomna",
|
|
||||||
"home.column_settings.basic": "Basic",
|
|
||||||
"home.column_settings.show_reblogs": "Mostrar los partatges",
|
|
||||||
"home.column_settings.show_replies": "Mostrar las responsas",
|
|
||||||
"home.column_settings.advanced": "Avançat",
|
|
||||||
"home.column_settings.filter_regex": "Filtrar amb una expression racionala",
|
|
||||||
"report.heading": "Nòu senhalament",
|
|
||||||
"report.placeholder": "Comentaris addicionals",
|
|
||||||
"report.submit": "Mandat",
|
|
||||||
"report.target": "Senhalament"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default oc;
|
|
@@ -1,57 +0,0 @@
|
|||||||
const uk = {
|
|
||||||
"column_back_button.label": "Назад",
|
|
||||||
"lightbox.close": "Закрити",
|
|
||||||
"loading_indicator.label": "Завантаження...",
|
|
||||||
"status.mention": "Згадати",
|
|
||||||
"status.delete": "Видалити",
|
|
||||||
"status.reply": "Відповісти",
|
|
||||||
"status.reblog": "Передмухнути",
|
|
||||||
"status.favourite": "Подобається",
|
|
||||||
"status.reblogged_by": "{name} передмухнув(-ла)",
|
|
||||||
"status.sensitive_warning": "Непристойний зміст",
|
|
||||||
"status.sensitive_toggle": "Натисніть, щоб подивитися",
|
|
||||||
"video_player.toggle_sound": "Увімкнути/вимкнути звук",
|
|
||||||
"account.mention": "Згадати",
|
|
||||||
"account.edit_profile": "Налаштування профілю",
|
|
||||||
"account.unblock": "Розблокувати",
|
|
||||||
"account.unfollow": "Відписатися",
|
|
||||||
"account.block": "Заблокувати",
|
|
||||||
"account.follow": "Підписатися",
|
|
||||||
"account.posts": "Пости",
|
|
||||||
"account.follows": "Підписки",
|
|
||||||
"account.followers": "Підписники",
|
|
||||||
"account.follows_you": "Підписаний",
|
|
||||||
"getting_started.heading": "Ласкаво просимо",
|
|
||||||
"getting_started.about_addressing": "Ви можете підписуватись на людей, якщо ви знаєте їх ім'я користувача чи домен, шляхом введення email-подібної адреси у верхньому рядку бокової панелі.",
|
|
||||||
"getting_started.about_shortcuts": "Якщо користувач, якого ви шукаєте, знаходиться на тому ж домені, що й ви, можна просто ввести ім'я користувача. Це правило стосується й згадування людей у статусах.",
|
|
||||||
"getting_started.about_developer": "Розробник проекту знаходиться за адресою Gargron@mastodon.social",
|
|
||||||
"column.home": "Головна",
|
|
||||||
"column.mentions": "Згадування",
|
|
||||||
"column.public": "Стіна",
|
|
||||||
"column.notifications": "Сповіщення",
|
|
||||||
"tabs_bar.compose": "Написати",
|
|
||||||
"tabs_bar.home": "Головна",
|
|
||||||
"tabs_bar.mentions": "Згадування",
|
|
||||||
"tabs_bar.public": "Стіна",
|
|
||||||
"tabs_bar.notifications": "Сповіщення",
|
|
||||||
"compose_form.placeholder": "Що у Вас на думці?",
|
|
||||||
"compose_form.publish": "Дмухнути",
|
|
||||||
"compose_form.sensitive": "Непристойний зміст",
|
|
||||||
"compose_form.unlisted": "Таємний режим",
|
|
||||||
"navigation_bar.edit_profile": "Редагувати профіль",
|
|
||||||
"navigation_bar.preferences": "Налаштування",
|
|
||||||
"navigation_bar.public_timeline": "Публічна стіна",
|
|
||||||
"navigation_bar.logout": "Вийти",
|
|
||||||
"reply_indicator.cancel": "Відмінити",
|
|
||||||
"search.placeholder": "Пошук",
|
|
||||||
"search.account": "Аккаунт",
|
|
||||||
"search.hashtag": "Хештеґ",
|
|
||||||
"upload_button.label": "Додати медіа",
|
|
||||||
"upload_form.undo": "Відмінити",
|
|
||||||
"notification.follow": "{name} підписався(-лась) на Вас",
|
|
||||||
"notification.favourite": "{name} сподобався ваш допис",
|
|
||||||
"notification.reblog": "{name} передмухнув(-ла) Ваш статус",
|
|
||||||
"notification.mention": "{name} згадав(-ла) Вас"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default uk;
|
|
@@ -1,22 +0,0 @@
|
|||||||
const play = audio => {
|
|
||||||
if (!audio.paused) {
|
|
||||||
audio.pause();
|
|
||||||
audio.fastSeek(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
audio.play();
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function soundsMiddleware() {
|
|
||||||
const soundCache = {
|
|
||||||
boop: new Audio(['/sounds/boop.mp3'])
|
|
||||||
};
|
|
||||||
|
|
||||||
return ({ dispatch }) => next => (action) => {
|
|
||||||
if (action.meta && action.meta.sound && soundCache[action.meta.sound]) {
|
|
||||||
play(soundCache[action.meta.sound]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(action);
|
|
||||||
};
|
|
||||||
};
|
|
@@ -1,48 +0,0 @@
|
|||||||
import { SETTING_CHANGE } from '../actions/settings';
|
|
||||||
import { STORE_HYDRATE } from '../actions/store';
|
|
||||||
import Immutable from 'immutable';
|
|
||||||
|
|
||||||
const initialState = Immutable.Map({
|
|
||||||
onboarded: false,
|
|
||||||
|
|
||||||
home: Immutable.Map({
|
|
||||||
shows: Immutable.Map({
|
|
||||||
reblog: true,
|
|
||||||
reply: true
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
|
|
||||||
notifications: Immutable.Map({
|
|
||||||
alerts: Immutable.Map({
|
|
||||||
follow: true,
|
|
||||||
favourite: true,
|
|
||||||
reblog: true,
|
|
||||||
mention: true
|
|
||||||
}),
|
|
||||||
|
|
||||||
shows: Immutable.Map({
|
|
||||||
follow: true,
|
|
||||||
favourite: true,
|
|
||||||
reblog: true,
|
|
||||||
mention: true
|
|
||||||
}),
|
|
||||||
|
|
||||||
sounds: Immutable.Map({
|
|
||||||
follow: true,
|
|
||||||
favourite: true,
|
|
||||||
reblog: true,
|
|
||||||
mention: true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function settings(state = initialState, action) {
|
|
||||||
switch(action.type) {
|
|
||||||
case STORE_HYDRATE:
|
|
||||||
return state.mergeDeep(action.state.get('settings'));
|
|
||||||
case SETTING_CHANGE:
|
|
||||||
return state.setIn(action.key, action.value);
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,317 +0,0 @@
|
|||||||
import {
|
|
||||||
TIMELINE_REFRESH_REQUEST,
|
|
||||||
TIMELINE_REFRESH_SUCCESS,
|
|
||||||
TIMELINE_REFRESH_FAIL,
|
|
||||||
TIMELINE_UPDATE,
|
|
||||||
TIMELINE_DELETE,
|
|
||||||
TIMELINE_EXPAND_SUCCESS,
|
|
||||||
TIMELINE_EXPAND_REQUEST,
|
|
||||||
TIMELINE_EXPAND_FAIL,
|
|
||||||
TIMELINE_SCROLL_TOP,
|
|
||||||
TIMELINE_CONNECT,
|
|
||||||
TIMELINE_DISCONNECT
|
|
||||||
} from '../actions/timelines';
|
|
||||||
import {
|
|
||||||
REBLOG_SUCCESS,
|
|
||||||
UNREBLOG_SUCCESS,
|
|
||||||
FAVOURITE_SUCCESS,
|
|
||||||
UNFAVOURITE_SUCCESS
|
|
||||||
} from '../actions/interactions';
|
|
||||||
import {
|
|
||||||
ACCOUNT_TIMELINE_FETCH_REQUEST,
|
|
||||||
ACCOUNT_TIMELINE_FETCH_SUCCESS,
|
|
||||||
ACCOUNT_TIMELINE_FETCH_FAIL,
|
|
||||||
ACCOUNT_TIMELINE_EXPAND_REQUEST,
|
|
||||||
ACCOUNT_TIMELINE_EXPAND_SUCCESS,
|
|
||||||
ACCOUNT_TIMELINE_EXPAND_FAIL,
|
|
||||||
ACCOUNT_BLOCK_SUCCESS,
|
|
||||||
ACCOUNT_MUTE_SUCCESS
|
|
||||||
} from '../actions/accounts';
|
|
||||||
import {
|
|
||||||
CONTEXT_FETCH_SUCCESS
|
|
||||||
} from '../actions/statuses';
|
|
||||||
import Immutable from 'immutable';
|
|
||||||
|
|
||||||
const initialState = Immutable.Map({
|
|
||||||
home: Immutable.Map({
|
|
||||||
path: () => '/api/v1/timelines/home',
|
|
||||||
next: null,
|
|
||||||
isLoading: false,
|
|
||||||
online: false,
|
|
||||||
loaded: false,
|
|
||||||
top: true,
|
|
||||||
unread: 0,
|
|
||||||
items: Immutable.List()
|
|
||||||
}),
|
|
||||||
|
|
||||||
public: Immutable.Map({
|
|
||||||
path: () => '/api/v1/timelines/public',
|
|
||||||
next: null,
|
|
||||||
isLoading: false,
|
|
||||||
online: false,
|
|
||||||
loaded: false,
|
|
||||||
top: true,
|
|
||||||
unread: 0,
|
|
||||||
items: Immutable.List()
|
|
||||||
}),
|
|
||||||
|
|
||||||
community: Immutable.Map({
|
|
||||||
path: () => '/api/v1/timelines/public',
|
|
||||||
next: null,
|
|
||||||
params: { local: true },
|
|
||||||
isLoading: false,
|
|
||||||
online: false,
|
|
||||||
loaded: false,
|
|
||||||
top: true,
|
|
||||||
unread: 0,
|
|
||||||
items: Immutable.List()
|
|
||||||
}),
|
|
||||||
|
|
||||||
tag: Immutable.Map({
|
|
||||||
path: (id) => `/api/v1/timelines/tag/${id}`,
|
|
||||||
next: null,
|
|
||||||
isLoading: false,
|
|
||||||
id: null,
|
|
||||||
loaded: false,
|
|
||||||
top: true,
|
|
||||||
unread: 0,
|
|
||||||
items: Immutable.List()
|
|
||||||
}),
|
|
||||||
|
|
||||||
accounts_timelines: Immutable.Map(),
|
|
||||||
ancestors: Immutable.Map(),
|
|
||||||
descendants: Immutable.Map()
|
|
||||||
});
|
|
||||||
|
|
||||||
const normalizeStatus = (state, status) => {
|
|
||||||
const replyToId = status.get('in_reply_to_id');
|
|
||||||
const id = status.get('id');
|
|
||||||
|
|
||||||
if (replyToId) {
|
|
||||||
if (!state.getIn(['descendants', replyToId], Immutable.List()).includes(id)) {
|
|
||||||
state = state.updateIn(['descendants', replyToId], Immutable.List(), set => set.push(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state.getIn(['ancestors', id], Immutable.List()).includes(replyToId)) {
|
|
||||||
state = state.updateIn(['ancestors', id], Immutable.List(), set => set.push(replyToId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeTimeline = (state, timeline, statuses, next) => {
|
|
||||||
let ids = Immutable.List();
|
|
||||||
const loaded = state.getIn([timeline, 'loaded']);
|
|
||||||
|
|
||||||
statuses.forEach((status, i) => {
|
|
||||||
state = normalizeStatus(state, status);
|
|
||||||
ids = ids.set(i, status.get('id'));
|
|
||||||
});
|
|
||||||
|
|
||||||
state = state.setIn([timeline, 'loaded'], true);
|
|
||||||
state = state.setIn([timeline, 'isLoading'], false);
|
|
||||||
|
|
||||||
if (state.getIn([timeline, 'next']) === null) {
|
|
||||||
state = state.setIn([timeline, 'next'], next);
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.updateIn([timeline, 'items'], Immutable.List(), list => (loaded ? list.unshift(...ids) : ids));
|
|
||||||
};
|
|
||||||
|
|
||||||
const appendNormalizedTimeline = (state, timeline, statuses, next) => {
|
|
||||||
let moreIds = Immutable.List();
|
|
||||||
|
|
||||||
statuses.forEach((status, i) => {
|
|
||||||
state = normalizeStatus(state, status);
|
|
||||||
moreIds = moreIds.set(i, status.get('id'));
|
|
||||||
});
|
|
||||||
|
|
||||||
state = state.setIn([timeline, 'isLoading'], false);
|
|
||||||
state = state.setIn([timeline, 'next'], next);
|
|
||||||
|
|
||||||
return state.updateIn([timeline, 'items'], Immutable.List(), list => list.push(...moreIds));
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeAccountTimeline = (state, accountId, statuses, replace = false) => {
|
|
||||||
let ids = Immutable.List();
|
|
||||||
|
|
||||||
statuses.forEach((status, i) => {
|
|
||||||
state = normalizeStatus(state, status);
|
|
||||||
ids = ids.set(i, status.get('id'));
|
|
||||||
});
|
|
||||||
|
|
||||||
return state.updateIn(['accounts_timelines', accountId], Immutable.Map(), map => map
|
|
||||||
.set('isLoading', false)
|
|
||||||
.set('loaded', true)
|
|
||||||
.set('next', true)
|
|
||||||
.update('items', Immutable.List(), list => (replace ? ids : list.unshift(...ids))));
|
|
||||||
};
|
|
||||||
|
|
||||||
const appendNormalizedAccountTimeline = (state, accountId, statuses, next) => {
|
|
||||||
let moreIds = Immutable.List([]);
|
|
||||||
|
|
||||||
statuses.forEach((status, i) => {
|
|
||||||
state = normalizeStatus(state, status);
|
|
||||||
moreIds = moreIds.set(i, status.get('id'));
|
|
||||||
});
|
|
||||||
|
|
||||||
return state.updateIn(['accounts_timelines', accountId], Immutable.Map(), map => map
|
|
||||||
.set('isLoading', false)
|
|
||||||
.set('next', next)
|
|
||||||
.update('items', list => list.push(...moreIds)));
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateTimeline = (state, timeline, status, references) => {
|
|
||||||
const top = state.getIn([timeline, 'top']);
|
|
||||||
|
|
||||||
state = normalizeStatus(state, status);
|
|
||||||
|
|
||||||
if (!top) {
|
|
||||||
state = state.updateIn([timeline, 'unread'], unread => unread + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = state.updateIn([timeline, 'items'], Immutable.List(), list => {
|
|
||||||
if (top && list.size > 40) {
|
|
||||||
list = list.take(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.includes(status.get('id'))) {
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reblogOfId = status.getIn(['reblog', 'id'], null);
|
|
||||||
|
|
||||||
if (reblogOfId !== null) {
|
|
||||||
list = list.filterNot(itemId => references.includes(itemId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return list.unshift(status.get('id'));
|
|
||||||
});
|
|
||||||
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteStatus = (state, id, accountId, references, reblogOf) => {
|
|
||||||
if (reblogOf) {
|
|
||||||
// If we are deleting a reblog, just replace reblog with its original
|
|
||||||
return state.updateIn(['home', 'items'], list => list.map(item => item === id ? reblogOf : item));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove references from timelines
|
|
||||||
['home', 'public', 'community', 'tag'].forEach(function (timeline) {
|
|
||||||
state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove references from account timelines
|
|
||||||
state = state.updateIn(['accounts_timelines', accountId, 'items'], Immutable.List([]), list => list.filterNot(item => item === id));
|
|
||||||
|
|
||||||
// Remove references from context
|
|
||||||
state.getIn(['descendants', id], Immutable.List()).forEach(descendantId => {
|
|
||||||
state = state.updateIn(['ancestors', descendantId], Immutable.List(), list => list.filterNot(itemId => itemId === id));
|
|
||||||
});
|
|
||||||
|
|
||||||
state.getIn(['ancestors', id], Immutable.List()).forEach(ancestorId => {
|
|
||||||
state = state.updateIn(['descendants', ancestorId], Immutable.List(), list => list.filterNot(itemId => itemId === id));
|
|
||||||
});
|
|
||||||
|
|
||||||
state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]);
|
|
||||||
|
|
||||||
// Remove reblogs of deleted status
|
|
||||||
references.forEach(ref => {
|
|
||||||
state = deleteStatus(state, ref[0], ref[1], []);
|
|
||||||
});
|
|
||||||
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterTimelines = (state, relationship, statuses) => {
|
|
||||||
let references;
|
|
||||||
|
|
||||||
statuses.forEach(status => {
|
|
||||||
if (status.get('account') !== relationship.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]);
|
|
||||||
state = deleteStatus(state, status.get('id'), status.get('account'), references);
|
|
||||||
});
|
|
||||||
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeContext = (state, id, ancestors, descendants) => {
|
|
||||||
const ancestorsIds = ancestors.map(ancestor => ancestor.get('id'));
|
|
||||||
const descendantsIds = descendants.map(descendant => descendant.get('id'));
|
|
||||||
|
|
||||||
return state.withMutations(map => {
|
|
||||||
map.setIn(['ancestors', id], ancestorsIds);
|
|
||||||
map.setIn(['descendants', id], descendantsIds);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetTimeline = (state, timeline, id) => {
|
|
||||||
if (timeline === 'tag' && typeof id !== 'undefined' && state.getIn([timeline, 'id']) !== id) {
|
|
||||||
state = state.update(timeline, map => map
|
|
||||||
.set('id', id)
|
|
||||||
.set('isLoading', true)
|
|
||||||
.set('loaded', false)
|
|
||||||
.set('next', null)
|
|
||||||
.set('top', true)
|
|
||||||
.update('items', list => list.clear()));
|
|
||||||
} else {
|
|
||||||
state = state.setIn([timeline, 'isLoading'], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateTop = (state, timeline, top) => {
|
|
||||||
if (top) {
|
|
||||||
state = state.setIn([timeline, 'unread'], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.setIn([timeline, 'top'], top);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function timelines(state = initialState, action) {
|
|
||||||
switch(action.type) {
|
|
||||||
case TIMELINE_REFRESH_REQUEST:
|
|
||||||
case TIMELINE_EXPAND_REQUEST:
|
|
||||||
return resetTimeline(state, action.timeline, action.id);
|
|
||||||
case TIMELINE_REFRESH_FAIL:
|
|
||||||
case TIMELINE_EXPAND_FAIL:
|
|
||||||
return state.setIn([action.timeline, 'isLoading'], false);
|
|
||||||
case TIMELINE_REFRESH_SUCCESS:
|
|
||||||
return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.next);
|
|
||||||
case TIMELINE_EXPAND_SUCCESS:
|
|
||||||
return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.next);
|
|
||||||
case TIMELINE_UPDATE:
|
|
||||||
return updateTimeline(state, action.timeline, Immutable.fromJS(action.status), action.references);
|
|
||||||
case TIMELINE_DELETE:
|
|
||||||
return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf);
|
|
||||||
case CONTEXT_FETCH_SUCCESS:
|
|
||||||
return normalizeContext(state, action.id, Immutable.fromJS(action.ancestors), Immutable.fromJS(action.descendants));
|
|
||||||
case ACCOUNT_TIMELINE_FETCH_REQUEST:
|
|
||||||
case ACCOUNT_TIMELINE_EXPAND_REQUEST:
|
|
||||||
return state.updateIn(['accounts_timelines', action.id], Immutable.Map(), map => map.set('isLoading', true));
|
|
||||||
case ACCOUNT_TIMELINE_FETCH_FAIL:
|
|
||||||
case ACCOUNT_TIMELINE_EXPAND_FAIL:
|
|
||||||
return state.updateIn(['accounts_timelines', action.id], Immutable.Map(), map => map.set('isLoading', false));
|
|
||||||
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
|
|
||||||
return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace);
|
|
||||||
case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
|
|
||||||
return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.next);
|
|
||||||
case ACCOUNT_BLOCK_SUCCESS:
|
|
||||||
case ACCOUNT_MUTE_SUCCESS:
|
|
||||||
return filterTimelines(state, action.relationship, action.statuses);
|
|
||||||
case TIMELINE_SCROLL_TOP:
|
|
||||||
return updateTop(state, action.timeline, action.top);
|
|
||||||
case TIMELINE_CONNECT:
|
|
||||||
return state.setIn([action.timeline, 'online'], true);
|
|
||||||
case TIMELINE_DISCONNECT:
|
|
||||||
return state.setIn([action.timeline, 'online'], false);
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,49 +0,0 @@
|
|||||||
import emojify from './components/emoji';
|
|
||||||
import { length } from 'stringz';
|
|
||||||
|
|
||||||
$(() => {
|
|
||||||
$.each($('.emojify'), (_, content) => {
|
|
||||||
const $content = $(content);
|
|
||||||
$content.html(emojify($content.html()));
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.video-player video').on('click', e => {
|
|
||||||
if (e.target.paused) {
|
|
||||||
e.target.play();
|
|
||||||
} else {
|
|
||||||
e.target.pause();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.media-spoiler').on('click', e => {
|
|
||||||
$(e.target).hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.webapp-btn').on('click', e => {
|
|
||||||
if (e.button === 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
window.location.href = $(e.target).attr('href');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.status__content__spoiler-link').on('click', e => {
|
|
||||||
e.preventDefault();
|
|
||||||
const contentEl = $(e.target).parent().parent().find('div');
|
|
||||||
|
|
||||||
if (contentEl.is(':visible')) {
|
|
||||||
contentEl.hide();
|
|
||||||
$(e.target).parent().attr('style', 'margin-bottom: 0');
|
|
||||||
} else {
|
|
||||||
contentEl.show();
|
|
||||||
$(e.target).parent().attr('style', null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// used on /settings/profile
|
|
||||||
$('.account_display_name').on('input', e => {
|
|
||||||
$('.name-counter').text(30 - length($(e.target).val()));
|
|
||||||
});
|
|
||||||
$('.account_note').on('input', e => {
|
|
||||||
$('.note-counter').text(160 - length($(e.target).val()));
|
|
||||||
});
|
|
||||||
});
|
|
File diff suppressed because one or more lines are too long
@@ -1,11 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Montserrat';
|
|
||||||
src: local('Montserrat');
|
|
||||||
src: font-url('montserrat/Montserrat-Regular.eot');
|
|
||||||
src: font-url('montserrat/Montserrat-Regular.eot?#iefix') format('embedded-opentype'),
|
|
||||||
font-url('montserrat/Montserrat-Regular.woff2') format('woff2'),
|
|
||||||
font-url('montserrat/Montserrat-Regular.woff') format('woff'),
|
|
||||||
font-url('montserrat/Montserrat-Regular.ttf') format('truetype');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
@@ -1,12 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Roboto Mono';
|
|
||||||
src: local('Roboto Mono');
|
|
||||||
src: font-url('roboto-mono/robotomono-regular-webfont.eot');
|
|
||||||
src: font-url('roboto-mono/robotomono-regular-webfont.eot?#iefix') format('embedded-opentype'),
|
|
||||||
font-url('roboto-mono/robotomono-regular-webfont.woff2') format('woff2'),
|
|
||||||
font-url('roboto-mono/robotomono-regular-webfont.woff') format('woff'),
|
|
||||||
font-url('roboto-mono/robotomono-regular-webfont.ttf') format('truetype'),
|
|
||||||
font-url('roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular') format('svg');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
@@ -1,52 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
src: local('Roboto');
|
|
||||||
src: font-url('roboto/roboto-italic-webfont.eot');
|
|
||||||
src: font-url('roboto/roboto-italic-webfont.eot?#iefix') format('embedded-opentype'),
|
|
||||||
font-url('roboto/roboto-italic-webfont.woff2') format('woff2'),
|
|
||||||
font-url('roboto/roboto-italic-webfont.woff') format('woff'),
|
|
||||||
font-url('roboto/roboto-italic-webfont.ttf') format('truetype'),
|
|
||||||
font-url('roboto/roboto-italic-webfont.svg#roboto-italic-webfont') format('svg');
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
src: local('Roboto');
|
|
||||||
src: font-url('roboto/roboto-bold-webfont.eot');
|
|
||||||
src: local('Roboto bold'), local('roboto-bold'),
|
|
||||||
font-url('roboto/roboto-bold-webfont.eot?#iefix') format('embedded-opentype'),
|
|
||||||
font-url('roboto/roboto-bold-webfont.woff2') format('woff2'),
|
|
||||||
font-url('roboto/roboto-bold-webfont.woff') format('woff'),
|
|
||||||
font-url('roboto/roboto-bold-webfont.ttf') format('truetype'),
|
|
||||||
font-url('roboto/roboto-bold-webfont.svg#roboto-bold-webfont') format('svg');
|
|
||||||
font-weight: bold;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
src: local('Roboto');
|
|
||||||
src: font-url('roboto/roboto-medium-webfont.eot');
|
|
||||||
src: font-url('roboto/roboto-medium-webfont.eot?#iefix') format('embedded-opentype'),
|
|
||||||
font-url('roboto/roboto-medium-webfont.woff2') format('woff2'),
|
|
||||||
font-url('roboto/roboto-medium-webfont.woff') format('woff'),
|
|
||||||
font-url('roboto/roboto-medium-webfont.ttf') format('truetype'),
|
|
||||||
font-url('roboto/roboto-medium-webfont.svg#roboto-medium-webfont') format('svg');
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
src: local('Roboto');
|
|
||||||
src: font-url('roboto/roboto-regular-webfont.eot');
|
|
||||||
src: font-url('roboto/roboto-regular-webfont.eot?#iefix') format('embedded-opentype'),
|
|
||||||
font-url('roboto/roboto-regular-webfont.woff2') format('woff2'),
|
|
||||||
font-url('roboto/roboto-regular-webfont.woff') format('woff'),
|
|
||||||
font-url('roboto/roboto-regular-webfont.ttf') format('truetype'),
|
|
||||||
font-url('roboto/roboto-regular-webfont.svg#roboto-regular-webfont') format('svg');
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
$color1: #282c37 !default; // darkest
|
|
||||||
$color2: #d9e1e8 !default; // lightest
|
|
||||||
$color3: #9baec8 !default; // lighter
|
|
||||||
$color4: #2b90d9 !default; // vibrant
|
|
||||||
$color5: #ffffff !default; // white
|
|
||||||
$color6: #df405a !default; // error red
|
|
||||||
$color7: #79bd9a !default; // succ green
|
|
||||||
$color8: #000000 !default; // black
|
|
@@ -6,12 +6,12 @@ class AccountsController < ApplicationController
|
|||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
@statuses = @account.statuses.permitted_for(@account, current_account).order('id desc').paginate_by_max_id(20, params[:max_id], params[:since_id])
|
@statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||||
@statuses = cache_collection(@statuses, Status)
|
@statuses = cache_collection(@statuses, Status)
|
||||||
end
|
end
|
||||||
|
|
||||||
format.atom do
|
format.atom do
|
||||||
@entries = @account.stream_entries.order('id desc').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
@entries = @account.stream_entries.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||||
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
|
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -2,16 +2,43 @@
|
|||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class AccountsController < BaseController
|
class AccountsController < BaseController
|
||||||
|
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload]
|
||||||
|
before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@accounts = filtered_accounts.page(params[:page])
|
@accounts = filtered_accounts.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show; end
|
||||||
@account = Account.find(params[:id])
|
|
||||||
|
def subscribe
|
||||||
|
Pubsubhubbub::SubscribeWorker.perform_async(@account.id)
|
||||||
|
redirect_to admin_account_path(@account.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe
|
||||||
|
UnsubscribeService.new.call(@account)
|
||||||
|
redirect_to admin_account_path(@account.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def redownload
|
||||||
|
@account.avatar = @account.avatar_remote_url
|
||||||
|
@account.header = @account.header_remote_url
|
||||||
|
@account.save!
|
||||||
|
|
||||||
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_remote_account!
|
||||||
|
redirect_to admin_account_path(@account.id) if @account.local?
|
||||||
|
end
|
||||||
|
|
||||||
def filtered_accounts
|
def filtered_accounts
|
||||||
AccountFilter.new(filter_params).results
|
AccountFilter.new(filter_params).results
|
||||||
end
|
end
|
||||||
@@ -23,7 +50,11 @@ module Admin
|
|||||||
:by_domain,
|
:by_domain,
|
||||||
:silenced,
|
:silenced,
|
||||||
:recent,
|
:recent,
|
||||||
:suspended
|
:suspended,
|
||||||
|
:username,
|
||||||
|
:display_name,
|
||||||
|
:email,
|
||||||
|
:ip
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -2,17 +2,15 @@
|
|||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class ConfirmationsController < BaseController
|
class ConfirmationsController < BaseController
|
||||||
before_action :set_account
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@account.user.confirm
|
account_user.confirm
|
||||||
redirect_to admin_accounts_path
|
redirect_to admin_accounts_path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_account
|
def account_user
|
||||||
@account = Account.find(params[:account_id])
|
Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class DomainBlocksController < BaseController
|
class DomainBlocksController < BaseController
|
||||||
|
before_action :set_domain_block, only: [:show, :destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@blocks = DomainBlock.page(params[:page])
|
@domain_blocks = DomainBlock.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@@ -21,20 +23,25 @@ module Admin
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show; end
|
||||||
@domain_block = DomainBlock.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@domain_block = DomainBlock.find(params[:id])
|
UnblockDomainService.new.call(@domain_block, retroactive_unblock?)
|
||||||
UnblockDomainService.new.call(@domain_block, resource_params[:retroactive])
|
|
||||||
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg')
|
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg')
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_domain_block
|
||||||
|
@domain_block = DomainBlock.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive)
|
params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def retroactive_unblock?
|
||||||
|
ActiveRecord::Type.lookup(:boolean).cast(resource_params[:retroactive])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -3,13 +3,18 @@
|
|||||||
module Admin
|
module Admin
|
||||||
class InstancesController < BaseController
|
class InstancesController < BaseController
|
||||||
def index
|
def index
|
||||||
@instances = ordered_instances.page(params[:page])
|
@instances = ordered_instances
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def paginated_instances
|
||||||
|
Account.remote.by_domain_accounts.page(params[:page])
|
||||||
|
end
|
||||||
|
helper_method :paginated_instances
|
||||||
|
|
||||||
def ordered_instances
|
def ordered_instances
|
||||||
Account.remote.by_domain_accounts
|
paginated_instances.map { |account| Instance.new(account) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class PubsubhubbubController < BaseController
|
|
||||||
def index
|
|
||||||
@subscriptions = Subscription.order('id desc').includes(:account).page(params[:page])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@@ -2,17 +2,34 @@
|
|||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class ReportedStatusesController < BaseController
|
class ReportedStatusesController < BaseController
|
||||||
def destroy
|
include Authorization
|
||||||
status = Status.find params[:id]
|
|
||||||
|
|
||||||
RemovalWorker.perform_async(status.id)
|
before_action :set_report
|
||||||
redirect_to admin_report_path(report)
|
before_action :set_status
|
||||||
|
|
||||||
|
def update
|
||||||
|
@status.update(status_params)
|
||||||
|
redirect_to admin_report_path(@report)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @status, :destroy?
|
||||||
|
RemovalWorker.perform_async(@status.id)
|
||||||
|
redirect_to admin_report_path(@report)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def report
|
def status_params
|
||||||
Report.find(params[:report_id])
|
params.require(:status).permit(:sensitive)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_report
|
||||||
|
@report = Report.find(params[:report_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_status
|
||||||
|
@status = @report.statuses.find(params[:id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -49,7 +49,7 @@ module Admin
|
|||||||
end
|
end
|
||||||
|
|
||||||
def filtered_reports
|
def filtered_reports
|
||||||
ReportFilter.new(filter_params).results.order('id desc').includes(
|
ReportFilter.new(filter_params).results.order(id: :desc).includes(
|
||||||
:account,
|
:account,
|
||||||
:target_account
|
:target_account
|
||||||
)
|
)
|
||||||
|
@@ -2,38 +2,43 @@
|
|||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class SettingsController < BaseController
|
class SettingsController < BaseController
|
||||||
|
ADMIN_SETTINGS = %w(
|
||||||
|
site_contact_username
|
||||||
|
site_contact_email
|
||||||
|
site_title
|
||||||
|
site_description
|
||||||
|
site_extended_description
|
||||||
|
open_registrations
|
||||||
|
closed_registrations_message
|
||||||
|
).freeze
|
||||||
BOOLEAN_SETTINGS = %w(open_registrations).freeze
|
BOOLEAN_SETTINGS = %w(open_registrations).freeze
|
||||||
|
|
||||||
def index
|
def edit
|
||||||
@settings = Setting.all_as_records
|
@settings = Setting.all_as_records
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@setting = Setting.where(var: params[:id]).first_or_initialize(var: params[:id])
|
settings_params.each do |key, value|
|
||||||
@setting.update(value: value_for_update)
|
setting = Setting.where(var: key).first_or_initialize(var: key)
|
||||||
|
setting.update(value: value_for_update(key, value))
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to admin_settings_path }
|
|
||||||
format.json { respond_with_bip(@setting) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
flash[:notice] = 'Success!'
|
||||||
|
redirect_to edit_admin_settings_path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def settings_params
|
def settings_params
|
||||||
params.require(:setting).permit(:value)
|
params.permit(ADMIN_SETTINGS)
|
||||||
end
|
end
|
||||||
|
|
||||||
def value_for_update
|
def value_for_update(key, value)
|
||||||
if updating_boolean_setting?
|
if BOOLEAN_SETTINGS.include?(key)
|
||||||
settings_params[:value] == 'true'
|
value == 'true'
|
||||||
else
|
else
|
||||||
settings_params[:value]
|
value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def updating_boolean_setting?
|
|
||||||
BOOLEAN_SETTINGS.include?(params[:id])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
19
app/controllers/admin/subscriptions_controller.rb
Normal file
19
app/controllers/admin/subscriptions_controller.rb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class SubscriptionsController < BaseController
|
||||||
|
def index
|
||||||
|
@subscriptions = ordered_subscriptions.page(requested_page)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ordered_subscriptions
|
||||||
|
Subscription.order(id: :desc).includes(:account)
|
||||||
|
end
|
||||||
|
|
||||||
|
def requested_page
|
||||||
|
params[:page].to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@@ -0,0 +1,18 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class TwoFactorAuthenticationsController < BaseController
|
||||||
|
before_action :set_user
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@user.disable_two_factor!
|
||||||
|
redirect_to admin_accounts_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_user
|
||||||
|
@user = User.find(params[:user_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@@ -1,6 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::Activitypub::ActivitiesController < ApiController
|
class Api::Activitypub::ActivitiesController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
|
||||||
# before_action :set_follow, only: [:show_follow]
|
# before_action :set_follow, only: [:show_follow]
|
||||||
before_action :set_status, only: [:show_status]
|
before_action :set_status, only: [:show_status]
|
||||||
|
|
||||||
@@ -8,7 +10,7 @@ class Api::Activitypub::ActivitiesController < ApiController
|
|||||||
|
|
||||||
# Show a status in AS2 format, as either an Announce (reblog) or a Create (post) activity.
|
# Show a status in AS2 format, as either an Announce (reblog) or a Create (post) activity.
|
||||||
def show_status
|
def show_status
|
||||||
return forbidden unless @status.permitted?
|
authorize @status, :show?
|
||||||
|
|
||||||
if @status.reblog?
|
if @status.reblog?
|
||||||
render :show_status_announce
|
render :show_status_announce
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::Activitypub::NotesController < ApiController
|
class Api::Activitypub::NotesController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
|
||||||
before_action :set_status
|
before_action :set_status
|
||||||
|
|
||||||
respond_to :activitystreams2
|
respond_to :activitystreams2
|
||||||
|
|
||||||
def show
|
def show
|
||||||
forbidden unless @status.permitted?
|
authorize @status, :show?
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::Activitypub::OutboxController < ApiController
|
class Api::Activitypub::OutboxController < Api::BaseController
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
|
|
||||||
respond_to :activitystreams2
|
respond_to :activitystreams2
|
||||||
|
@@ -1,16 +1,14 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ApiController < ApplicationController
|
class Api::BaseController < ApplicationController
|
||||||
DEFAULT_STATUSES_LIMIT = 20
|
DEFAULT_STATUSES_LIMIT = 20
|
||||||
DEFAULT_ACCOUNTS_LIMIT = 40
|
DEFAULT_ACCOUNTS_LIMIT = 40
|
||||||
|
|
||||||
protect_from_forgery with: :null_session
|
include RateLimitHeaders
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token
|
skip_before_action :verify_authenticity_token
|
||||||
skip_before_action :store_current_location
|
skip_before_action :store_current_location
|
||||||
|
|
||||||
before_action :set_rate_limit_headers
|
|
||||||
|
|
||||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
||||||
render json: { error: e.to_s }, status: 422
|
render json: { error: e.to_s }, status: 422
|
||||||
end
|
end
|
||||||
@@ -45,17 +43,6 @@ class ApiController < ApplicationController
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def set_rate_limit_headers
|
|
||||||
return if request.env['rack.attack.throttle_data'].nil?
|
|
||||||
|
|
||||||
now = Time.now.utc
|
|
||||||
match_data = request.env['rack.attack.throttle_data']['api']
|
|
||||||
|
|
||||||
response.headers['X-RateLimit-Limit'] = match_data[:limit].to_s
|
|
||||||
response.headers['X-RateLimit-Remaining'] = (match_data[:limit] - match_data[:count]).to_s
|
|
||||||
response.headers['X-RateLimit-Reset'] = (now + (match_data[:period] - now.to_i % match_data[:period])).iso8601(6)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_pagination_headers(next_path = nil, prev_path = nil)
|
def set_pagination_headers(next_path = nil, prev_path = nil)
|
||||||
links = []
|
links = []
|
||||||
links << [next_path, [%w(rel next)]] if next_path
|
links << [next_path, [%w(rel next)]] if next_path
|
||||||
@@ -93,11 +80,14 @@ class ApiController < ApplicationController
|
|||||||
if current_account.nil?
|
if current_account.nil?
|
||||||
@reblogs_map = {}
|
@reblogs_map = {}
|
||||||
@favourites_map = {}
|
@favourites_map = {}
|
||||||
|
@mutes_map = {}
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
|
status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
|
||||||
@reblogs_map = Status.reblogs_map(status_ids, current_account)
|
conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq
|
||||||
@favourites_map = Status.favourites_map(status_ids, current_account)
|
@reblogs_map = Status.reblogs_map(status_ids, current_account)
|
||||||
|
@favourites_map = Status.favourites_map(status_ids, current_account)
|
||||||
|
@mutes_map = Status.mutes_map(conversation_ids, current_account)
|
||||||
end
|
end
|
||||||
end
|
end
|
@@ -1,33 +1,25 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::OEmbedController < ApiController
|
class Api::OEmbedController < Api::BaseController
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@stream_entry = stream_entry_from_url(params[:url])
|
@stream_entry = find_stream_entry.stream_entry
|
||||||
@width = params[:maxwidth].present? ? params[:maxwidth].to_i : 400
|
@width = maxwidth_or_default
|
||||||
@height = params[:maxheight].present? ? params[:maxheight].to_i : 600
|
@height = maxheight_or_default
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def stream_entry_from_url(url)
|
def find_stream_entry
|
||||||
params = Rails.application.routes.recognize_path(url)
|
StreamEntryFinder.new(params[:url])
|
||||||
|
|
||||||
raise ActiveRecord::RecordNotFound unless recognized_stream_entry_url?(params)
|
|
||||||
|
|
||||||
stream_entry(params)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def recognized_stream_entry_url?(params)
|
def maxwidth_or_default
|
||||||
%w(stream_entries statuses).include?(params[:controller]) && params[:action] == 'show'
|
(params[:maxwidth].presence || 400).to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def stream_entry(params)
|
def maxheight_or_default
|
||||||
if params[:controller] == 'stream_entries'
|
params[:maxheight].present? ? params[:maxheight].to_i : nil
|
||||||
StreamEntry.find(params[:id])
|
|
||||||
else
|
|
||||||
Status.find(params[:id]).stream_entry
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user