Compare commits
	
		
			553 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | ||
|  | 01e011bc90 | ||
|  | e3b60b07d9 | ||
|  | d0665726ca | ||
|  | 96c84da1d4 | ||
|  | 7d36a76180 | ||
|  | 197af5de70 | ||
|  | 27301312a6 | ||
|  | 8ac7fca5d0 | ||
|  | a823509b99 | ||
|  | 298d28af51 | ||
|  | 439b2dceda | ||
|  | 9262f6968b | ||
|  | 71e73e36cd | ||
|  | 01c206326f | ||
|  | 9566893cc9 | ||
|  | 0e2589867f | ||
|  | 4acc386dd5 | ||
|  | 429480bb77 | ||
|  | 61067dc2e6 | ||
|  | effb08edbb | ||
|  | d1b4ebe07d | ||
|  | 5eef9dab80 | ||
|  | 2ca246d7d1 | ||
|  | 9a085e138e | ||
|  | 546b5a9dcf | ||
|  | a39e719b39 | ||
|  | f51b2cb2e7 | ||
|  | 9736753985 | ||
|  | ea783d3632 | ||
|  | 074e9612a2 | ||
|  | 7406404fa3 | ||
|  | 010e4f2879 | ||
|  | ebbbcfef1c | ||
|  | 3d776de2cb | ||
|  | 831ff60698 | ||
|  | c25426ca47 | ||
|  | 09b93aaf85 | ||
|  | d4fedf84e0 | ||
|  | 2af4f3c4e2 | ||
|  | b8e7eee837 | ||
|  | b48c9013aa | ||
|  | b48f2cbc8b | ||
|  | 1736badf28 | ||
|  | 88725d6ce8 | ||
|  | be0a01145b | ||
|  | 1f805a6377 | ||
|  | e595ccb294 | ||
|  | 43a29a9d0f | ||
|  | 0dd71af5fa | ||
|  | 0618f09939 | ||
|  | a23eaf720e | ||
|  | 9d3fc1281d | ||
|  | 8857cabca4 | ||
|  | affd75936e | ||
|  | 3e78b7cc3a | ||
|  | 647a148d4d | ||
|  | bb777c24ff | ||
|  | 0576daf5f9 | ||
|  | c442cade78 | ||
|  | 6004b143a8 | ||
|  | 25cbb8454c | ||
|  | 416c9675fc | ||
|  | 6a1e287053 | ||
|  | 5bda32e460 | ||
|  | 6137268e79 | ||
|  | 234e931db2 | ||
|  | fdcb55a0a6 | ||
|  | d97a0525aa | ||
|  | 193dddb433 | ||
|  | 8fe36654ef | ||
|  | 8ee1af9530 | ||
|  | e4c294432f | ||
|  | 3ebaeccec9 | ||
|  | 7177e37b99 | ||
|  | fbc5099402 | ||
|  | 45b379abac | ||
|  | 6ae975996e | ||
|  | 5d26c70a9c | ||
|  | 322cbf83c8 | ||
|  | fc99d11703 | ||
|  | 48652cb41e | ||
|  | d4f7f11c3c | ||
|  | 1ce951d0be | ||
|  | 52c119052a | ||
|  | 9317ec8eb1 | ||
|  | 1b9447853b | ||
|  | 122d59ac41 | ||
|  | 8b5179d006 | ||
|  | 3ea5b948a4 | ||
|  | 164bad171f | ||
|  | acc691851d | ||
|  | d2159deaf2 | ||
|  | 17c591ffba | ||
|  | bb04a9be52 | ||
|  | 338df98ddf | ||
|  | da022e1e4f | ||
|  | f098f55cab | ||
|  | bc955eaf61 | ||
|  | 2d99c962df | ||
|  | e59f5c8e13 | ||
|  | b073b092c9 | ||
|  | 092fdc89fa | ||
|  | d000a0b58a | ||
|  | cf845fed38 | ||
|  | 72c984e105 | ||
|  | 3d3707a077 | ||
|  | a0dd90a397 | ||
|  | 1e2a5dded7 | ||
|  | 549ce78cf5 | ||
|  | 1801a36414 | ||
|  | 501514960a | ||
|  | ef5937da1f | ||
|  | 072c6f1527 | ||
|  | 55e32fe579 | ||
|  | 7ee8e50b9c | ||
|  | 995f0ad51c | ||
|  | 948dd26931 | ||
|  | 55e1e12b7d | ||
|  | 6e27e08cb4 | ||
|  | d670f72830 | ||
|  | 57cd6546c3 | ||
|  | 1244630ab4 | ||
|  | a9a4710fe1 | ||
|  | cc83ee60fb | ||
|  | 89dc29affb | ||
|  | 85e09518b9 | ||
|  | 663f090c45 | ||
|  | 1cf9e14a41 | ||
|  | 0c2fe22bc1 | ||
|  | 6f0b3b069f | ||
|  | 9cd20a7062 | ||
|  | 219fb317ee | ||
|  | 66fd8e7821 | ||
|  | 83e3538181 | ||
|  | 723f25a999 | ||
|  | 59b1de0bcf | ||
|  | df46864b39 | ||
|  | cca41ea544 | ||
|  | 532bec6e56 | ||
|  | ee82d8a876 | ||
|  | 1646ca75f0 | ||
|  | f876665264 | ||
|  | 5817bae2da | ||
|  | 4cfc155560 | ||
|  | a1174a6d7e | ||
|  | 5357329454 | ||
|  | 7fea36d155 | ||
|  | 2bf7e81ed5 | ||
|  | ca8ae21b52 | ||
|  | d0ec4fb828 | ||
|  | 4428cf6f07 | ||
|  | fe43991d02 | ||
|  | 8565ba68f7 | ||
|  | 629d35e6f5 | ||
|  | 67dea31b0f | ||
|  | 6af21daac9 | ||
|  | 9d3be5579a | ||
|  | 05ac28f3e4 | ||
|  | 0f852c6f74 | ||
|  | 974ac467de | ||
|  | af7d02da5d | ||
|  | 5abd543766 | ||
|  | 1948f9e767 | ||
|  | 27ea2a88c1 | ||
|  | 16cd648181 | ||
|  | 53b21ac1cd | ||
|  | e4550811b2 | ||
|  | 704846a258 | ||
|  | 935aecdc32 | ||
|  | 1714f08d75 | ||
|  | 452dc6b5fe | ||
|  | 5c9aa2b732 | ||
|  | 78af88e1f4 | ||
|  | 74c474a652 | ||
|  | 5e33ad29d4 | ||
|  | 27a99b19e8 | ||
|  | ee0c897bba | ||
|  | ceecf96208 | ||
|  | 47a3036ea6 | ||
|  | d2dc31a74a | ||
|  | 60a9b938b4 | ||
|  | dd517b9a55 | ||
|  | b3329c362e | ||
|  | b00f4a0cf3 | ||
|  | a0ed88a99b | ||
|  | 25d2853db2 | ||
|  | 1930051bde | ||
|  | 6c34eafe02 | ||
|  | bbff431e3a | ||
|  | 3660a321f0 | ||
|  | e06f307c2d | ||
|  | 3b342be2f2 | ||
|  | 2dda356e3f | ||
|  | 972f6bc861 | ||
|  | d2514445e1 | ||
|  | daa46f14c4 | ||
|  | 1ada494bb2 | ||
|  | 4df26b2621 | ||
|  | 556f68ab15 | ||
|  | fdb2689a14 | ||
|  | 598d3defd5 | ||
|  | 7f8044d913 | ||
|  | 66b39ccaed | ||
|  | cae2a26ee3 | ||
|  | b79ba3db8a | ||
|  | 59a77923b3 | 
							
								
								
									
										61
									
								
								.babelrc
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								.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", | ||||||
|  |     "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: | ||||||
|  |   brakeman: | ||||||
|  |     enabled: true | ||||||
|  |   bundler-audit: | ||||||
|  |     enabled: true | ||||||
|   duplication: |   duplication: | ||||||
|     enabled: false |     enabled: false | ||||||
|  |   eslint: | ||||||
|  |     enabled: true | ||||||
|   rubocop: |   rubocop: | ||||||
|     enabled: true |     enabled: true | ||||||
|  eslint: |   scss-lint: | ||||||
|     enabled: true |     enabled: true | ||||||
| ratings: | ratings: | ||||||
|   paths: |   paths: | ||||||
|   - "**.rb" |   - "**.rb" | ||||||
|   - "**.js" |   - "**.js" | ||||||
|  |   - "**.scss" | ||||||
| exclude_paths: | exclude_paths: | ||||||
| - spec/ | - spec/ | ||||||
| - vendor/asset | - vendor/asset | ||||||
|   | |||||||
| @@ -9,3 +9,5 @@ vendor/bundle | |||||||
| .DS_Store | .DS_Store | ||||||
| *.swp | *.swp | ||||||
| *~ | *~ | ||||||
|  | postgres | ||||||
|  | redis | ||||||
|   | |||||||
| @@ -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,11 +10,14 @@ 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 | ||||||
|  |  | ||||||
| # Application secrets | # Application secrets | ||||||
| @@ -36,8 +40,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 +50,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 | ||||||
|  |  | ||||||
| @@ -90,3 +95,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 | ||||||
|   | |||||||
							
								
								
									
										79
									
								
								.eslintrc
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								.eslintrc
									
									
									
									
									
								
							| @@ -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 |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										88
									
								
								.eslintrc.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								.eslintrc.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | --- | ||||||
|  | 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: error | ||||||
|  |   no-console: warn | ||||||
|  |   no-irregular-whitespace: error | ||||||
|  |   no-unreachable: error | ||||||
|  |   valid-typeof: error | ||||||
|  |   consistent-return: error | ||||||
|  |   dot-notation: error | ||||||
|  |   eqeqeq: error | ||||||
|  |   no-fallthrough: error | ||||||
|  |   no-unused-expressions: error | ||||||
|  |   strict: off | ||||||
|  |   no-catch-shadow: error | ||||||
|  |   indent: | ||||||
|  |   - warn | ||||||
|  |   - 2 | ||||||
|  |   brace-style: warn | ||||||
|  |   comma-spacing: | ||||||
|  |   - warn | ||||||
|  |   - before: false | ||||||
|  |     after: true | ||||||
|  |   comma-style: | ||||||
|  |   - warn | ||||||
|  |   - last | ||||||
|  |   no-mixed-spaces-and-tabs: warn | ||||||
|  |   no-nested-ternary: warn | ||||||
|  |   no-trailing-spaces: warn | ||||||
|  |   semi: error | ||||||
|  |   padded-blocks: | ||||||
|  |   - error | ||||||
|  |   - classes: always | ||||||
|  |   comma-dangle: | ||||||
|  |   - error | ||||||
|  |   - always-multiline | ||||||
|  |  | ||||||
|  |   react/jsx-wrap-multilines: error | ||||||
|  |   react/jsx-no-bind: error | ||||||
|  |   react/self-closing-comp: error | ||||||
|  |   react/prop-types: error | ||||||
|  |   react/no-multi-comp: off | ||||||
|  |  | ||||||
|  |   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 | ||||||
							
								
								
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -22,7 +22,7 @@ public/assets | |||||||
| .env | .env | ||||||
| .env.production | .env.production | ||||||
| node_modules/ | node_modules/ | ||||||
| neo4j/ | build/ | ||||||
|  |  | ||||||
| # Ignore Vagrant files | # Ignore Vagrant files | ||||||
| .vagrant/ | .vagrant/ | ||||||
| @@ -43,3 +43,17 @@ redis | |||||||
| # Ignore vim files | # Ignore vim files | ||||||
| *~ | *~ | ||||||
| *.swp | *.swp | ||||||
|  | /public/packs | ||||||
|  | /node_modules | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # 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 | ||||||
							
								
								
									
										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 | ||||||
| @@ -77,6 +77,9 @@ Style/Lambda: | |||||||
| Rails/HasAndBelongsToMany: | Rails/HasAndBelongsToMany: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
|  | Bundler/OrderedGems: | ||||||
|  |   Enabled: false | ||||||
|  |  | ||||||
| AllCops: | AllCops: | ||||||
|   TargetRubyVersion: 2.3 |   TargetRubyVersion: 2.3 | ||||||
|   Exclude: |   Exclude: | ||||||
| @@ -88,3 +91,4 @@ AllCops: | |||||||
|   - 'Rakefile' |   - 'Rakefile' | ||||||
|   - 'node_modules/**/*' |   - 'node_modules/**/*' | ||||||
|   - 'Vagrantfile' |   - 'Vagrantfile' | ||||||
|  |   - 'vendor/**/*' | ||||||
|   | |||||||
							
								
								
									
										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 | ||||||
							
								
								
									
										39
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,7 +1,11 @@ | |||||||
| language: ruby | language: ruby | ||||||
| cache: bundler | cache: | ||||||
|  |   bundler: true | ||||||
|  |   yarn: true | ||||||
|  |   directories: | ||||||
|  |   - node_modules | ||||||
| dist: trusty | dist: trusty | ||||||
| sudo: required | sudo: false | ||||||
|  |  | ||||||
| notifications: | notifications: | ||||||
|   email: false |   email: false | ||||||
| @@ -11,9 +15,21 @@ 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: | ||||||
|  |     sources: | ||||||
|  |     - ubuntu-toolchain-r-test | ||||||
|  |     - trusty-media | ||||||
|  |     packages: | ||||||
|  |     - ffmpeg | ||||||
|  |     - g++-6 | ||||||
|  |     - libprotobuf-dev | ||||||
|  |     - protobuf-compiler | ||||||
|  |  | ||||||
| rvm: | rvm: | ||||||
|   - 2.3.4 |   - 2.3.4 | ||||||
| @@ -22,23 +38,18 @@ rvm: | |||||||
| services: | services: | ||||||
|   - redis-server |   - redis-server | ||||||
|  |  | ||||||
| bundler_args: --without development production --retry=3 --jobs=3 |  | ||||||
|  |  | ||||||
| before_install: |  | ||||||
|   - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test |  | ||||||
|   - sudo add-apt-repository -y ppa:mc3man/trusty-media |  | ||||||
|   - sudo apt-get -qq update |  | ||||||
|   - sudo apt-get -qq install g++-4.8 ffmpeg |  | ||||||
| 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 |   - bundle exec parallel_test spec/ --group-by filesize --type rspec | ||||||
|   - npm test |   - npm test | ||||||
|   - i18n-tasks unused |   - bundle exec i18n-tasks unused | ||||||
|   | |||||||
							
								
								
									
										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 } | ||||||
|   | |||||||
							
								
								
									
										53
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -3,39 +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 \ | ||||||
|     build-base" \ |     postgresql-dev \ | ||||||
|  && apk -U upgrade && apk add \ |     protobuf-dev \ | ||||||
|     $BUILD_DEPS \ |     python \ | ||||||
|     nodejs@edge \ |  && apk add \ | ||||||
|     nodejs-npm@edge \ |     ca-certificates \ | ||||||
|  |     ffmpeg \ | ||||||
|  |     file \ | ||||||
|  |     git \ | ||||||
|  |     imagemagick@edge \ | ||||||
|     libpq \ |     libpq \ | ||||||
|     libxml2 \ |     libxml2 \ | ||||||
|     libxslt \ |     libxslt \ | ||||||
|     ffmpeg \ |     nodejs-npm@edge \ | ||||||
|     file \ |     nodejs@edge \ | ||||||
|     imagemagick@edge \ |     protobuf \ | ||||||
|  |     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 \ |  && update-ca-certificates \ | ||||||
|  && yarn --ignore-optional \ |  | ||||||
|  && yarn cache clean \ |  | ||||||
|  && npm -g cache clean \ |  | ||||||
|  && 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"] | ||||||
|   | |||||||
							
								
								
									
										160
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -3,101 +3,101 @@ | |||||||
| 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.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 'nokogiri' | gem 'httplog', '~> 0.99' | ||||||
| gem 'oj' | gem 'kaminari', '~> 1.0' | ||||||
| gem 'ostatus2', '~> 1.1' | gem 'link_header', '~> 0.0' | ||||||
| gem 'ox' | gem 'nokogiri', '~> 1.7' | ||||||
| gem 'rabl' | gem 'oj', '~> 3.0' | ||||||
| gem 'rack-attack' | gem 'ostatus2', '~> 2.0' | ||||||
| gem 'rack-cors', require: 'rack/cors' | gem 'ox', '~> 2.5' | ||||||
| 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 'sidekiq' | gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis'] | ||||||
| gem 'sidekiq-unique-jobs' | gem 'rqrcode', '~> 0.10' | ||||||
| gem 'simple-navigation' | gem 'ruby-oembed', '~> 0.12', require: 'oembed' | ||||||
| gem 'simple_form' | gem 'sanitize', '~> 4.4' | ||||||
| gem 'sprockets-rails', :require => 'sprockets/railtie' | gem 'sidekiq', '~> 5.0' | ||||||
| gem 'statsd-instrument' | gem 'sidekiq-scheduler', '~> 2.1' | ||||||
| gem 'twitter-text' | gem 'sidekiq-unique-jobs', '~> 5.0' | ||||||
| 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 'faker', '~> 1.7' | ||||||
|   gem 'microformats2' |   gem 'microformats2', '~> 3.0' | ||||||
|   gem 'rails-controller-testing' |   gem 'rails-controller-testing', '~> 1.0' | ||||||
|   gem 'rspec-sidekiq' |   gem 'rspec-sidekiq', '~> 3.0' | ||||||
|   gem 'simplecov', require: false |   gem 'simplecov', '~> 0.14', require: false | ||||||
|   gem 'webmock' |   gem 'webmock', '~> 3.0' | ||||||
|  |   gem 'parallel_tests', '~> 2.14' | ||||||
| end | end | ||||||
|  |  | ||||||
| group :development do | group :development do | ||||||
|   gem 'rubocop', require: false |   gem 'active_record_query_trace', '~> 1.5' | ||||||
|   gem 'better_errors' |   gem 'annotate', '~> 2.7' | ||||||
|   gem 'binding_of_caller' |   gem 'better_errors', '~> 2.1' | ||||||
|   gem 'letter_opener' |   gem 'binding_of_caller', '~> 0.7' | ||||||
|   gem 'letter_opener_web' |   gem 'bullet', '~> 5.5' | ||||||
|   gem 'bullet' |   gem 'letter_opener', '~> 1.4' | ||||||
|   gem 'active_record_query_trace' |   gem 'letter_opener_web', '~> 1.3' | ||||||
|  |   gem 'rubocop', '~> 0.48', require: false | ||||||
|  |   gem 'brakeman', '~> 3.6', require: false | ||||||
|  |   gem 'bundler-audit', '~> 0.5', require: false | ||||||
|  |   gem 'scss_lint', '~> 0.53', require: false | ||||||
|  |  | ||||||
|   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 | ||||||
|   | |||||||
							
								
								
									
										470
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										470
									
								
								Gemfile.lock
									
									
									
									
									
								
							| @@ -1,40 +1,40 @@ | |||||||
| GEM | GEM | ||||||
|   remote: https://rubygems.org/ |   remote: https://rubygems.org/ | ||||||
|   specs: |   specs: | ||||||
|     actioncable (5.0.2) |     actioncable (5.0.3) | ||||||
|       actionpack (= 5.0.2) |       actionpack (= 5.0.3) | ||||||
|       nio4r (>= 1.2, < 3.0) |       nio4r (>= 1.2, < 3.0) | ||||||
|       websocket-driver (~> 0.6.1) |       websocket-driver (~> 0.6.1) | ||||||
|     actionmailer (5.0.2) |     actionmailer (5.0.3) | ||||||
|       actionpack (= 5.0.2) |       actionpack (= 5.0.3) | ||||||
|       actionview (= 5.0.2) |       actionview (= 5.0.3) | ||||||
|       activejob (= 5.0.2) |       activejob (= 5.0.3) | ||||||
|       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.0.3) | ||||||
|       actionview (= 5.0.2) |       actionview (= 5.0.3) | ||||||
|       activesupport (= 5.0.2) |       activesupport (= 5.0.3) | ||||||
|       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.0.3) | ||||||
|       activesupport (= 5.0.2) |       activesupport (= 5.0.3) | ||||||
|       builder (~> 3.1) |       builder (~> 3.1) | ||||||
|       erubis (~> 2.7.0) |       erubis (~> 2.7.0) | ||||||
|       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.0.3) | ||||||
|       activesupport (= 5.0.2) |       activesupport (= 5.0.3) | ||||||
|       globalid (>= 0.3.6) |       globalid (>= 0.3.6) | ||||||
|     activemodel (5.0.2) |     activemodel (5.0.3) | ||||||
|       activesupport (= 5.0.2) |       activesupport (= 5.0.3) | ||||||
|     activerecord (5.0.2) |     activerecord (5.0.3) | ||||||
|       activemodel (= 5.0.2) |       activemodel (= 5.0.3) | ||||||
|       activesupport (= 5.0.2) |       activesupport (= 5.0.3) | ||||||
|       arel (~> 7.0) |       arel (~> 7.0) | ||||||
|     activesupport (5.0.2) |     activesupport (5.0.3) | ||||||
|       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) | ||||||
|  |     annotate (2.7.1) | ||||||
|  |       activerecord (>= 3.2, < 6.0) | ||||||
|  |       rake (>= 10.4, < 12.0) | ||||||
|     arel (7.1.4) |     arel (7.1.4) | ||||||
|     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.1) |  | ||||||
|       execjs |  | ||||||
|     av (0.9.0) |     av (0.9.0) | ||||||
|       cocaine (~> 0.5.3) |       cocaine (~> 0.5.3) | ||||||
|     aws-sdk (2.9.6) |     aws-sdk (2.9.21) | ||||||
|       aws-sdk-resources (= 2.9.6) |       aws-sdk-resources (= 2.9.21) | ||||||
|     aws-sdk-core (2.9.6) |     aws-sdk-core (2.9.21) | ||||||
|       aws-sigv4 (~> 1.0) |       aws-sigv4 (~> 1.0) | ||||||
|       jmespath (~> 1.0) |       jmespath (~> 1.0) | ||||||
|     aws-sdk-resources (2.9.6) |     aws-sdk-resources (2.9.21) | ||||||
|       aws-sdk-core (= 2.9.6) |       aws-sdk-core (= 2.9.21) | ||||||
|     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 (0.2.14) | ||||||
|       addressable (>= 2.4.0) |       msgpack (~> 1.0) | ||||||
|       railties (>= 4.0.0, < 5.1) |     brakeman (3.6.1) | ||||||
|       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,17 +85,15 @@ 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 (>= 3.1) |  | ||||||
|     capistrano-rails (1.2.3) |     capistrano-rails (1.2.3) | ||||||
|       capistrano (~> 3.1) |       capistrano (~> 3.1) | ||||||
|       capistrano-bundler (~> 1.1) |       capistrano-bundler (~> 1.1) | ||||||
|     capistrano-rbenv (2.1.0) |     capistrano-rbenv (2.1.1) | ||||||
|       capistrano (~> 3.1) |       capistrano (~> 3.1) | ||||||
|       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.0) | ||||||
|       addressable |       addressable | ||||||
|       mime-types (>= 1.16) |       mime-types (>= 1.16) | ||||||
|       nokogiri (>= 1.3.3) |       nokogiri (>= 1.3.3) | ||||||
| @@ -107,7 +101,9 @@ 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) | ||||||
| @@ -116,11 +112,12 @@ GEM | |||||||
|     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) | ||||||
|     debug_inspector (0.0.2) |     crass (1.0.2) | ||||||
|     devise (4.2.1) |     debug_inspector (0.0.3) | ||||||
|  |     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) | ||||||
| @@ -135,29 +132,30 @@ GEM | |||||||
|       unf (>= 0.0.5, < 1.0.0) |       unf (>= 0.0.5, < 1.0.0) | ||||||
|     doorkeeper (4.2.5) |     doorkeeper (4.2.5) | ||||||
|       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) | ||||||
|     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) | ||||||
|     globalid (0.3.7) |     globalid (0.4.0) | ||||||
|       activesupport (>= 4.1.0) |       activesupport (>= 4.2.0) | ||||||
|     goldfinger (1.1.2) |     goldfinger (1.2.0) | ||||||
|       addressable (~> 2.4) |       addressable (~> 2.4) | ||||||
|       http (~> 2.0) |       http (~> 2.0) | ||||||
|       nokogiri (~> 1.6) |       nokogiri (~> 1.6) | ||||||
| @@ -170,24 +168,25 @@ 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) | ||||||
|     http (2.2.1) |     http (2.2.2) | ||||||
|       addressable (~> 2.3) |       addressable (~> 2.3) | ||||||
|       http-cookie (~> 1.0) |       http-cookie (~> 1.0) | ||||||
|       http-form_data (~> 1.0.1) |       http-form_data (~> 1.0.1) | ||||||
|       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.2) |     httplog (0.99.3) | ||||||
|       colorize |       colorize | ||||||
|  |       rack | ||||||
|     i18n (0.8.1) |     i18n (0.8.1) | ||||||
|     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) | ||||||
| @@ -198,11 +197,7 @@ 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) |     json (2.1.0) | ||||||
|       rails-dom-testing (>= 1, < 3) |  | ||||||
|       railties (>= 4.2.0) |  | ||||||
|       thor (>= 0.14, < 2.0) |  | ||||||
|     json (2.0.3) |  | ||||||
|     kaminari (1.0.1) |     kaminari (1.0.1) | ||||||
|       activesupport (>= 4.1.0) |       activesupport (>= 4.1.0) | ||||||
|       kaminari-actionview (= 1.0.1) |       kaminari-actionview (= 1.0.1) | ||||||
| @@ -224,17 +219,16 @@ 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) | ||||||
|     lograge (0.4.1) |     lograge (0.5.1) | ||||||
|       actionpack (>= 4, < 5.1) |       actionpack (>= 4, < 5.2) | ||||||
|       activesupport (>= 4, < 5.1) |       activesupport (>= 4, < 5.2) | ||||||
|       railties (>= 4, < 5.1) |       railties (>= 4, < 5.2) | ||||||
|     loofah (2.0.3) |     loofah (2.0.3) | ||||||
|       nokogiri (>= 1.5.9) |       nokogiri (>= 1.5.9) | ||||||
|     mail (2.6.4) |     mail (2.6.5) | ||||||
|       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) | ||||||
| @@ -242,22 +236,26 @@ GEM | |||||||
|     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.1.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.0.0) | ||||||
|     nokogiri (1.7.1) |     nokogiri (1.7.2) | ||||||
|       mini_portile2 (~> 2.1.0) |       mini_portile2 (~> 2.1.0) | ||||||
|     oj (2.18.5) |     nokogumbo (1.4.11) | ||||||
|  |       nokogiri | ||||||
|  |     oj (3.0.9) | ||||||
|     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.0) | ||||||
|       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.11) |     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) | ||||||
| @@ -267,12 +265,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.4) |     pghero (1.7.0) | ||||||
|       activerecord |       activerecord | ||||||
|     pkg-config (1.1.7) |     pkg-config (1.2.0) | ||||||
|     powerpack (0.1.1) |     powerpack (0.1.1) | ||||||
|     pry (0.10.4) |     pry (0.10.4) | ||||||
|       coderay (~> 1.1.0) |       coderay (~> 1.1.0) | ||||||
| @@ -284,60 +285,50 @@ GEM | |||||||
|     puma (3.8.2) |     puma (3.8.2) | ||||||
|     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.0.3) | ||||||
|       actioncable (= 5.0.2) |       actioncable (= 5.0.3) | ||||||
|       actionmailer (= 5.0.2) |       actionmailer (= 5.0.3) | ||||||
|       actionpack (= 5.0.2) |       actionpack (= 5.0.3) | ||||||
|       actionview (= 5.0.2) |       actionview (= 5.0.3) | ||||||
|       activejob (= 5.0.2) |       activejob (= 5.0.3) | ||||||
|       activemodel (= 5.0.2) |       activemodel (= 5.0.3) | ||||||
|       activerecord (= 5.0.2) |       activerecord (= 5.0.3) | ||||||
|       activesupport (= 5.0.2) |       activesupport (= 5.0.3) | ||||||
|       bundler (>= 1.3.0, < 2.0) |       bundler (>= 1.3.0, < 2.0) | ||||||
|       railties (= 5.0.2) |       railties (= 5.0.3) | ||||||
|       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.0.3) | ||||||
|       rails_serve_static_assets |       actionpack (= 5.0.3) | ||||||
|       rails_stdout_logging |       activesupport (= 5.0.3) | ||||||
|     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.1) |     rainbow (2.2.2) | ||||||
|     rake (12.0.0) |       rake | ||||||
|     react-rails (1.11.0) |     rake (11.3.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) | ||||||
| @@ -346,8 +337,10 @@ 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-rack (2.0.1) |     redis-namespace (1.5.3) | ||||||
|       rack (>= 2.0, < 3) |       redis (~> 3.0, >= 3.0.4) | ||||||
|  |     redis-rack (2.0.2) | ||||||
|  |       rack (>= 1.5, < 3) | ||||||
|       redis-store (>= 1.2, < 1.4) |       redis-store (>= 1.2, < 1.4) | ||||||
|     redis-rails (5.0.2) |     redis-rails (5.0.2) | ||||||
|       redis-actionpack (>= 5.0, < 6) |       redis-actionpack (>= 5.0, < 6) | ||||||
| @@ -355,31 +348,32 @@ 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.48.1) | ||||||
|       parser (>= 2.3.3.1, < 3.0) |       parser (>= 2.3.3.1, < 3.0) | ||||||
|       powerpack (~> 0.1) |       powerpack (~> 0.1) | ||||||
| @@ -388,32 +382,40 @@ 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.0) | ||||||
|  |       et-orbi (~> 1.0) | ||||||
|     safe_yaml (1.0.4) |     safe_yaml (1.0.4) | ||||||
|     sass (3.4.23) |     sanitize (4.4.0) | ||||||
|     sass-rails (5.0.6) |       crass (~> 1.0.2) | ||||||
|       railties (>= 4.0.0, < 6) |       nokogiri (>= 1.4.4) | ||||||
|       sass (~> 3.1) |       nokogumbo (~> 1.4.1) | ||||||
|       sprockets (>= 2.8, < 4.0) |     sass (3.4.24) | ||||||
|       sprockets-rails (>= 2.0, < 4.0) |     scss_lint (0.53.0) | ||||||
|       tilt (>= 1.1, < 3) |       rake (>= 0.9, < 13) | ||||||
|     sidekiq (4.2.10) |       sass (~> 3.4.20) | ||||||
|  |     sidekiq (5.0.0) | ||||||
|       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-scheduler (2.1.4) | ||||||
|       sidekiq (>= 4.0) |       redis (~> 3) | ||||||
|       thor |       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) | ||||||
| @@ -427,8 +429,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) | ||||||
| @@ -443,19 +445,22 @@ GEM | |||||||
|       execjs (>= 0.3.0, < 3) |       execjs (>= 0.3.0, < 3) | ||||||
|     unf (0.1.4) |     unf (0.1.4) | ||||||
|       unf_ext |       unf_ext | ||||||
|     unf_ext (0.0.7.3) |     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) | ||||||
|     webmock (2.3.2) |     webmock (3.0.1) | ||||||
|       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.0.0) |     xpath (2.0.0) | ||||||
|       nokogiri (~> 1.3) |       nokogiri (~> 1.3) | ||||||
|  |  | ||||||
| @@ -463,86 +468,87 @@ 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 |   devise (~> 4.2) | ||||||
|   doorkeeper |   devise-two-factor (~> 3.0) | ||||||
|   dotenv-rails |   doorkeeper (~> 4.2) | ||||||
|   fabrication |   dotenv-rails (~> 2.2) | ||||||
|   faker |   fabrication (~> 2.16) | ||||||
|   fast_blank |   faker (~> 1.7) | ||||||
|   font-awesome-rails |   fast_blank (~> 1.0) | ||||||
|   fuubar |   fuubar (~> 2.2) | ||||||
|   goldfinger |   goldfinger (~> 1.2) | ||||||
|   hamlit-rails |   hamlit-rails (~> 0.2) | ||||||
|   hiredis |   hiredis (~> 0.6) | ||||||
|   htmlentities |   htmlentities (~> 4.3) | ||||||
|   http |   http (~> 2.2) | ||||||
|   http_accept_language |   http_accept_language (~> 2.1) | ||||||
|   httplog |   httplog (~> 0.99) | ||||||
|   i18n-tasks (~> 0.9.6) |   i18n-tasks (~> 0.9) | ||||||
|   jquery-rails |   kaminari (~> 1.0) | ||||||
|   kaminari |   letter_opener (~> 1.4) | ||||||
|   letter_opener |   letter_opener_web (~> 1.3) | ||||||
|   letter_opener_web |   link_header (~> 0.0) | ||||||
|   link_header |   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 |   rabl (~> 0.13) | ||||||
|   rack-cors |   rack-attack (~> 5.0) | ||||||
|   rack-timeout |   rack-cors (~> 0.4) | ||||||
|   rails (~> 5.0.2) |   rack-timeout (~> 0.4) | ||||||
|   rails-controller-testing |   rails (~> 5.0) | ||||||
|   rails-i18n |   rails-controller-testing (~> 1.0) | ||||||
|   rails-settings-cached |   rails-i18n (~> 5.0) | ||||||
|   rails_12factor |   rails-settings-cached (~> 0.6) | ||||||
|   react-rails |   redis (~> 3.3) | ||||||
|   redis (~> 3.2) |   redis-namespace (~> 1.5) | ||||||
|   redis-rails |   redis-rails (~> 5.0) | ||||||
|   rqrcode |   rqrcode (~> 0.10) | ||||||
|   rspec-rails |   rspec-rails (~> 3.6) | ||||||
|   rspec-sidekiq |   rspec-sidekiq (~> 3.0) | ||||||
|   rubocop |   rubocop (~> 0.48) | ||||||
|   ruby-oembed |   ruby-oembed (~> 0.12) | ||||||
|   sass-rails (~> 5.0) |   sanitize (~> 4.4) | ||||||
|   sidekiq |   scss_lint (~> 0.53) | ||||||
|   sidekiq-unique-jobs |   sidekiq (~> 5.0) | ||||||
|   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 | ||||||
|   | |||||||
							
								
								
									
										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). | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								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,47 +33,40 @@ 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 rbenv | # Install rvm | ||||||
| git clone https://github.com/rbenv/rbenv.git ~/.rbenv | read RUBY_VERSION < .ruby-version | ||||||
| cd ~/.rbenv && src/configure && make -C src | gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 | ||||||
| echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile | curl -sSL https://get.rvm.io | bash -s stable --ruby=$RUBY_VERSION | ||||||
| echo 'eval "$(rbenv init -)"' >> ~/.bash_profile | source /home/vagrant/.rvm/scripts/rvm | ||||||
|  |  | ||||||
| git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build |  | ||||||
|  |  | ||||||
| export PATH="$HOME/.rbenv/bin:$PATH" |  | ||||||
| eval "$(rbenv init -)" |  | ||||||
|  |  | ||||||
| cd /vagrant |  | ||||||
|  |  | ||||||
| echo "Compiling Ruby $(cat .ruby-version): warning, this takes a while!!!" |  | ||||||
| rbenv install $(cat .ruby-version) |  | ||||||
| rbenv global $(cat .ruby-version) |  | ||||||
|  |  | ||||||
| # Configure database | # Configure database | ||||||
| sudo -u postgres createuser -U postgres vagrant -s | 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) | ||||||
| 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 | ||||||
|  |  | ||||||
| @@ -83,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 | ||||||
| @@ -113,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 jquery |  | ||||||
| //= require jquery_ujs |  | ||||||
| //= require components |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| //= require jquery |  | ||||||
| //= require jquery_ujs |  | ||||||
| //= require extras |  | ||||||
| //= require best_in_place |  | ||||||
|  |  | ||||||
| $(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,64 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
|  |  | ||||||
| const Avatar = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     src: React.PropTypes.string.isRequired, |  | ||||||
|     staticSrc: React.PropTypes.string, |  | ||||||
|     size: React.PropTypes.number.isRequired, |  | ||||||
|     style: React.PropTypes.object, |  | ||||||
|     animate: React.PropTypes.bool |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   getDefaultProps () { |  | ||||||
|     return { |  | ||||||
|       animate: false |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   getInitialState () { |  | ||||||
|     return { |  | ||||||
|       hovering: false |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   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='avatar' |  | ||||||
|         onMouseEnter={this.handleMouseEnter} |  | ||||||
|         onMouseLeave={this.handleMouseLeave} |  | ||||||
|         style={style} |  | ||||||
|       /> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default Avatar; |  | ||||||
| @@ -1,62 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
|  |  | ||||||
| const Button = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     text: React.PropTypes.node, |  | ||||||
|     onClick: React.PropTypes.func, |  | ||||||
|     disabled: React.PropTypes.bool, |  | ||||||
|     block: React.PropTypes.bool, |  | ||||||
|     secondary: React.PropTypes.bool, |  | ||||||
|     size: React.PropTypes.number, |  | ||||||
|     style: React.PropTypes.object, |  | ||||||
|     children: React.PropTypes.node |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   getDefaultProps () { |  | ||||||
|     return { |  | ||||||
|       size: 36 |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   handleClick (e) { |  | ||||||
|     if (!this.props.disabled) { |  | ||||||
|       this.props.onClick(); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const style = { |  | ||||||
|       fontFamily: 'inherit', |  | ||||||
|       display: this.props.block ? 'block' : 'inline-block', |  | ||||||
|       width: this.props.block ? '100%' : 'auto', |  | ||||||
|       position: 'relative', |  | ||||||
|       boxSizing: 'border-box', |  | ||||||
|       textAlign: 'center', |  | ||||||
|       border: '10px none', |  | ||||||
|       fontSize: '14px', |  | ||||||
|       fontWeight: '500', |  | ||||||
|       letterSpacing: '0', |  | ||||||
|       padding: `0 ${this.props.size / 2.25}px`, |  | ||||||
|       height: `${this.props.size}px`, |  | ||||||
|       cursor: 'pointer', |  | ||||||
|       lineHeight: `${this.props.size}px`, |  | ||||||
|       borderRadius: '4px', |  | ||||||
|       textDecoration: 'none', |  | ||||||
|       whiteSpace: 'nowrap', |  | ||||||
|       textOverflow: 'ellipsis', |  | ||||||
|       overflow: 'hidden' |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     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> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default Button; |  | ||||||
| @@ -1,44 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import { FormattedMessage } from 'react-intl'; |  | ||||||
|  |  | ||||||
| const outerStyle = { |  | ||||||
|   position: 'absolute', |  | ||||||
|   right: '0', |  | ||||||
|   top: '-48px', |  | ||||||
|   padding: '15px', |  | ||||||
|   fontSize: '16px', |  | ||||||
|   flex: '0 0 auto', |  | ||||||
|   cursor: 'pointer' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const iconStyle = { |  | ||||||
|   display: 'inline-block', |  | ||||||
|   marginRight: '5px' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const ColumnBackButtonSlim = React.createClass({ |  | ||||||
|  |  | ||||||
|   contextTypes: { |  | ||||||
|     router: React.PropTypes.object |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   handleClick () { |  | ||||||
|     this.context.router.push('/'); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     return ( |  | ||||||
|       <div style={{ position: 'relative' }}> |  | ||||||
|         <div role='button' tabIndex='0' style={outerStyle} onClick={this.handleClick} className='column-back-button'> |  | ||||||
|           <i className='fa fa-fw fa-chevron-left' style={iconStyle} /> |  | ||||||
|           <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default ColumnBackButtonSlim; |  | ||||||
| @@ -1,65 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import { Motion, spring } from 'react-motion'; |  | ||||||
|  |  | ||||||
| const iconStyle = { |  | ||||||
|   fontSize: '16px', |  | ||||||
|   padding: '15px', |  | ||||||
|   position: 'absolute', |  | ||||||
|   right: '0', |  | ||||||
|   top: '-48px', |  | ||||||
|   cursor: 'pointer', |  | ||||||
|   zIndex: '3' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const ColumnCollapsable = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     icon: React.PropTypes.string.isRequired, |  | ||||||
|     title: React.PropTypes.string, |  | ||||||
|     fullHeight: React.PropTypes.number.isRequired, |  | ||||||
|     children: React.PropTypes.node, |  | ||||||
|     onCollapse: React.PropTypes.func |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   getInitialState () { |  | ||||||
|     return { |  | ||||||
|       collapsed: true |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   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 style={{ position: 'relative' }}> |  | ||||||
|         <div role='button' tabIndex='0' title={`${title}`} style={{...iconStyle }} 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> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default ColumnCollapsable; |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import escapeTextContentForBrowser from 'escape-html'; |  | ||||||
| import emojify from '../emoji'; |  | ||||||
|  |  | ||||||
| const DisplayName = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     account: ImmutablePropTypes.map.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const displayName     = this.props.account.get('display_name').length === 0 ? this.props.account.get('username') : this.props.account.get('display_name'); |  | ||||||
|     const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) }; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <span style={{ display: 'block', maxWidth: '100%', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }} className='display-name'> |  | ||||||
|         <strong style={{ fontWeight: '500' }} dangerouslySetInnerHTML={displayNameHTML} /> <span style={{ fontSize: '14px' }}>@{this.props.account.get('acct')}</span> |  | ||||||
|       </span> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default DisplayName; |  | ||||||
| @@ -1,72 +0,0 @@ | |||||||
| import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown'; |  | ||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
|  |  | ||||||
| const DropdownMenu = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     icon: React.PropTypes.string.isRequired, |  | ||||||
|     items: React.PropTypes.array.isRequired, |  | ||||||
|     size: React.PropTypes.number.isRequired, |  | ||||||
|     direction: React.PropTypes.string |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   getDefaultProps () { |  | ||||||
|     return { |  | ||||||
|       direction: 'left' |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   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={i} className='dropdown__sep' />; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const { text, action, href = '#' } = item; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <li key={i}> |  | ||||||
|         <a href={href} target='_blank' rel='noopener' onClick={this.handleClick.bind(this, i)}> |  | ||||||
|           {text} |  | ||||||
|         </a> |  | ||||||
|       </li> |  | ||||||
|     ); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { icon, items, size, direction } = 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` }}> |  | ||||||
|           <i className={`fa fa-fw fa-${icon}`} style={{ verticalAlign: 'middle' }} /> |  | ||||||
|         </DropdownTrigger> |  | ||||||
|  |  | ||||||
|         <DropdownContent className={directionClass} style={{ lineHeight: '18px', textAlign: 'left' }}> |  | ||||||
|           <ul> |  | ||||||
|             {items.map(this.renderItem)} |  | ||||||
|           </ul> |  | ||||||
|         </DropdownContent> |  | ||||||
|       </Dropdown> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default DropdownMenu; |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| const Permalink = React.createClass({ |  | ||||||
|  |  | ||||||
|   contextTypes: { |  | ||||||
|     router: React.PropTypes.object |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     href: React.PropTypes.string.isRequired, |  | ||||||
|     to: React.PropTypes.string.isRequired, |  | ||||||
|     children: React.PropTypes.node |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleClick (e) { |  | ||||||
|     if (e.button === 0) { |  | ||||||
|       e.preventDefault(); |  | ||||||
|       this.context.router.push(this.props.to); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { href, children, ...other } = this.props; |  | ||||||
|  |  | ||||||
|     return <a href={href} onClick={this.handleClick} {...other}>{children}</a>; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default Permalink; |  | ||||||
| @@ -1,124 +0,0 @@ | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| 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: 'Reblog' }, |  | ||||||
|   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, |  | ||||||
|   open: { id: 'status.open', defaultMessage: 'Expand this status' }, |  | ||||||
|   report: { id: 'status.report', defaultMessage: 'Report @{name}' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const StatusActionBar = React.createClass({ |  | ||||||
|  |  | ||||||
|   contextTypes: { |  | ||||||
|     router: React.PropTypes.object |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     status: ImmutablePropTypes.map.isRequired, |  | ||||||
|     onReply: React.PropTypes.func, |  | ||||||
|     onFavourite: React.PropTypes.func, |  | ||||||
|     onReblog: React.PropTypes.func, |  | ||||||
|     onDelete: React.PropTypes.func, |  | ||||||
|     onMention: React.PropTypes.func, |  | ||||||
|     onMute: React.PropTypes.func, |  | ||||||
|     onBlock: React.PropTypes.func, |  | ||||||
|     onReport: React.PropTypes.func, |  | ||||||
|     me: React.PropTypes.number.isRequired, |  | ||||||
|     intl: React.PropTypes.object.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   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; |  | ||||||
|     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 style={{ marginTop: '10px', overflow: 'hidden' }}> |  | ||||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton title={reply_title} icon={reply_icon} onClick={this.handleReplyClick} /></div> |  | ||||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton disabled={status.get('visibility') === 'private' || status.get('visibility') === 'direct'} active={status.get('reblogged')} title={intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} /></div> |  | ||||||
|         <div style={{ float: 'left', marginRight: '18px'}}><IconButton animate={true} active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> |  | ||||||
|  |  | ||||||
|         <div style={{ width: '18px', height: '18px', float: 'left' }}> |  | ||||||
|           <DropdownMenu items={menu} icon='ellipsis-h' size={18} direction="right" /> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default injectIntl(StatusActionBar); |  | ||||||
| @@ -1,252 +0,0 @@ | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import IconButton from './icon_button'; |  | ||||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; |  | ||||||
| import { isIOS } from '../is_mobile'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   toggle_sound: { id: 'video_player.toggle_sound', defaultMessage: 'Toggle sound' }, |  | ||||||
|   toggle_visible: { id: 'video_player.toggle_visible', defaultMessage: 'Toggle visibility' }, |  | ||||||
|   expand_video: { id: 'video_player.expand', defaultMessage: 'Expand video' }, |  | ||||||
|   expand_video: { id: 'video_player.video_error', defaultMessage: 'Video could not be played' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const videoStyle = { |  | ||||||
|   position: 'relative', |  | ||||||
|   zIndex: '1', |  | ||||||
|   width: '100%', |  | ||||||
|   height: '100%', |  | ||||||
|   objectFit: 'cover', |  | ||||||
|   top: '50%', |  | ||||||
|   transform: 'translateY(-50%)' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const muteStyle = { |  | ||||||
|   position: 'absolute', |  | ||||||
|   top: '4px', |  | ||||||
|   right: '4px', |  | ||||||
|   color: 'white', |  | ||||||
|   textShadow: "0px 1px 1px black, 1px 0px 1px black", |  | ||||||
|   opacity: '0.8', |  | ||||||
|   zIndex: '5' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const coverStyle = { |  | ||||||
|   marginTop: '8px', |  | ||||||
|   textAlign: 'center', |  | ||||||
|   height: '100%', |  | ||||||
|   cursor: 'pointer', |  | ||||||
|   display: 'flex', |  | ||||||
|   alignItems: 'center', |  | ||||||
|   justifyContent: 'center', |  | ||||||
|   flexDirection: 'column', |  | ||||||
|   position: 'relative' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const spoilerSpanStyle = { |  | ||||||
|   display: 'block', |  | ||||||
|   fontSize: '14px' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const spoilerSubSpanStyle = { |  | ||||||
|   display: 'block', |  | ||||||
|   fontSize: '11px', |  | ||||||
|   fontWeight: '500' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const spoilerButtonStyle = { |  | ||||||
|   position: 'absolute', |  | ||||||
|   top: '4px', |  | ||||||
|   left: '4px', |  | ||||||
|   color: 'white', |  | ||||||
|   textShadow: "0px 1px 1px black, 1px 0px 1px black", |  | ||||||
|   zIndex: '100' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const expandButtonStyle = { |  | ||||||
|   position: 'absolute', |  | ||||||
|   bottom: '4px', |  | ||||||
|   right: '4px', |  | ||||||
|   color: 'white', |  | ||||||
|   textShadow: "0px 1px 1px black, 1px 0px 1px black", |  | ||||||
|   zIndex: '100' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const VideoPlayer = React.createClass({ |  | ||||||
|   propTypes: { |  | ||||||
|     media: ImmutablePropTypes.map.isRequired, |  | ||||||
|     width: React.PropTypes.number, |  | ||||||
|     height: React.PropTypes.number, |  | ||||||
|     sensitive: React.PropTypes.bool, |  | ||||||
|     intl: React.PropTypes.object.isRequired, |  | ||||||
|     autoplay: React.PropTypes.bool, |  | ||||||
|     onOpenVideo: React.PropTypes.func.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   getDefaultProps () { |  | ||||||
|     return { |  | ||||||
|       width: 239, |  | ||||||
|       height: 110 |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   getInitialState () { |  | ||||||
|     return { |  | ||||||
|       visible: !this.props.sensitive, |  | ||||||
|       preview: true, |  | ||||||
|       muted: true, |  | ||||||
|       hasAudio: true, |  | ||||||
|       videoError: false |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   handleClick () { |  | ||||||
|     this.setState({ muted: !this.state.muted }); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleVideoClick (e) { |  | ||||||
|     e.stopPropagation(); |  | ||||||
|  |  | ||||||
|     const node = ReactDOM.findDOMNode(this).querySelector('video'); |  | ||||||
|  |  | ||||||
|     if (node.paused) { |  | ||||||
|       node.play(); |  | ||||||
|     } else { |  | ||||||
|       node.pause(); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleOpen () { |  | ||||||
|     this.setState({ preview: !this.state.preview }); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleVisibility () { |  | ||||||
|     this.setState({ |  | ||||||
|       visible: !this.state.visible, |  | ||||||
|       preview: true |  | ||||||
|     }); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleExpand () { |  | ||||||
|     this.video.pause(); |  | ||||||
|     this.props.onOpenVideo(this.props.media, this.video.currentTime); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   setRef (c) { |  | ||||||
|     this.video = c; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleLoadedData () { |  | ||||||
|     if (('WebkitAppearance' in document.documentElement.style && this.video.audioTracks.length === 0) || this.video.mozHasAudio === false) { |  | ||||||
|       this.setState({ hasAudio: false }); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleVideoError () { |  | ||||||
|     this.setState({ videoError: true }); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   componentDidMount () { |  | ||||||
|     if (!this.video) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     this.video.addEventListener('loadeddata', this.handleLoadedData); |  | ||||||
|     this.video.addEventListener('error', this.handleVideoError); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   componentDidUpdate () { |  | ||||||
|     if (!this.video) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     this.video.addEventListener('loadeddata', this.handleLoadedData); |  | ||||||
|     this.video.addEventListener('error', this.handleVideoError); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   componentWillUnmount () { |  | ||||||
|     if (!this.video) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     this.video.removeEventListener('loadeddata', this.handleLoadedData); |  | ||||||
|     this.video.removeEventListener('error', this.handleVideoError); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { media, intl, width, height, sensitive, autoplay } = this.props; |  | ||||||
|  |  | ||||||
|     let spoilerButton = ( |  | ||||||
|       <div style={{...spoilerButtonStyle, display: !this.state.visible ? 'none' : 'block'}} > |  | ||||||
|         <IconButton overlay title={intl.formatMessage(messages.toggle_visible)} icon={this.state.visible ? 'eye' : 'eye-slash'} onClick={this.handleVisibility} /> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let expandButton = ( |  | ||||||
|       <div style={expandButtonStyle} > |  | ||||||
|         <IconButton overlay title={intl.formatMessage(messages.expand_video)} icon='expand' onClick={this.handleExpand} /> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let muteButton = ''; |  | ||||||
|  |  | ||||||
|     if (this.state.hasAudio) { |  | ||||||
|       muteButton = ( |  | ||||||
|         <div style={muteStyle}> |  | ||||||
|           <IconButton overlay title={intl.formatMessage(messages.toggle_sound)} icon={this.state.muted ? 'volume-off' : 'volume-up'} onClick={this.handleClick} /> |  | ||||||
|         </div> |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!this.state.visible) { |  | ||||||
|       if (sensitive) { |  | ||||||
|         return ( |  | ||||||
|           <div role='button' tabIndex='0' style={{...coverStyle, width: `${width}px`, height: `${height}px` }} className='media-spoiler' onClick={this.handleVisibility}> |  | ||||||
|             {spoilerButton} |  | ||||||
|             <span style={spoilerSpanStyle}><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span> |  | ||||||
|             <span style={spoilerSubSpanStyle}><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span> |  | ||||||
|           </div> |  | ||||||
|         ); |  | ||||||
|       } else { |  | ||||||
|         return ( |  | ||||||
|           <div role='button' tabIndex='0' style={{...coverStyle, width: `${width}px`, height: `${height}px` }} className='media-spoiler' onClick={this.handleVisibility}> |  | ||||||
|             {spoilerButton} |  | ||||||
|             <span style={spoilerSpanStyle}><FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' /></span> |  | ||||||
|             <span style={spoilerSubSpanStyle}><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span> |  | ||||||
|           </div> |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (this.state.preview && !autoplay) { |  | ||||||
|       return ( |  | ||||||
|         <div role='button' tabIndex='0' style={{ cursor: 'pointer', position: 'relative', marginTop: '8px', width: `${width}px`, height: `${height}px`, background: `url(${media.get('preview_url')}) no-repeat center`, backgroundSize: 'cover' }} onClick={this.handleOpen}> |  | ||||||
|           {spoilerButton} |  | ||||||
|           <div style={{ position: 'absolute', top: '50%', left: '50%', fontSize: '36px', transform: 'translate(-50%, -50%)', padding: '5px', borderRadius: '100px', color: 'rgba(255, 255, 255, 0.8)' }}><i className='fa fa-play' /></div> |  | ||||||
|         </div> |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (this.state.videoError) { |  | ||||||
|       return ( |  | ||||||
|         <div style={{...coverStyle, width: `${width}px`, height: `${height}px` }} className='video-error-cover' > |  | ||||||
|           <span style={spoilerSpanStyle}><FormattedMessage id='video_player.video_error' defaultMessage='Video could not be played' /></span> |  | ||||||
|         </div> |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div style={{ cursor: 'default', marginTop: '8px', overflow: 'hidden', width: `${width}px`, height: `${height}px`, boxSizing: 'border-box', background: '#000', position: 'relative' }}> |  | ||||||
|         {spoilerButton} |  | ||||||
|         {muteButton} |  | ||||||
|         {expandButton} |  | ||||||
|         <video role='button' tabIndex='0' ref={this.setRef} src={media.get('url')} autoPlay={!isIOS()} loop={true} muted={this.state.muted} style={videoStyle} onClick={this.handleVideoClick} /> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default injectIntl(VideoPlayer); |  | ||||||
| @@ -1,97 +0,0 @@ | |||||||
| import { connect } from 'react-redux'; |  | ||||||
| import Status from '../components/status'; |  | ||||||
| import { makeGetStatus } from '../selectors'; |  | ||||||
| import { |  | ||||||
|   replyCompose, |  | ||||||
|   mentionCompose |  | ||||||
| } from '../actions/compose'; |  | ||||||
| import { |  | ||||||
|   reblog, |  | ||||||
|   favourite, |  | ||||||
|   unreblog, |  | ||||||
|   unfavourite |  | ||||||
| } from '../actions/interactions'; |  | ||||||
| import { |  | ||||||
|   blockAccount, |  | ||||||
|   muteAccount |  | ||||||
| } from '../actions/accounts'; |  | ||||||
| import { deleteStatus } from '../actions/statuses'; |  | ||||||
| import { initReport } from '../actions/reports'; |  | ||||||
| import { openModal } from '../actions/modal'; |  | ||||||
| import { createSelector } from 'reselect' |  | ||||||
| import { isMobile } from '../is_mobile' |  | ||||||
|  |  | ||||||
| const makeMapStateToProps = () => { |  | ||||||
|   const getStatus = makeGetStatus(); |  | ||||||
|  |  | ||||||
|   const mapStateToProps = (state, props) => ({ |  | ||||||
|     status: getStatus(state, props.id), |  | ||||||
|     me: state.getIn(['meta', 'me']), |  | ||||||
|     boostModal: state.getIn(['meta', 'boost_modal']), |  | ||||||
|     autoPlayGif: state.getIn(['meta', 'auto_play_gif']) |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   return mapStateToProps; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const mapDispatchToProps = (dispatch) => ({ |  | ||||||
|  |  | ||||||
|   onReply (status, router) { |  | ||||||
|     dispatch(replyCompose(status, router)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onModalReblog (status) { |  | ||||||
|     dispatch(reblog(status)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onReblog (status, e) { |  | ||||||
|     if (status.get('reblogged')) { |  | ||||||
|       dispatch(unreblog(status)); |  | ||||||
|     } else { |  | ||||||
|       if (e.shiftKey || !this.boostModal) { |  | ||||||
|         this.onModalReblog(status); |  | ||||||
|       } else { |  | ||||||
|         dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog })); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onFavourite (status) { |  | ||||||
|     if (status.get('favourited')) { |  | ||||||
|       dispatch(unfavourite(status)); |  | ||||||
|     } else { |  | ||||||
|       dispatch(favourite(status)); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onDelete (status) { |  | ||||||
|     dispatch(deleteStatus(status.get('id'))); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onMention (account, router) { |  | ||||||
|     dispatch(mentionCompose(account, router)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onOpenMedia (media, index) { |  | ||||||
|     dispatch(openModal('MEDIA', { media, index })); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onOpenVideo (media, time) { |  | ||||||
|     dispatch(openModal('VIDEO', { media, time })); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onBlock (account) { |  | ||||||
|     dispatch(blockAccount(account.get('id'))); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onReport (status) { |  | ||||||
|     dispatch(initReport(status.get('account'), status)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onMute (account) { |  | ||||||
|     dispatch(muteAccount(account.get('id'))); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default connect(makeMapStateToProps, mapDispatchToProps)(Status); |  | ||||||
| @@ -1,73 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import InnerHeader from '../../account/components/header'; |  | ||||||
| import ActionBar from '../../account/components/action_bar'; |  | ||||||
| import MissingIndicator from '../../../components/missing_indicator'; |  | ||||||
|  |  | ||||||
| const Header = React.createClass({ |  | ||||||
|   contextTypes: { |  | ||||||
|     router: React.PropTypes.object |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     account: ImmutablePropTypes.map, |  | ||||||
|     me: React.PropTypes.number.isRequired, |  | ||||||
|     onFollow: React.PropTypes.func.isRequired, |  | ||||||
|     onBlock: React.PropTypes.func.isRequired, |  | ||||||
|     onMention: React.PropTypes.func.isRequired, |  | ||||||
|     onReport: React.PropTypes.func.isRequired, |  | ||||||
|     onMute: React.PropTypes.func.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   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> |  | ||||||
|         <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> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default Header; |  | ||||||
| @@ -1,60 +0,0 @@ | |||||||
| import { connect } from 'react-redux'; |  | ||||||
| import { makeGetAccount } from '../../../selectors'; |  | ||||||
| import Header from '../components/header'; |  | ||||||
| import { |  | ||||||
|   followAccount, |  | ||||||
|   unfollowAccount, |  | ||||||
|   blockAccount, |  | ||||||
|   unblockAccount, |  | ||||||
|   muteAccount, |  | ||||||
|   unmuteAccount |  | ||||||
| } from '../../../actions/accounts'; |  | ||||||
| import { mentionCompose } from '../../../actions/compose'; |  | ||||||
| import { initReport } from '../../../actions/reports'; |  | ||||||
|  |  | ||||||
| const makeMapStateToProps = () => { |  | ||||||
|   const getAccount = makeGetAccount(); |  | ||||||
|  |  | ||||||
|   const mapStateToProps = (state, { accountId }) => ({ |  | ||||||
|     account: getAccount(state, Number(accountId)), |  | ||||||
|     me: state.getIn(['meta', 'me']) |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   return mapStateToProps; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const mapDispatchToProps = dispatch => ({ |  | ||||||
|   onFollow (account) { |  | ||||||
|     if (account.getIn(['relationship', 'following'])) { |  | ||||||
|       dispatch(unfollowAccount(account.get('id'))); |  | ||||||
|     } else { |  | ||||||
|       dispatch(followAccount(account.get('id'))); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onBlock (account) { |  | ||||||
|     if (account.getIn(['relationship', 'blocking'])) { |  | ||||||
|       dispatch(unblockAccount(account.get('id'))); |  | ||||||
|     } else { |  | ||||||
|       dispatch(blockAccount(account.get('id'))); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onMention (account, router) { |  | ||||||
|     dispatch(mentionCompose(account, router)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onReport (account) { |  | ||||||
|     dispatch(initReport(account)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onMute (account) { |  | ||||||
|     if (account.getIn(['relationship', 'muting'])) { |  | ||||||
|       dispatch(unmuteAccount(account.get('id'))); |  | ||||||
|     } else { |  | ||||||
|       dispatch(muteAccount(account.get('id'))); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default connect(makeMapStateToProps, mapDispatchToProps)(Header); |  | ||||||
| @@ -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 style={{ overflow: 'hidden' }} className='autosuggest-account'> |  | ||||||
|     <div style={{ float: 'left', marginRight: '5px' }}><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 style={{ overflow: 'hidden' }} 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,27 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
|  |  | ||||||
| const CharacterCounter = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     text: React.PropTypes.string.isRequired, |  | ||||||
|     max: React.PropTypes.number.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   checkRemainingText (diff) { |  | ||||||
|     if (diff <= 0) { |  | ||||||
|       return <span style={{ fontSize: '16px', cursor: 'default', color: '#ff5050' }}>{diff}</span>; |  | ||||||
|     } |  | ||||||
|     return <span style={{ fontSize: '16px', cursor: 'default' }}>{diff}</span>; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const diff = this.props.max - this.props.text.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "_").length; |  | ||||||
|  |  | ||||||
|     return this.checkRemainingText(diff); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default CharacterCounter; |  | ||||||
| @@ -1,58 +0,0 @@ | |||||||
| import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown'; |  | ||||||
| import EmojiPicker from 'emojione-picker'; |  | ||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const settings = { |  | ||||||
|   imageType: 'png', |  | ||||||
|   sprites: false, |  | ||||||
|   imagePathPNG: '/emoji/' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const style = { |  | ||||||
|   position: 'absolute', |  | ||||||
|   right: '5px', |  | ||||||
|   top: '5px' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const EmojiPickerDropdown = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     intl: React.PropTypes.object.isRequired, |  | ||||||
|     onPickEmoji: React.PropTypes.func.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   setRef (c) { |  | ||||||
|     this.dropdown = c; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleChange (data) { |  | ||||||
|     this.dropdown.hide(); |  | ||||||
|     this.props.onPickEmoji(data); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { intl } = this.props; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <Dropdown ref={this.setRef} style={style}> |  | ||||||
|         <DropdownTrigger className='emoji-button' title={intl.formatMessage(messages.emoji)} style={{ fontSize: `24px`, width: `24px`, lineHeight: `24px`, display: 'block', marginLeft: '2px' }}> |  | ||||||
|           <img draggable="false" className="emojione" alt="🙂" src="/emoji/1f602.svg" /> |  | ||||||
|         </DropdownTrigger> |  | ||||||
|  |  | ||||||
|         <DropdownContent className='dropdown__left light'> |  | ||||||
|           <EmojiPicker emojione={settings} onChange={this.handleChange} search={true} /> |  | ||||||
|         </DropdownContent> |  | ||||||
|       </Dropdown> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default injectIntl(EmojiPickerDropdown); |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import Avatar from '../../../components/avatar'; |  | ||||||
| import IconButton from '../../../components/icon_button'; |  | ||||||
| import DisplayName from '../../../components/display_name'; |  | ||||||
| import Permalink from '../../../components/permalink'; |  | ||||||
| import { FormattedMessage } from 'react-intl'; |  | ||||||
| import { Link } from 'react-router'; |  | ||||||
|  |  | ||||||
| const NavigationBar = React.createClass({ |  | ||||||
|   propTypes: { |  | ||||||
|     account: ImmutablePropTypes.map.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     return ( |  | ||||||
|       <div className='navigation-bar'> |  | ||||||
|         <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`} style={{ textDecoration: 'none' }}><Avatar src={this.props.account.get('avatar')} animate size={40} /></Permalink> |  | ||||||
|  |  | ||||||
|         <div style={{ flex: '1 1 auto', marginLeft: '8px' }}> |  | ||||||
|           <strong style={{ fontWeight: '500', display: 'block' }}>{this.props.account.get('acct')}</strong> |  | ||||||
|           <a href='/settings/profile' style={{ color: 'inherit', textDecoration: 'none' }}><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default NavigationBar; |  | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
|  |  | ||||||
| const TextIconButton = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     label: React.PropTypes.string.isRequired, |  | ||||||
|     title: React.PropTypes.string, |  | ||||||
|     active: React.PropTypes.bool, |  | ||||||
|     onClick: React.PropTypes.func.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   handleClick (e) { |  | ||||||
|     e.preventDefault(); |  | ||||||
|     this.props.onClick(); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { label, title, active } = this.props; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <button title={title} aria-label={title} className={`text-icon-button ${active ? 'active' : ''}`} onClick={this.handleClick}> |  | ||||||
|         {label} |  | ||||||
|       </button> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default TextIconButton; |  | ||||||
| @@ -1,53 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import IconButton from '../../../components/icon_button'; |  | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   upload: { id: 'upload_button.label', defaultMessage: 'Add media' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const iconStyle = { |  | ||||||
|   lineHeight: '27px', |  | ||||||
|   height: null |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const UploadButton = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     disabled: React.PropTypes.bool, |  | ||||||
|     onSelectFile: React.PropTypes.func.isRequired, |  | ||||||
|     style: React.PropTypes.object, |  | ||||||
|     resetFileKey: React.PropTypes.number, |  | ||||||
|     intl: React.PropTypes.object.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   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 style={this.props.style}> |  | ||||||
|         <IconButton icon='camera' title={intl.formatMessage(messages.upload)} disabled={disabled} onClick={this.handleClick} style={iconStyle} size={18} inverted /> |  | ||||||
|         <input key={resetFileKey} ref={this.setRef} type='file' multiple={false} onChange={this.handleChange} disabled={disabled} style={{ display: 'none' }} /> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default injectIntl(UploadButton); |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import IconButton from '../../../components/icon_button'; |  | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; |  | ||||||
| import UploadProgressContainer from '../containers/upload_progress_container'; |  | ||||||
| import { Motion, spring } from 'react-motion'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   undo: { id: 'upload_form.undo', defaultMessage: 'Undo' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const UploadForm = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     media: ImmutablePropTypes.list.isRequired, |  | ||||||
|     onRemoveFile: React.PropTypes.func.isRequired, |  | ||||||
|     intl: React.PropTypes.object.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { intl, media } = this.props; |  | ||||||
|  |  | ||||||
|     const uploads = media.map(attachment => |  | ||||||
|       <div key={attachment.get('id')} style={{ margin: '5px', flex: '1 1 0' }}> |  | ||||||
|         <Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}> |  | ||||||
|           {({ scale }) => |  | ||||||
|             <div style={{ transform: `translateZ(0) scale(${scale})`, width: '100%', height: '100px', borderRadius: '4px', background: `url(${attachment.get('preview_url')}) no-repeat center`, backgroundSize: 'cover' }}> |  | ||||||
|               <IconButton icon='times' title={intl.formatMessage(messages.undo)} size={36} onClick={this.props.onRemoveFile.bind(this, attachment.get('id'))} /> |  | ||||||
|             </div> |  | ||||||
|           } |  | ||||||
|         </Motion> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div style={{ overflow: 'hidden' }}> |  | ||||||
|         <UploadProgressContainer /> |  | ||||||
|         <div style={{ display: 'flex', padding: '5px' }}>{uploads}</div> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default injectIntl(UploadForm); |  | ||||||
| @@ -1,78 +0,0 @@ | |||||||
| import { connect } from 'react-redux'; |  | ||||||
| import ComposeForm from '../components/compose_form'; |  | ||||||
| import { uploadCompose } from '../../../actions/compose'; |  | ||||||
| import { createSelector } from 'reselect'; |  | ||||||
| import { |  | ||||||
|   changeCompose, |  | ||||||
|   submitCompose, |  | ||||||
|   clearComposeSuggestions, |  | ||||||
|   fetchComposeSuggestions, |  | ||||||
|   selectComposeSuggestion, |  | ||||||
|   changeComposeSpoilerText, |  | ||||||
|   insertEmojiCompose |  | ||||||
| } from '../../../actions/compose'; |  | ||||||
|  |  | ||||||
| const getMentionedUsernames = createSelector(state => state.getIn(['compose', 'text']), text => text.match(/(?:^|[^\/\w])@([a-z0-9_]+@[a-z0-9\.\-]+)/ig)); |  | ||||||
|  |  | ||||||
| const getMentionedDomains = createSelector(getMentionedUsernames, mentionedUsernamesWithDomains => { |  | ||||||
|   return mentionedUsernamesWithDomains !== null ? [...new Set(mentionedUsernamesWithDomains.map(item => item.split('@')[2]))] : []; |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const mapStateToProps = (state, props) => { |  | ||||||
|   const mentionedUsernames = getMentionedUsernames(state); |  | ||||||
|   const mentionedUsernamesWithDomains = getMentionedDomains(state); |  | ||||||
|  |  | ||||||
|   return { |  | ||||||
|     text: state.getIn(['compose', 'text']), |  | ||||||
|     suggestion_token: state.getIn(['compose', 'suggestion_token']), |  | ||||||
|     suggestions: state.getIn(['compose', 'suggestions']), |  | ||||||
|     spoiler: state.getIn(['compose', 'spoiler']), |  | ||||||
|     spoiler_text: state.getIn(['compose', 'spoiler_text']), |  | ||||||
|     privacy: state.getIn(['compose', 'privacy']), |  | ||||||
|     focusDate: state.getIn(['compose', 'focusDate']), |  | ||||||
|     preselectDate: state.getIn(['compose', 'preselectDate']), |  | ||||||
|     is_submitting: state.getIn(['compose', 'is_submitting']), |  | ||||||
|     is_uploading: state.getIn(['compose', 'is_uploading']), |  | ||||||
|     me: state.getIn(['compose', 'me']), |  | ||||||
|     needsPrivacyWarning: (state.getIn(['compose', 'privacy']) === 'private' || state.getIn(['compose', 'privacy']) === 'direct') && mentionedUsernames !== null, |  | ||||||
|     mentionedDomains: mentionedUsernamesWithDomains |  | ||||||
|   }; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const mapDispatchToProps = (dispatch) => ({ |  | ||||||
|  |  | ||||||
|   onChange (text) { |  | ||||||
|     dispatch(changeCompose(text)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onSubmit () { |  | ||||||
|     dispatch(submitCompose()); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onClearSuggestions () { |  | ||||||
|     dispatch(clearComposeSuggestions()); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onFetchSuggestions (token) { |  | ||||||
|     dispatch(fetchComposeSuggestions(token)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onSuggestionSelected (position, token, accountId) { |  | ||||||
|     dispatch(selectComposeSuggestion(position, token, accountId)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onChangeSpoilerText (checked) { |  | ||||||
|     dispatch(changeComposeSpoilerText(checked)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onPaste (files) { |  | ||||||
|     dispatch(uploadCompose(files)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   onPickEmoji (position, data) { |  | ||||||
|     dispatch(insertEmojiCompose(position, data)); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm); |  | ||||||
| @@ -1,58 +0,0 @@ | |||||||
| 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 outerStyle = { |  | ||||||
|   padding: '14px 10px' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const panelStyle = { |  | ||||||
|   display: 'flex', |  | ||||||
|   flexDirection: 'row', |  | ||||||
|   padding: '10px 0' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const btnStyle = { |  | ||||||
|   flex: '1 1 auto', |  | ||||||
|   textAlign: 'center' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => { |  | ||||||
|   const content = { __html: emojify(account.get('note')) }; |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     <div> |  | ||||||
|       <div style={outerStyle}> |  | ||||||
|         <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name' style={{ display: 'block', overflow: 'hidden', marginBottom: '15px' }}> |  | ||||||
|           <div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div> |  | ||||||
|           <DisplayName account={account} /> |  | ||||||
|         </Permalink> |  | ||||||
|  |  | ||||||
|         <div style={{ fontSize: '14px' }} className='account__header__content' dangerouslySetInnerHTML={content} /> |  | ||||||
|       </div> |  | ||||||
|  |  | ||||||
|       <div className='account--panel' style={panelStyle}> |  | ||||||
|         <div style={btnStyle}><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div> |  | ||||||
|         <div style={btnStyle}><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div> |  | ||||||
|       </div> |  | ||||||
|     </div> |  | ||||||
|   ) |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| AccountAuthorize.propTypes = { |  | ||||||
|   account: ImmutablePropTypes.map.isRequired, |  | ||||||
|   onAuthorize: React.PropTypes.func.isRequired, |  | ||||||
|   onReject: React.PropTypes.func.isRequired, |  | ||||||
|   intl: React.PropTypes.object.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default injectIntl(AccountAuthorize); |  | ||||||
| @@ -1,60 +0,0 @@ | |||||||
| import Column from '../ui/components/column'; |  | ||||||
| import ColumnLink from '../ui/components/column_link'; |  | ||||||
| import { Link } from 'react-router'; |  | ||||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; |  | ||||||
| import { connect } from 'react-redux'; |  | ||||||
| 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' }, |  | ||||||
|   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)}> |  | ||||||
|       <div style={{ position: 'relative' }}> |  | ||||||
|         <ColumnLink icon='users' text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' /> |  | ||||||
|         <ColumnLink icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' /> |  | ||||||
|         <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' /> |  | ||||||
|         <ColumnLink icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' /> |  | ||||||
|         {followRequests} |  | ||||||
|         <ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' /> |  | ||||||
|         <ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' /> |  | ||||||
|         <ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' /> |  | ||||||
|         <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: React.PropTypes.object.isRequired, |  | ||||||
|   me: ImmutablePropTypes.map.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default connect(mapStateToProps)(injectIntl(GettingStarted)); |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
|  |  | ||||||
| const style = { |  | ||||||
|   display: 'block', |  | ||||||
|   fontFamily: 'inherit', |  | ||||||
|   marginBottom: '10px', |  | ||||||
|   padding: '7px 0', |  | ||||||
|   boxSizing: 'border-box', |  | ||||||
|   width: '100%' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const SettingText = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     settings: ImmutablePropTypes.map.isRequired, |  | ||||||
|     settingKey: React.PropTypes.array.isRequired, |  | ||||||
|     label: React.PropTypes.string.isRequired, |  | ||||||
|     onChange: React.PropTypes.func.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleChange (e) { |  | ||||||
|     this.props.onChange(this.props.settingKey, e.target.value) |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { settings, settingKey, label } = this.props; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <input |  | ||||||
|         style={style} |  | ||||||
|         className='setting-text' |  | ||||||
|         value={settings.getIn(settingKey)} |  | ||||||
|         onChange={this.handleChange} |  | ||||||
|         placeholder={label} |  | ||||||
|       /> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default SettingText; |  | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| import { connect } from 'react-redux'; |  | ||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| 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 |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const HomeTimeline = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     intl: React.PropTypes.object.isRequired, |  | ||||||
|     hasUnread: React.PropTypes.bool |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { intl, hasUnread } = this.props; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <Column icon='home' active={hasUnread} heading={intl.formatMessage(messages.title)}> |  | ||||||
|         <ColumnSettingsContainer /> |  | ||||||
|         <StatusListContainer {...this.props} 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> |  | ||||||
|     ); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default connect(mapStateToProps)(injectIntl(HomeTimeline)); |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import Toggle from 'react-toggle'; |  | ||||||
|  |  | ||||||
| const labelStyle = { |  | ||||||
|   display: 'block', |  | ||||||
|   lineHeight: '24px', |  | ||||||
|   verticalAlign: 'middle' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const labelSpanStyle = { |  | ||||||
|   display: 'inline-block', |  | ||||||
|   verticalAlign: 'middle', |  | ||||||
|   marginBottom: '14px', |  | ||||||
|   marginLeft: '8px' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const SettingToggle = ({ settings, settingKey, label, onChange, htmlFor = '' }) => ( |  | ||||||
|   <label htmlFor={htmlFor} style={labelStyle}> |  | ||||||
|     <Toggle checked={settings.getIn(settingKey)} onChange={(e) => onChange(settingKey, e.target.checked)} /> |  | ||||||
|     <span className='setting-toggle' style={labelSpanStyle}>{label}</span> |  | ||||||
|   </label> |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| SettingToggle.propTypes = { |  | ||||||
|   settings: ImmutablePropTypes.map.isRequired, |  | ||||||
|   settingKey: React.PropTypes.array.isRequired, |  | ||||||
|   label: React.PropTypes.node.isRequired, |  | ||||||
|   onChange: React.PropTypes.func.isRequired, |  | ||||||
|   htmlFor: React.PropTypes.string |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default SettingToggle; |  | ||||||
| @@ -1,90 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import IconButton from '../../../components/icon_button'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import DropdownMenu from '../../../components/dropdown_menu'; |  | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   delete: { id: 'status.delete', defaultMessage: 'Delete' }, |  | ||||||
|   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' }, |  | ||||||
|   reply: { id: 'status.reply', defaultMessage: 'Reply' }, |  | ||||||
|   reblog: { id: 'status.reblog', defaultMessage: 'Reblog' }, |  | ||||||
|   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, |  | ||||||
|   report: { id: 'status.report', defaultMessage: 'Report @{name}' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const ActionBar = React.createClass({ |  | ||||||
|  |  | ||||||
|   contextTypes: { |  | ||||||
|     router: React.PropTypes.object |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     status: ImmutablePropTypes.map.isRequired, |  | ||||||
|     onReply: React.PropTypes.func.isRequired, |  | ||||||
|     onReblog: React.PropTypes.func.isRequired, |  | ||||||
|     onFavourite: React.PropTypes.func.isRequired, |  | ||||||
|     onDelete: React.PropTypes.func.isRequired, |  | ||||||
|     onMention: React.PropTypes.func.isRequired, |  | ||||||
|     onReport: React.PropTypes.func, |  | ||||||
|     me: React.PropTypes.number.isRequired, |  | ||||||
|     intl: React.PropTypes.object.isRequired |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   handleReplyClick () { |  | ||||||
|     this.props.onReply(this.props.status); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleReblogClick (e) { |  | ||||||
|     this.props.onReblog(this.props.status, e); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleFavouriteClick () { |  | ||||||
|     this.props.onFavourite(this.props.status); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleDeleteClick () { |  | ||||||
|     this.props.onDelete(this.props.status); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleMentionClick () { |  | ||||||
|     this.props.onMention(this.props.status.get('account'), this.context.router); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   handleReport () { |  | ||||||
|     this.props.onReport(this.props.status); |  | ||||||
|     this.context.router.push('/report'); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { status, me, intl } = this.props; |  | ||||||
|  |  | ||||||
|     let menu = []; |  | ||||||
|  |  | ||||||
|     if (me === status.getIn(['account', 'id'])) { |  | ||||||
|       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.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'; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div className='detailed-status__action-bar'> |  | ||||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_id', null) === null ? 'reply' : 'reply-all'} onClick={this.handleReplyClick} /></div> |  | ||||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton disabled={status.get('visibility') === 'direct' || status.get('visibility') === 'private'} active={status.get('reblogged')} title={intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} /></div> |  | ||||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton animate={true} active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> |  | ||||||
|         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><DropdownMenu size={18} icon='ellipsis-h' items={menu} direction="left" /></div> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default injectIntl(ActionBar); |  | ||||||
| @@ -1,69 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
|  |  | ||||||
| const contentStyle = { |  | ||||||
|   flex: '1 1 auto', |  | ||||||
|   padding: '8px', |  | ||||||
|   paddingLeft: '14px', |  | ||||||
|   overflow: 'hidden' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const imageStyle = { |  | ||||||
|   display: 'block', |  | ||||||
|   width: '100%', |  | ||||||
|   height: 'auto', |  | ||||||
|   margin: '0', |  | ||||||
|   borderRadius: '4px 0 0 4px' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const hostStyle = { |  | ||||||
|   display: 'block', |  | ||||||
|   marginTop: '5px', |  | ||||||
|   fontSize: '13px' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const getHostname = url => { |  | ||||||
|   const parser = document.createElement('a'); |  | ||||||
|   parser.href = url; |  | ||||||
|   return parser.hostname; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const Card = React.createClass({ |  | ||||||
|   propTypes: { |  | ||||||
|     card: ImmutablePropTypes.map |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { card } = this.props; |  | ||||||
|  |  | ||||||
|     if (card === null) { |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let image = ''; |  | ||||||
|  |  | ||||||
|     if (card.get('image')) { |  | ||||||
|       image = ( |  | ||||||
|         <div className='status-card__image'> |  | ||||||
|           <img src={card.get('image')} alt={card.get('title')} style={imageStyle} /> |  | ||||||
|         </div> |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <a href={card.get('url')} className='status-card' target='_blank' rel='noopener'> |  | ||||||
|         {image} |  | ||||||
|  |  | ||||||
|         <div className='status-card__content' style={contentStyle}> |  | ||||||
|           <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong> |  | ||||||
|           <p className='status-card__description'>{card.get('description').substring(0, 50)}</p> |  | ||||||
|           <span className='status-card__host' style={hostStyle}>{getHostname(card.get('url'))}</span> |  | ||||||
|         </div> |  | ||||||
|       </a> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default Card; |  | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
|  |  | ||||||
| const ColumnHeader = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     icon: React.PropTypes.string, |  | ||||||
|     type: React.PropTypes.string, |  | ||||||
|     active: React.PropTypes.bool, |  | ||||||
|     onClick: React.PropTypes.func |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   handleClick () { |  | ||||||
|     this.props.onClick(); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { type, active } = this.props; |  | ||||||
|  |  | ||||||
|     let icon = ''; |  | ||||||
|  |  | ||||||
|     if (this.props.icon) { |  | ||||||
|       icon = <i className={`fa fa-fw fa-${this.props.icon}`} style={{ display: 'inline-block', marginRight: '5px' }} />; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div role='button' tabIndex='0' aria-label={type} className={`column-header ${active ? 'active' : ''}`} onClick={this.handleClick}> |  | ||||||
|         {icon} |  | ||||||
|         {type} |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default ColumnHeader; |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| import { Link } from 'react-router'; |  | ||||||
|  |  | ||||||
| const outerStyle = { |  | ||||||
|   display: 'block', |  | ||||||
|   padding: '15px', |  | ||||||
|   fontSize: '16px', |  | ||||||
|   textDecoration: 'none' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const iconStyle = { |  | ||||||
|   display: 'inline-block', |  | ||||||
|   marginRight: '5px' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const ColumnLink = ({ icon, text, to, href, method }) => { |  | ||||||
|   if (href) { |  | ||||||
|     return ( |  | ||||||
|       <a href={href} style={outerStyle} className='column-link' data-method={method}> |  | ||||||
|         <i className={`fa fa-fw fa-${icon}`} style={iconStyle} /> |  | ||||||
|         {text} |  | ||||||
|       </a> |  | ||||||
|     ); |  | ||||||
|   } else { |  | ||||||
|     return ( |  | ||||||
|       <Link to={to} style={outerStyle} className='column-link'> |  | ||||||
|         <i className={`fa fa-fw fa-${icon}`} style={iconStyle} /> |  | ||||||
|         {text} |  | ||||||
|       </Link> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| ColumnLink.propTypes = { |  | ||||||
|   icon: React.PropTypes.string.isRequired, |  | ||||||
|   text: React.PropTypes.string.isRequired, |  | ||||||
|   to: React.PropTypes.string, |  | ||||||
|   href: React.PropTypes.string, |  | ||||||
|   method: React.PropTypes.string |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default ColumnLink; |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; |  | ||||||
|  |  | ||||||
| const style = { |  | ||||||
|   display: 'flex', |  | ||||||
|   flex: '1 1 auto', |  | ||||||
|   overflowX: 'auto' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const ColumnsArea = React.createClass({ |  | ||||||
|  |  | ||||||
|   propTypes: { |  | ||||||
|     children: React.PropTypes.node |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   mixins: [PureRenderMixin], |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     return ( |  | ||||||
|       <div className='columns-area' style={style}> |  | ||||||
|         {this.props.children} |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| export default ColumnsArea; |  | ||||||
| @@ -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,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,77 +0,0 @@ | |||||||
| const de = { |  | ||||||
|   "column_back_button.label": "Zurück", |  | ||||||
|   "lightbox.close": "Schließen", |  | ||||||
|   "loading_indicator.label": "Lade…", |  | ||||||
|   "status.mention": "Erwähnen", |  | ||||||
|   "status.delete": "Löschen", |  | ||||||
|   "status.reply": "Antworten", |  | ||||||
|   "status.reblog": "Teilen", |  | ||||||
|   "status.favourite": "Favorisieren", |  | ||||||
|   "status.reblogged_by": "{name} teilte", |  | ||||||
|   "status.sensitive_warning": "Heikle Inhalte", |  | ||||||
|   "status.sensitive_toggle": "Klicke, um sie zu sehen", |  | ||||||
|   "status.open": "Öffnen", |  | ||||||
|   "video_player.toggle_sound": "Ton umschalten", |  | ||||||
|   "account.mention": "Erwähnen", |  | ||||||
|   "account.edit_profile": "Profil bearbeiten", |  | ||||||
|   "account.unblock": "Entblocken", |  | ||||||
|   "account.unfollow": "Entfolgen", |  | ||||||
|   "account.block": "Blocken", |  | ||||||
|   "account.follow": "Folgen", |  | ||||||
|   "account.posts": "Beiträge", |  | ||||||
|   "account.follows": "Folgt", |  | ||||||
|   "account.followers": "Folgende", |  | ||||||
|   "account.follows_you": "Folgt dir", |  | ||||||
|   "account.requested": "Warte auf Erlaubnis", |  | ||||||
|   "getting_started.heading": "Erste Schritte", |  | ||||||
|   "getting_started.about_addressing": "Du kannst Leuten folgen, falls du ihren Nutzernamen und ihre Domain kennst, in dem du eine e-mail-artige Addresse in das Suchfeld oben auf der Seite eingibst.", |  | ||||||
|   "getting_started.about_shortcuts": "Falls die Person auf derselben Domain ist wie du, reicht auch ihr Nutzername alleine. Das gilt auch für Erwähnungen in Beiträgen.", |  | ||||||
|   "getting_started.about_developer": "Der Entwickler des Projekts kann unter Gargron@mastodon.social gefunden werden", |  | ||||||
|   "getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf {github} dazu beitragen oder Probleme melden.", |  | ||||||
|   "column.home": "Home", |  | ||||||
|   "column.mentions": "Erwähnungen", |  | ||||||
|   "column.public": "Gesamtes bekanntes Netz", |  | ||||||
|   "column.notifications": "Mitteilungen", |  | ||||||
|   "column.follow_requests": "Folgeanfragen", |  | ||||||
|   "tabs_bar.compose": "Schreiben", |  | ||||||
|   "tabs_bar.home": "Home", |  | ||||||
|   "tabs_bar.mentions": "Erwähnungen", |  | ||||||
|   "tabs_bar.public": "Gesamtes Netz", |  | ||||||
|   "tabs_bar.notifications": "Mitteilungen", |  | ||||||
|   "compose_form.placeholder": "Worüber möchtest du schreiben?", |  | ||||||
|   "compose_form.publish": "Tröt", |  | ||||||
|   "compose_form.sensitive": "Medien als heikel markieren", |  | ||||||
|   "compose_form.private": "Als privat markieren", |  | ||||||
|   "compose_form.unlisted": "Nicht öffentlich auflisten", |  | ||||||
|   "navigation_bar.edit_profile": "Profil bearbeiten", |  | ||||||
|   "navigation_bar.preferences": "Einstellungen", |  | ||||||
|   "navigation_bar.public_timeline": "Öffentlich", |  | ||||||
|   "navigation_bar.logout": "Abmelden", |  | ||||||
|   "navigation_bar.follow_requests": "Folgeanfragen", |  | ||||||
|   "reply_indicator.cancel": "Abbrechen", |  | ||||||
|   "search.placeholder": "Suche", |  | ||||||
|   "search.account": "Konto", |  | ||||||
|   "search.hashtag": "Hashtag", |  | ||||||
|   "upload_button.label": "Mediendatei hinzufügen", |  | ||||||
|   "upload_form.undo": "Entfernen", |  | ||||||
|   "notification.follow": "{name} folgt dir", |  | ||||||
|   "notification.favourite": "{name} favorisierte deinen Status", |  | ||||||
|   "notification.reblog": "{name} teilte deinen Status", |  | ||||||
|   "notification.mention": "{name} erwähnte dich", |  | ||||||
|   "notifications.column_settings.alert": "Desktop-Benachrichtigungen", |  | ||||||
|   "notifications.column_settings.show": "In der Spalte anzeigen", |  | ||||||
|   "notifications.column_settings.follow": "Neue Folgende:", |  | ||||||
|   "notifications.column_settings.favourite": "Favorisierungen:", |  | ||||||
|   "notifications.column_settings.mention": "Erwähnungen:", |  | ||||||
|   "notifications.column_settings.reblog": "Geteilte Beiträge:", |  | ||||||
|   "follow_request.authorize": "Erlauben", |  | ||||||
|   "follow_request.reject": "Ablehnen", |  | ||||||
|   "home.column_settings.basic": "Einfach", |  | ||||||
|   "home.column_settings.advanced": "Fortgeschritten", |  | ||||||
|   "home.column_settings.show_reblogs": "Geteilte Beiträge anzeigen", |  | ||||||
|   "home.column_settings.show_replies": "Antworten anzeigen", |  | ||||||
|   "home.column_settings.filter_regex": "Filter durch reguläre Ausdrücke", |  | ||||||
|   "missing_indicator.label": "Nicht gefunden" |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default de; |  | ||||||
| @@ -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,44 +0,0 @@ | |||||||
| import en from './en'; |  | ||||||
| import de from './de'; |  | ||||||
| import es from './es'; |  | ||||||
| import hr from './hr'; |  | ||||||
| import hu from './hu'; |  | ||||||
| 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 bg from './bg'; |  | ||||||
|  |  | ||||||
| const locales = { |  | ||||||
|   en, |  | ||||||
|   de, |  | ||||||
|   es, |  | ||||||
|   hr, |  | ||||||
|   hu, |  | ||||||
|   it, |  | ||||||
|   fr, |  | ||||||
|   nl, |  | ||||||
|   no, |  | ||||||
|   pt, |  | ||||||
|   'pt-BR': pt_br, |  | ||||||
|   uk, |  | ||||||
|   fi, |  | ||||||
|   eo, |  | ||||||
|   ru, |  | ||||||
|   ja, |  | ||||||
|   'zh-HK': zh_hk, |  | ||||||
|   bg, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default function getMessagesForLocale (locale) { |  | ||||||
|   return locales[locale]; |  | ||||||
| }; |  | ||||||
| @@ -1,95 +0,0 @@ | |||||||
| const nl = { |  | ||||||
|   "column_back_button.label": "terug", |  | ||||||
|   "lightbox.close": "Sluiten", |  | ||||||
|   "loading_indicator.label": "Laden…", |  | ||||||
|   "status.mention": "@{name} vermelden", |  | ||||||
|   "status.delete": "Verwijderen", |  | ||||||
|   "status.reply": "Reageren", |  | ||||||
|   "status.reblog": "Boost", |  | ||||||
|   "status.favourite": "Favoriet", |  | ||||||
|   "status.reblogged_by": "{name} boostte", |  | ||||||
|   "status.sensitive_warning": "Gevoelige inhoud", |  | ||||||
|   "status.sensitive_toggle": "Klik om te zien", |  | ||||||
|   "video_player.toggle_sound": "Geluid in-/uitschakelen", |  | ||||||
|   "account.mention": "Vermeld @{name}", |  | ||||||
|   "account.edit_profile": "Profiel bewerken", |  | ||||||
|   "account.unblock": "Deblokkeer @{name}", |  | ||||||
|   "account.unfollow": "Ontvolgen", |  | ||||||
|   "account.block": "Blokkeer @{name}", |  | ||||||
|   "account.follow": "Volgen", |  | ||||||
|   "account.posts": "Berichten", |  | ||||||
|   "account.follows": "Volgt", |  | ||||||
|   "account.followers": "Volgers", |  | ||||||
|   "account.follows_you": "Volgt jou", |  | ||||||
|   "account.requested": "Wacht op goedkeuring", |  | ||||||
|   "account.mute": "Negeer @{name}", |  | ||||||
|   "account.unmute": "Negeer @{name} niet meer", |  | ||||||
|   "account.report": "Rapporteer @{name}", |  | ||||||
|   "getting_started.heading": "Beginnen", |  | ||||||
|   "getting_started.about_addressing": "Je kunt mensen volgen als je hun gebruikersnaam en het domein van hun server kent. Voer hiervoor het e-mailachtige adres in het zoekveld in.", |  | ||||||
|   "getting_started.about_shortcuts": "Als de gezochte gebruiker op hetzelfde domein zit als jijzelf, is invoeren van de gebruikersnaam genoeg. Dat geldt ook als je mensen in toots wilt vermelden.", |  | ||||||
|   "getting_started.open_source_notice": "Mastodon is open-sourcesoftware. Je kunt bijdragen of problemen melden op GitHub via {github}. {apps}.", |  | ||||||
|   "getting_started.apps": "Er zijn meerdere apps beschikbaar", |  | ||||||
|   "column.home": "Jouw tijdlijn", |  | ||||||
|   "column.community": "Lokale tijdlijn", |  | ||||||
|   "column.public": "Globale tijdlijn", |  | ||||||
|   "column.notifications": "Meldingen", |  | ||||||
|   "column.favourites": "Favorieten", |  | ||||||
|   "column.blocks": "Geblokkeerde gebruikers", |  | ||||||
|   "column.mutes": "Genegeerde gebruikers", |  | ||||||
|   "tabs_bar.compose": "Schrijven", |  | ||||||
|   "tabs_bar.home": "Jouw tijdlijn", |  | ||||||
|   "tabs_bar.mentions": "Vermeldingen", |  | ||||||
|   "tabs_bar.public": "Globale tijdlijn", |  | ||||||
|   "tabs_bar.notifications": "Meldingen", |  | ||||||
|   "compose_form.placeholder": "Waar ben je mee bezig?", |  | ||||||
|   "compose_form.publish": "Toot", |  | ||||||
|   "compose_form.sensitive": "Media als gevoelig markeren", |  | ||||||
|   "compose_form.spoiler": "Tekst achter waarschuwing verbergen", |  | ||||||
|   "compose_form.spoiler_placeholder": "Waarschuwingstekst", |  | ||||||
|   "compose_form.private": "Als privé markeren", |  | ||||||
|   "compose_form.privacy_disclaimer": "Jouw privétoot wordt afgeleverd aan de vermelde gebruikers op {domains}. Vertrouw jij {domainsCount, plural, one {die server} other {die servers}}? Het privé plaatsen van toots werkt alleen op Mastodon-servers. Wanneer {domains} {domainsCount, plural, one {geen Mastodon-server is} other {geen Mastodon-servers zijn}}, dan wordt er niet aangegeven dat de toot privé is, waardoor het kan worden geboost of op een andere manier zichtbaar wordt gemaakt voor mensen waarvoor het niet was bedoeld.", |  | ||||||
|   "compose_form.unlisted": "Niet op openbare tijdlijnen tonen", |  | ||||||
|   "navigation_bar.edit_profile": "Profiel bewerken", |  | ||||||
|   "navigation_bar.preferences": "Voorkeuren", |  | ||||||
|   "navigation_bar.community_timeline": "Lokale tijdlijn", |  | ||||||
|   "navigation_bar.public_timeline": "Globale tijdlijn", |  | ||||||
|   "navigation_bar.follow_requests": "Volgverzoeken", |  | ||||||
|   "navigation_bar.info": "Uitgebreide informatie", |  | ||||||
|   "navigation_bar.blocks": "Geblokkeerde gebruikers", |  | ||||||
|   "navigation_bar.mutes": "Genegeerde gebruikers", |  | ||||||
|   "navigation_bar.logout": "Afmelden", |  | ||||||
|   "navigation_bar.favourites": "Favorieten", |  | ||||||
|   "reply_indicator.cancel": "Annuleren", |  | ||||||
|   "search.placeholder": "Zoeken", |  | ||||||
|   "search.account": "Account", |  | ||||||
|   "search.hashtag": "Hashtag", |  | ||||||
|   "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}", |  | ||||||
|   "upload_button.label": "Media toevoegen", |  | ||||||
|   "upload_form.undo": "Ongedaan maken", |  | ||||||
|   "notification.follow": "{name} volgt jou nu", |  | ||||||
|   "notification.favourite": "{name} markeerde jouw toot als favoriet", |  | ||||||
|   "notification.reblog": "{name} boostte jouw toot", |  | ||||||
|   "notification.mention": "{name} vermeldde jou", |  | ||||||
|   "notifications.clear_confirmation": "Weet je zeker dat je al jouw meldingen wilt verwijderen?", |  | ||||||
|   "notifications.clear": "Meldingen verwijderen", |  | ||||||
|   "notifications.column_settings.alert": "Desktopmeldingen", |  | ||||||
|   "notifications.column_settings.show": "In kolom tonen", |  | ||||||
|   "notifications.column_settings.follow": "Nieuwe volgers:", |  | ||||||
|   "notifications.column_settings.favourite": "Favorieten:", |  | ||||||
|   "notifications.column_settings.mention": "Vermeldingen:", |  | ||||||
|   "notifications.column_settings.reblog": "Boosts:", |  | ||||||
|   "notifications.column_settings.sound": "Geluid afspelen", |  | ||||||
|   "notifications.settings": "Kolom-instellingen", |  | ||||||
|   "privacy.change": "Privacy toot aanpassen", |  | ||||||
|   "privacy.direct.long": "Toot alleen naar vermelde gebruikers", |  | ||||||
|   "privacy.direct.short": "Direct", |  | ||||||
|   "privacy.private.long": "Toot alleen naar jouw volgers", |  | ||||||
|   "privacy.private.short": "Privé", |  | ||||||
|   "privacy.public.long": "Toot naar openbare tijdlijnen", |  | ||||||
|   "privacy.public.short": "Openbaar", |  | ||||||
|   "privacy.unlisted.long": "Niet op openbare tijdlijnen weergeven", |  | ||||||
|   "privacy.unlisted.short": "Minder openbaar", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default nl; |  | ||||||
| @@ -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": "Se 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": "Utilizaires blocats", |  | ||||||
|   "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 d'utilizaires 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'autres 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": "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": "Utilizaires blocats", |  | ||||||
|   "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,150 +0,0 @@ | |||||||
| import zh from 'react-intl/locale-data/zh'; |  | ||||||
|  |  | ||||||
| const localeData = zh.reduce(function (acc, localeData) { |  | ||||||
|   if (localeData.locale === "zh-Hant-HK") { |  | ||||||
|     // rename the locale "zh-Hant-HK" as "zh-HK" |  | ||||||
|     // (match the code usually used in Accepted-Language header) |  | ||||||
|     acc.push(Object.assign({}, |  | ||||||
|       localeData, |  | ||||||
|       { |  | ||||||
|         "locale": "zh-HK", |  | ||||||
|         "parentLocale": "zh-Hant-HK", |  | ||||||
|       } |  | ||||||
|     )); |  | ||||||
|   } |  | ||||||
|   return acc; |  | ||||||
| }, []); |  | ||||||
|  |  | ||||||
| export { localeData as localeData }; |  | ||||||
|  |  | ||||||
| const zh_hk = { |  | ||||||
|   "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.notifications": "通知", |  | ||||||
|   "column.public": "跨站公共時間軸", |  | ||||||
|   "compose_form.placeholder": "你在想甚麼?", |  | ||||||
|   "compose_form.privacy_disclaimer": "你的私人文章,將被遞送至你所提及的 {domains} 用戶。你是否信任 {domainsCount, plural, one {這個網站} other {這些網站}}?請留意,文章私隱設定只適用於各 Mastodon 服務站,如果 {domains} {domainsCount, plural, one {不是 Mastodon 服務站} other {之中有些不是 Mastodon 服務站}},對方將無法收到這篇文章的私隱設定,然後可能被轉推給不能預知的用戶閱讀。", |  | ||||||
|   "compose_form.private": "標示為「只有關注你的人能看」", |  | ||||||
|   "compose_form.publish": "發文", |  | ||||||
|   "compose_form.sensitive": "將媒體檔案標示為「敏感內容」", |  | ||||||
|   "compose_form.spoiler_placeholder": "敏感內容", |  | ||||||
|   "compose_form.spoiler": "將部份文字藏於警告訊息之後", |  | ||||||
|   "compose_form.unlisted": "請勿在公共時間軸顯示", |  | ||||||
|   "emoji_button.label": "加入表情符號", |  | ||||||
|   "empty_column.community": "本站時間軸暫時未有內容,快貼文來搶頭香啊!", |  | ||||||
|   "empty_column.hashtag": "這個標籤暫時未有內容。", |  | ||||||
|   "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。", |  | ||||||
|   "empty_column.home.public_timeline": "公共時間軸", |  | ||||||
|   "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。", |  | ||||||
|   "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。", |  | ||||||
|   "empty_column.public": "跨站公共時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。", |  | ||||||
|   "follow_request.authorize": "批准", |  | ||||||
|   "follow_request.reject": "拒絕", |  | ||||||
|   "getting_started.about_addressing": "只要你知道一位用戶的用戶名稱和域名,你可以用「@用戶名稱@域名」的格式在搜尋欄尋找該用戶。", |  | ||||||
|   "getting_started.about_shortcuts": "只要該用戶是在你現在的服務站開立,你可以直接輸入用戶𠱷搜尋。同樣的規則適用於在文章提及別的用戶。", |  | ||||||
|   "getting_started.apps": "手機或桌面應用程式", |  | ||||||
|   "getting_started.heading": "開始使用", |  | ||||||
|   "getting_started.open_source_notice": "Mastodon 是一個開放源碼的軟件。你可以在官方 GitHub ({github}) 貢獻或者回報問題。你亦可透過{apps}閱讀 Mastodon 上的消息。", |  | ||||||
|   "home.column_settings.advanced": "進階", |  | ||||||
|   "home.column_settings.basic": "基本", |  | ||||||
|   "home.column_settings.filter_regex": "使用正規表達式 (regular expression) 過濾", |  | ||||||
|   "home.column_settings.show_reblogs": "顯示被轉推的文章", |  | ||||||
|   "home.column_settings.show_replies": "顯示回應文章", |  | ||||||
|   "home.settings": "欄位設定", |  | ||||||
|   "lightbox.close": "Close", |  | ||||||
|   "loading_indicator.label": "載入中...", |  | ||||||
|   "media_gallery.toggle_visible": "打開或關上", |  | ||||||
|   "missing_indicator.label": "找不到內容", |  | ||||||
|   "navigation_bar.blocks": "被封鎖的用戶", |  | ||||||
|   "navigation_bar.community_timeline": "本站時間軸", |  | ||||||
|   "navigation_bar.edit_profile": "修改個人資料", |  | ||||||
|   "navigation_bar.favourites": "喜歡的內容", |  | ||||||
|   "navigation_bar.follow_requests": "關注請求", |  | ||||||
|   "navigation_bar.info": "關於本服務站", |  | ||||||
|   "navigation_bar.logout": "登出", |  | ||||||
|   "navigation_bar.preferences": "偏好設定", |  | ||||||
|   "navigation_bar.public_timeline": "跨站公共時間軸", |  | ||||||
|   "notification.favourite": "{name} 喜歡你的文章", |  | ||||||
|   "notification.follow": "{name} 開始關注你", |  | ||||||
|   "notification.mention": "{name} 提及你", |  | ||||||
|   "notification.reblog": "{name} 轉推你的文章", |  | ||||||
|   "notifications.clear_confirmation": "你確定要清空通知紀錄嗎?", |  | ||||||
|   "notifications.clear": "清空通知紀錄", |  | ||||||
|   "notifications.column_settings.alert": "顯示桌面通知", |  | ||||||
|   "notifications.column_settings.favourite": "喜歡你的文章:", |  | ||||||
|   "notifications.column_settings.follow": "關注你:", |  | ||||||
|   "notifications.column_settings.mention": "提及你:", |  | ||||||
|   "notifications.column_settings.reblog": "轉推你的文章:", |  | ||||||
|   "notifications.column_settings.show": "在通知欄顯示", |  | ||||||
|   "notifications.column_settings.sound": "播放音效", |  | ||||||
|   "notifications.settings": "欄位設定", |  | ||||||
|   "privacy.change": "調整私隱設定", |  | ||||||
|   "privacy.direct.long": "只有提及的用戶能看到", |  | ||||||
|   "privacy.direct.short": "私人訊息", |  | ||||||
|   "privacy.private.long": "只有關注你用戶能看到", |  | ||||||
|   "privacy.private.short": "關注者", |  | ||||||
|   "privacy.public.long": "在公共時間軸顯示", |  | ||||||
|   "privacy.public.short": "公共", |  | ||||||
|   "privacy.unlisted.long": "公開,但不在公共時間軸顯示", |  | ||||||
|   "privacy.unlisted.short": "公開", |  | ||||||
|   "reply_indicator.cancel": "取消", |  | ||||||
|   "report.heading": "舉報", |  | ||||||
|   "report.placeholder": "額外訊息", |  | ||||||
|   "report.submit": "提交", |  | ||||||
|   "report.target": "Reporting", |  | ||||||
|   "search_results.total": "{count, number} 項結果", |  | ||||||
|   "search.account": "用戶", |  | ||||||
|   "search.hashtag": "標籤", |  | ||||||
|   "search.placeholder": "搜尋", |  | ||||||
|   "search.status_by": "按{name}搜尋文章", |  | ||||||
|   "status.delete": "刪除", |  | ||||||
|   "status.favourite": "喜歡", |  | ||||||
|   "status.load_more": "載入更多", |  | ||||||
|   "status.media_hidden": "隱藏媒體內容", |  | ||||||
|   "status.mention": "提及 @{name}", |  | ||||||
|   "status.open": "展開文章", |  | ||||||
|   "status.reblog": "轉推", |  | ||||||
|   "status.reblogged_by": "{name} 轉推", |  | ||||||
|   "status.reply": "回應", |  | ||||||
|   "status.report": "舉報 @{name}", |  | ||||||
|   "status.sensitive_toggle": "點擊顯示", |  | ||||||
|   "status.sensitive_warning": "敏感內容", |  | ||||||
|   "status.show_less": "減少顯示", |  | ||||||
|   "status.show_more": "顯示更多", |  | ||||||
|   "tabs_bar.compose": "撰寫", |  | ||||||
|   "tabs_bar.federated_timeline": "跨站", |  | ||||||
|   "tabs_bar.home": "主頁", |  | ||||||
|   "tabs_bar.local_timeline": "本站", |  | ||||||
|   "tabs_bar.mentions": "提及", |  | ||||||
|   "tabs_bar.notifications": "通知", |  | ||||||
|   "tabs_bar.public": "跨站公共時間軸", |  | ||||||
|   "upload_area.title": "將檔案拖放至此上載", |  | ||||||
|   "upload_button.label": "上載媒體檔案", |  | ||||||
|   "upload_form.undo": "還原", |  | ||||||
|   "upload_progress.label": "上載中……", |  | ||||||
|   "video_player.expand": "展開影片", |  | ||||||
|   "video_player.toggle_sound": "開關音效", |  | ||||||
|   "video_player.toggle_visible": "打開或關上", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default zh_hk; |  | ||||||
| @@ -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,40 +0,0 @@ | |||||||
| import emojify from './components/emoji' |  | ||||||
|  |  | ||||||
| $(() => { |  | ||||||
|   $.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); |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,10 +0,0 @@ | |||||||
| @font-face { |  | ||||||
|   font-family: '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,11 +0,0 @@ | |||||||
| @font-face { |  | ||||||
|   font-family: '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,48 +0,0 @@ | |||||||
| @font-face { |  | ||||||
| 	font-family: '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: 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: 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: 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,271 +0,0 @@ | |||||||
| code { |  | ||||||
|   font-family: 'Roboto Mono', monospace; |  | ||||||
|   font-weight: 400; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .form-container { |  | ||||||
|   max-width: 400px; |  | ||||||
|   padding: 20px; |  | ||||||
|   margin: 0 auto; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .simple_form { |  | ||||||
|   .input { |  | ||||||
|     margin-bottom: 15px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   span.hint { |  | ||||||
|     display: block; |  | ||||||
|     color: $color3; |  | ||||||
|     font-size: 12px; |  | ||||||
|     margin-top: 4px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   p.hint { |  | ||||||
|     margin-bottom: 15px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   strong { |  | ||||||
|     font-weight: 500; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .label_input { |  | ||||||
|     display: flex; |  | ||||||
|  |  | ||||||
|     label { |  | ||||||
|       flex: 0 0 auto; |  | ||||||
|       width: 100px; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     input { |  | ||||||
|       flex: 1 1 auto; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .input.file, .input.select, .input.radio_buttons { |  | ||||||
|     padding: 15px 0; |  | ||||||
|     margin-bottom: 0; |  | ||||||
|  |  | ||||||
|     label { |  | ||||||
|       font-family: inherit; |  | ||||||
|       font-size: 16px; |  | ||||||
|       color: $color5; |  | ||||||
|       display: block; |  | ||||||
|       padding-top: 5px; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .fields-group { |  | ||||||
|     margin-bottom: 25px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .input.radio_buttons .radio label { |  | ||||||
|     margin-bottom: 5px; |  | ||||||
|     font-family: inherit; |  | ||||||
|     font-size: 14px; |  | ||||||
|     color: white; |  | ||||||
|     display: block; |  | ||||||
|     width: auto; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .input.boolean { |  | ||||||
|     margin-bottom: 5px; |  | ||||||
|  |  | ||||||
|     label { |  | ||||||
|       font-family: inherit; |  | ||||||
|       font-size: 14px; |  | ||||||
|       color: white; |  | ||||||
|       display: block; |  | ||||||
|       width: auto; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     label.checkbox { |  | ||||||
|       position: relative; |  | ||||||
|       padding-left: 25px; |  | ||||||
|       flex: 1 1 auto; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     input[type=checkbox] { |  | ||||||
| 	    position: absolute; |  | ||||||
| 	    left: 0; |  | ||||||
|       top: 1px; |  | ||||||
|       margin: 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     .hint { |  | ||||||
|       padding-left: 25px; |  | ||||||
|       margin-left: 0; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   input[type=text], input[type=number], input[type=email], input[type=password], textarea { |  | ||||||
|     background: transparent; |  | ||||||
|     box-sizing: border-box; |  | ||||||
|     border: 0; |  | ||||||
|     border-bottom: 2px solid $color3; |  | ||||||
|     border-radius: 2px 2px 0 0; |  | ||||||
|     padding: 7px 4px; |  | ||||||
|     font-size: 16px; |  | ||||||
|     color: $color5; |  | ||||||
|     display: block; |  | ||||||
|     width: 100%; |  | ||||||
|     outline: 0; |  | ||||||
|     font-family: inherit; |  | ||||||
|     resize: vertical; |  | ||||||
|  |  | ||||||
|     &:invalid { |  | ||||||
|       box-shadow: none; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &:focus:invalid { |  | ||||||
|       border-bottom-color: $color6; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &:required:valid { |  | ||||||
|       border-bottom-color: $color7; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &:active, &:focus { |  | ||||||
|       border-bottom-color: $color4; |  | ||||||
|       background: rgba($color8, 0.1); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .input.field_with_errors { |  | ||||||
|     label { |  | ||||||
|       color: $color6; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     input[type=text], input[type=email], input[type=password] { |  | ||||||
|       border-bottom-color: $color6; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     .error { |  | ||||||
|       display: block; |  | ||||||
|       font-weight: 500; |  | ||||||
|       color: $color6; |  | ||||||
|       margin-top: 4px; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .actions { |  | ||||||
|     margin-top: 30px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   button, .block-button { |  | ||||||
|     display: block; |  | ||||||
|     width: 100%; |  | ||||||
|     border: 0; |  | ||||||
|     border-radius: 4px; |  | ||||||
|     background: $color4; |  | ||||||
|     color: $color5; |  | ||||||
|     font-size: 18px; |  | ||||||
|     padding: 10px; |  | ||||||
|     text-transform: uppercase; |  | ||||||
|     text-decoration: none; |  | ||||||
|     text-align: center; |  | ||||||
|     box-sizing: border-box; |  | ||||||
|     cursor: pointer; |  | ||||||
|     font-weight: 500; |  | ||||||
|     outline: 0; |  | ||||||
|     margin-bottom: 10px; |  | ||||||
|  |  | ||||||
|     &:hover { |  | ||||||
|       background-color: lighten($color4, 5%); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &:active, &:focus { |  | ||||||
|       position: relative; |  | ||||||
|       top: 1px; |  | ||||||
|       background-color: darken($color4, 5%); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &.negative { |  | ||||||
|       background: $color6; |  | ||||||
|  |  | ||||||
|       &:hover { |  | ||||||
|         background-color: lighten($color6, 5%); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       &:active, &:focus { |  | ||||||
|         background-color: darken($color6, 5%); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   select { |  | ||||||
|     font-size: 16px; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .flash-message { |  | ||||||
|   background: $color1; |  | ||||||
|   color: $color3; |  | ||||||
|   border-radius: 4px; |  | ||||||
|   padding: 15px 10px; |  | ||||||
|   margin-bottom: 30px; |  | ||||||
|   box-shadow: 0 0 5px rgba($color8, 0.2); |  | ||||||
|   text-align: center; |  | ||||||
|  |  | ||||||
|   strong { |  | ||||||
|     font-weight: 500; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .form-footer { |  | ||||||
|   margin-top: 30px; |  | ||||||
|   text-align: center; |  | ||||||
|  |  | ||||||
|   a { |  | ||||||
|     color: $color5; |  | ||||||
|     text-decoration: none; |  | ||||||
|  |  | ||||||
|     &:hover { |  | ||||||
|       text-decoration: underline; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .oauth-prompt, .follow-prompt { |  | ||||||
|   margin-bottom: 30px; |  | ||||||
|   text-align: center; |  | ||||||
|   color: $color3; |  | ||||||
|  |  | ||||||
|   h2 { |  | ||||||
|     font-size: 16px; |  | ||||||
|     margin-bottom: 30px; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   strong { |  | ||||||
|     color: $color2; |  | ||||||
|     font-weight: 500; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .qr-wrapper { |  | ||||||
|   display: flex; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .qr-code { |  | ||||||
|   flex: 0 0 auto; |  | ||||||
|   background: #fff; |  | ||||||
|   padding: 4px; |  | ||||||
|   margin-bottom: 20px; |  | ||||||
|   box-shadow: 0 0 15px rgba($color8, 0.2); |  | ||||||
|   display: inline-block; |  | ||||||
|  |  | ||||||
|   svg { |  | ||||||
|     display: block; |  | ||||||
|     margin: 0; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .qr-alternative { |  | ||||||
|   margin-left: 10px; |  | ||||||
|   color: $color3; |  | ||||||
|  |  | ||||||
|   samp { |  | ||||||
|     display: block; |  | ||||||
|     font-size: 14px; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -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).order(id: :desc).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.order(id: :desc).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 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,7 +23,11 @@ module Admin | |||||||
|         :by_domain, |         :by_domain, | ||||||
|         :silenced, |         :silenced, | ||||||
|         :recent, |         :recent, | ||||||
|         :suspended |         :suspended, | ||||||
|  |         :username, | ||||||
|  |         :display_name, | ||||||
|  |         :email, | ||||||
|  |         :ip | ||||||
|       ) |       ) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								app/controllers/admin/confirmations_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/controllers/admin/confirmations_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | module Admin | ||||||
|  |   class ConfirmationsController < BaseController | ||||||
|  |     def create | ||||||
|  |       account_user.confirm | ||||||
|  |       redirect_to admin_accounts_path | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     private | ||||||
|  |  | ||||||
|  |     def account_user | ||||||
|  |       Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) | ||||||
|  |     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,7 +3,7 @@ | |||||||
| module Admin | module Admin | ||||||
|   class PubsubhubbubController < BaseController |   class PubsubhubbubController < BaseController | ||||||
|     def index |     def index | ||||||
|       @subscriptions = Subscription.order('id desc').includes(:account).page(params[:page]) |       @subscriptions = Subscription.order(id: :desc).includes(:account).page(params[:page]) | ||||||
|     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,32 +2,43 @@ | |||||||
|  |  | ||||||
| module Admin | module Admin | ||||||
|   class SettingsController < BaseController |   class SettingsController < BaseController | ||||||
|     def index |     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 | ||||||
|  |  | ||||||
|  |     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| | ||||||
|       value    = settings_params[:value] |         setting = Setting.where(var: key).first_or_initialize(var: key) | ||||||
|  |         setting.update(value: value_for_update(key, value)) | ||||||
|       # Special cases |  | ||||||
|       value = value == 'true' if @setting.var == 'open_registrations' |  | ||||||
|  |  | ||||||
|       if @setting.value != value |  | ||||||
|         @setting.value = value |  | ||||||
|         @setting.save |  | ||||||
|       end |       end | ||||||
|  |  | ||||||
|       respond_to do |format| |       flash[:notice] = 'Success!' | ||||||
|         format.html { redirect_to admin_settings_path } |       redirect_to edit_admin_settings_path | ||||||
|         format.json { respond_with_bip(@setting) } |  | ||||||
|       end |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     private |     private | ||||||
|  |  | ||||||
|     def settings_params |     def settings_params | ||||||
|       params.require(:setting).permit(:value) |       params.permit(ADMIN_SETTINGS) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def value_for_update(key, value) | ||||||
|  |       if BOOLEAN_SETTINGS.include?(key) | ||||||
|  |         value == 'true' | ||||||
|  |       else | ||||||
|  |         value | ||||||
|  |       end | ||||||
|     end |     end | ||||||
|   end |   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 | ||||||
							
								
								
									
										25
									
								
								app/controllers/api/activitypub/activities_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/controllers/api/activitypub/activities_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | class Api::Activitypub::ActivitiesController < ApiController | ||||||
|  |   # before_action :set_follow, only: [:show_follow] | ||||||
|  |   before_action :set_status, only: [:show_status] | ||||||
|  |  | ||||||
|  |   respond_to :activitystreams2 | ||||||
|  |  | ||||||
|  |   # Show a status in AS2 format, as either an Announce (reblog) or a Create (post) activity. | ||||||
|  |   def show_status | ||||||
|  |     return forbidden unless @status.permitted? | ||||||
|  |  | ||||||
|  |     if @status.reblog? | ||||||
|  |       render :show_status_announce | ||||||
|  |     else | ||||||
|  |       render :show_status_create | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def set_status | ||||||
|  |     @status = Status.find(params[:id]) | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										17
									
								
								app/controllers/api/activitypub/notes_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/controllers/api/activitypub/notes_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | class Api::Activitypub::NotesController < ApiController | ||||||
|  |   before_action :set_status | ||||||
|  |  | ||||||
|  |   respond_to :activitystreams2 | ||||||
|  |  | ||||||
|  |   def show | ||||||
|  |     forbidden unless @status.permitted? | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def set_status | ||||||
|  |     @status = Status.find(params[:id]) | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										69
									
								
								app/controllers/api/activitypub/outbox_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								app/controllers/api/activitypub/outbox_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | class Api::Activitypub::OutboxController < ApiController | ||||||
|  |   before_action :set_account | ||||||
|  |  | ||||||
|  |   respond_to :activitystreams2 | ||||||
|  |  | ||||||
|  |   def show | ||||||
|  |     if params[:max_id] || params[:since_id] | ||||||
|  |       show_outbox_page | ||||||
|  |     else | ||||||
|  |       show_base_outbox | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def show_base_outbox | ||||||
|  |     @statuses = Status.as_outbox_timeline(@account) | ||||||
|  |     @statuses = cache_collection(@statuses) | ||||||
|  |  | ||||||
|  |     set_maps(@statuses) | ||||||
|  |  | ||||||
|  |     set_first_last_page(@statuses) | ||||||
|  |  | ||||||
|  |     render :show | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def show_outbox_page | ||||||
|  |     all_statuses = Status.as_outbox_timeline(@account) | ||||||
|  |     @statuses = all_statuses.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id]) | ||||||
|  |  | ||||||
|  |     all_statuses = cache_collection(all_statuses) | ||||||
|  |     @statuses = cache_collection(@statuses) | ||||||
|  |  | ||||||
|  |     set_maps(@statuses) | ||||||
|  |  | ||||||
|  |     set_first_last_page(all_statuses) | ||||||
|  |  | ||||||
|  |     @next_page_url = api_activitypub_outbox_url(pagination_params(max_id: @statuses.last.id))    unless @statuses.empty? | ||||||
|  |     @prev_page_url = api_activitypub_outbox_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty? | ||||||
|  |  | ||||||
|  |     @paginated = @next_page_url || @prev_page_url | ||||||
|  |     @part_of_url = api_activitypub_outbox_url | ||||||
|  |  | ||||||
|  |     set_pagination_headers(@next_page_url, @prev_page_url) | ||||||
|  |  | ||||||
|  |     render :show_page | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def cache_collection(raw) | ||||||
|  |     super(raw, Status) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def set_account | ||||||
|  |     @account = Account.find(params[:id]) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def set_first_last_page(statuses) # rubocop:disable Style/AccessorMethodName | ||||||
|  |     return if statuses.empty? | ||||||
|  |  | ||||||
|  |     @first_page_url = api_activitypub_outbox_url(max_id: statuses.first.id + 1) | ||||||
|  |     @last_page_url = api_activitypub_outbox_url(since_id: statuses.last.id - 1) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def pagination_params(core_params) | ||||||
|  |     params.permit(:local, :limit).merge(core_params) | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -6,7 +6,7 @@ class Api::OEmbedController < ApiController | |||||||
|   def show |   def show | ||||||
|     @stream_entry = stream_entry_from_url(params[:url]) |     @stream_entry = stream_entry_from_url(params[:url]) | ||||||
|     @width        = params[:maxwidth].present?  ? params[:maxwidth].to_i  : 400 |     @width        = params[:maxwidth].present?  ? params[:maxwidth].to_i  : 400 | ||||||
|     @height       = params[:maxheight].present? ? params[:maxheight].to_i : 600 |     @height       = params[:maxheight].present? ? params[:maxheight].to_i : nil | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
| @@ -14,8 +14,20 @@ class Api::OEmbedController < ApiController | |||||||
|   def stream_entry_from_url(url) |   def stream_entry_from_url(url) | ||||||
|     params = Rails.application.routes.recognize_path(url) |     params = Rails.application.routes.recognize_path(url) | ||||||
|  |  | ||||||
|     raise ActiveRecord::RecordNotFound unless params[:controller] == 'stream_entries' && params[:action] == 'show' |     raise ActiveRecord::RecordNotFound unless recognized_stream_entry_url?(params) | ||||||
|  |  | ||||||
|  |     stream_entry(params) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def recognized_stream_entry_url?(params) | ||||||
|  |     %w(stream_entries statuses).include?(params[:controller]) && params[:action] == 'show' | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def stream_entry(params) | ||||||
|  |     if params[:controller] == 'stream_entries' | ||||||
|       StreamEntry.find(params[:id]) |       StreamEntry.find(params[:id]) | ||||||
|  |     else | ||||||
|  |       Status.find(params[:id]).stream_entry | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -2,36 +2,66 @@ | |||||||
|  |  | ||||||
| class Api::PushController < ApiController | class Api::PushController < ApiController | ||||||
|   def update |   def update | ||||||
|     mode          = params['hub.mode'] |     response, status = process_push_request | ||||||
|     topic         = params['hub.topic'] |  | ||||||
|     callback      = params['hub.callback'] |  | ||||||
|     lease_seconds = params['hub.lease_seconds'] |  | ||||||
|     secret        = params['hub.secret'] |  | ||||||
|  |  | ||||||
|     case mode |  | ||||||
|     when 'subscribe' |  | ||||||
|       response, status = Pubsubhubbub::SubscribeService.new.call(topic_to_account(topic), callback, secret, lease_seconds) |  | ||||||
|     when 'unsubscribe' |  | ||||||
|       response, status = Pubsubhubbub::UnsubscribeService.new.call(topic_to_account(topic), callback) |  | ||||||
|     else |  | ||||||
|       response = "Unknown mode: #{mode}" |  | ||||||
|       status   = 422 |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     render plain: response, status: status |     render plain: response, status: status | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|   def topic_to_account(topic_url) |   def process_push_request | ||||||
|     return if topic_url.blank? |     case hub_mode | ||||||
|  |     when 'subscribe' | ||||||
|     uri    = Addressable::URI.parse(topic_url) |       Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds) | ||||||
|     params = Rails.application.routes.recognize_path(uri.path) |     when 'unsubscribe' | ||||||
|     domain = uri.host + (uri.port ? ":#{uri.port}" : '') |       Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback) | ||||||
|  |     else | ||||||
|     return unless TagManager.instance.web_domain?(domain) && params[:controller] == 'accounts' && params[:action] == 'show' && params[:format] == 'atom' |       ["Unknown mode: #{hub_mode}", 422] | ||||||
|  |     end | ||||||
|     Account.find_local(params[:username]) |   end | ||||||
|  |  | ||||||
|  |   def hub_mode | ||||||
|  |     params['hub.mode'] | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def hub_topic | ||||||
|  |     params['hub.topic'] | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def hub_callback | ||||||
|  |     params['hub.callback'] | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def hub_lease_seconds | ||||||
|  |     params['hub.lease_seconds'] | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def hub_secret | ||||||
|  |     params['hub.secret'] | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def account_from_topic | ||||||
|  |     if hub_topic.present? && local_domain? && account_feed_path? | ||||||
|  |       Account.find_local(hub_topic_params[:username]) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def hub_topic_params | ||||||
|  |     @_hub_topic_params ||= Rails.application.routes.recognize_path(hub_topic_uri.path) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def hub_topic_uri | ||||||
|  |     @_hub_topic_uri ||= Addressable::URI.parse(hub_topic).normalize | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def local_domain? | ||||||
|  |     TagManager.instance.web_domain?(hub_topic_domain) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def hub_topic_domain | ||||||
|  |     hub_topic_uri.host + (hub_topic_uri.port ? ":#{hub_topic_uri.port}" : '') | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def account_feed_path? | ||||||
|  |     hub_topic_params[:controller] == 'accounts' && hub_topic_params[:action] == 'show' && hub_topic_params[:format] == 'atom' | ||||||
|   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