Compare commits
	
		
			434 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2d97c898f2 | ||
|  | f6a93fc150 | ||
|  | 019f3377bb | ||
|  | 4b11675bdc | ||
|  | 2531c5953b | ||
|  | c6db416ff7 | ||
|  | b00cb2aed3 | ||
|  | 7c67cb5997 | ||
|  | a098d08d12 | ||
|  | 6267759607 | ||
|  | bc39ad37c4 | ||
|  | a6ba004bf5 | ||
|  | b89ab7e69d | ||
|  | 33d7338779 | ||
|  | cf4fe6cab8 | ||
|  | 2241a15ee9 | ||
|  | bca334cd28 | ||
|  | 3e3ec9b2c8 | ||
|  | a8736aab7a | ||
|  | 71b266377c | ||
|  | 08dce5e607 | ||
|  | 2469fd1cdc | ||
|  | 531c1bb245 | ||
|  | 58f5040ee8 | ||
|  | 838f51770b | ||
|  | c52090dbfe | ||
|  | 807c192fcf | ||
|  | 3b59f9c6c2 | ||
|  | 135bdd149e | ||
|  | 3572138b16 | ||
|  | 9f69aa3cb1 | ||
|  | f5c3d20e9c | ||
|  | 1ec7c87001 | ||
|  | 8e4d1cba00 | ||
|  | 676ba50601 | ||
|  | bbc3db8b20 | ||
|  | f937cad68f | ||
|  | be83d450eb | ||
|  | 1fd18a61bd | ||
|  | 5d9f479538 | ||
|  | 3ce9ca4c99 | ||
|  | 2ca1f0737a | ||
|  | 19ecde8fe7 | ||
|  | 7ee5fc5d68 | ||
|  | 4289ed1d13 | ||
|  | 256e3adc1d | ||
|  | 152b4d54e8 | ||
|  | ea2ef16ea4 | ||
|  | 1d3e0a5060 | ||
|  | bf575a1f5e | ||
|  | 860ffc0560 | ||
|  | 7eb4abe20a | ||
|  | 1baa75f79f | ||
|  | 1d436a4322 | ||
|  | 8fd174298d | ||
|  | 9afd7dadbf | ||
|  | 8e84177305 | ||
|  | a28ce13b3e | ||
|  | e1b42e9aa0 | ||
|  | b51398d0dd | ||
|  | ec34ec63b1 | ||
|  | 4a4733b397 | ||
|  | bda7391221 | ||
|  | b9e8ffbd12 | ||
|  | 7966d3a872 | ||
|  | 422e4d897b | ||
|  | cb2707776f | ||
|  | 48e7a22e34 | ||
|  | 2bb5486357 | ||
|  | 60e2b951de | ||
|  | a94c152fd3 | ||
|  | 9d04de1c8d | ||
|  | 73e4468ff3 | ||
|  | fbbd80b40b | ||
|  | 361a606edb | ||
|  | df92f010ad | ||
|  | 07af8c05fd | ||
|  | aa662cecad | ||
|  | 84608c3ff8 | ||
|  | b69365e397 | ||
|  | a478af92c3 | ||
|  | 7fba4cb3d1 | ||
|  | a4c757767f | ||
|  | 2af5cd96fe | ||
|  | 860f408475 | ||
|  | 440441ccb3 | ||
|  | 3eb13307ca | ||
|  | 756db8103a | ||
|  | 20c0054460 | ||
|  | ae78d012ac | ||
|  | ef900789bc | ||
|  | d78f555254 | ||
|  | c2f70829d9 | ||
|  | b280c387c8 | ||
|  | b75f13927e | ||
|  | 22cb286ad7 | ||
|  | 8f4b7c1820 | ||
|  | 2e112e2406 | ||
|  | 812fe90eca | ||
|  | 6c1122a1d9 | ||
|  | d3be2b582a | ||
|  | 419226d1f6 | ||
|  | f554807563 | ||
|  | d972845ff6 | ||
|  | 2c405aed55 | ||
|  | da0a18a318 | ||
|  | 8ed3fa1693 | ||
|  | 60fe9983ee | ||
|  | 724fc3cbdf | ||
|  | de475cf8d3 | ||
|  | b369fc2de4 | ||
|  | 8c5eaf7ae9 | ||
|  | 7eb8b2efad | ||
|  | b6f6152e26 | ||
|  | f8ee136c29 | ||
|  | f1ab70649b | ||
|  | 1548695c83 | ||
|  | 3da521a586 | ||
|  | d22cec81fb | ||
|  | d2e0edd721 | ||
|  | 3002a89419 | ||
|  | 17ba662004 | ||
|  | db4119f971 | ||
|  | 4a3db71692 | ||
|  | dc559d6b7a | ||
|  | 595e060347 | ||
|  | 8e4fc5d5d2 | ||
|  | b8b7b506a2 | ||
|  | 550863198c | ||
|  | 198ae3e366 | ||
|  | 6e4c7d6211 | ||
|  | d2542dcec0 | ||
|  | f18a6c2cf2 | ||
|  | 25e5aa645d | ||
|  | 620d0d8029 | ||
|  | 8ec8410651 | ||
|  | 4cc8ddabe5 | ||
|  | 07e875972a | ||
|  | 79ef8b3653 | ||
|  | b11c4326d2 | ||
|  | 390a2a8ab9 | ||
|  | cf6f67997e | ||
|  | 4d1ce3c7ad | ||
|  | 76449df903 | ||
|  | 226c9836e4 | ||
|  | 05008f3930 | ||
|  | 59ceeae8ea | ||
|  | b397f69633 | ||
|  | 5b3c7572ca | ||
|  | e89e4355eb | ||
|  | abe0d9421f | ||
|  | 7c1f3f8163 | ||
|  | eab93992d1 | ||
|  | 0d59d7c680 | ||
|  | 1efda1c453 | ||
|  | a51c8074df | ||
|  | 3722f90865 | ||
|  | 9bddb946f0 | ||
|  | bbaac89eb0 | ||
|  | 0dfffb6dcb | ||
|  | 1b0a5658f1 | ||
|  | 682b68438e | ||
|  | 09ec6e504b | ||
|  | a2a2af244c | ||
|  | cb50ecdb07 | ||
|  | 1379124682 | ||
|  | 0b34ade66b | ||
|  | 191696ab30 | ||
|  | af706583bd | ||
|  | 85c9496340 | ||
|  | 6ee3a10f17 | ||
|  | d0dd9eb5b5 | ||
|  | a588358f41 | ||
|  | 0a110d07b6 | ||
|  | 5f727f9068 | ||
|  | 72c8562cc9 | ||
|  | 882e4f5322 | ||
|  | bc1a91f4cd | ||
|  | aeb90b7c4a | ||
|  | fb87e847bc | ||
|  | 657496b5a9 | ||
|  | fd03a3d957 | ||
|  | 4bd0488a77 | ||
|  | 1b17da6ed9 | ||
|  | dc5b746f42 | ||
|  | 89210781cb | ||
|  | e9810cbad6 | ||
|  | 1027556614 | ||
|  | 3dcb5fa28f | ||
|  | 1d5dcfcd46 | ||
|  | 66ff9ed34e | ||
|  | 66328adf83 | ||
|  | b65950bb2e | ||
|  | 0d70fe2659 | ||
|  | a1fc2cfa09 | ||
|  | b535966ab5 | ||
|  | 02412429ab | ||
|  | 5abdc77c80 | ||
|  | b5a9c6b3d2 | ||
|  | 60f3230a05 | ||
|  | 0cb4b9205c | ||
|  | 43d754eb42 | ||
|  | 2cc0d56652 | ||
|  | e0c3ed29d8 | ||
|  | 2991a7cfe6 | ||
|  | 44a3584e2d | ||
|  | 831386977e | ||
|  | 68035966fb | ||
|  | 62a98a3f0e | ||
|  | 888864ad5a | ||
|  | 654f4f62ed | ||
|  | 58bcd50f7f | ||
|  | 60ecfb87ae | ||
|  | d0ef318eaa | ||
|  | 65f9db73b0 | ||
|  | a822f7a05a | ||
|  | a2c8da0185 | ||
|  | 88fd5cb688 | ||
|  | c78e8c01a3 | ||
|  | 0ec77c5b3e | ||
|  | 2d000e9c4e | ||
|  | b913746752 | ||
|  | 9cd3a6836b | ||
|  | 53c2274d48 | ||
|  | 7ff84cb07e | ||
|  | e6fbf0334f | ||
|  | 72698bc3b4 | ||
|  | 65027657ec | ||
|  | 08949cca41 | ||
|  | a231f915a0 | ||
|  | c3ef5d5414 | ||
|  | 57a3d71c90 | ||
|  | 43db2cf5e7 | ||
|  | cc9a6a710f | ||
|  | 2fba4196ef | ||
|  | fd66f7cdc0 | ||
|  | d142544159 | ||
|  | 7ac092513c | ||
|  | 2db53526c9 | ||
|  | 1f28d40c78 | ||
|  | e2491680e6 | ||
|  | 3a38322a54 | ||
|  | 29d8313b28 | ||
|  | 682507bc3c | ||
|  | 441d6dc734 | ||
|  | d5cabfe5c6 | ||
|  | af6a84da14 | ||
|  | 08e94d1b19 | ||
|  | 2fba94b36e | ||
|  | 8c9116dc98 | ||
|  | 42eb841dc2 | ||
|  | 584b45530c | ||
|  | f5cdea5122 | ||
|  | f36a791227 | ||
|  | ef226a6f22 | ||
|  | 7c249dfd88 | ||
|  | 5bea42412e | ||
|  | 04166c4a35 | ||
|  | fed585e3f4 | ||
|  | 406229d927 | ||
|  | 7a7d12d27f | ||
|  | cd830a2fab | ||
|  | aef554d553 | ||
|  | 01c4c29b3a | ||
|  | 459bbfa4b2 | ||
|  | 7140def5c9 | ||
|  | b85dec2b97 | ||
|  | cbd673601c | ||
|  | 66a3979cba | ||
|  | 9de254c46e | ||
|  | 388e70b881 | ||
|  | 8c9aff0bef | ||
|  | 48594b18e6 | ||
|  | b18504adfe | ||
|  | bba537a7be | ||
|  | 0291b73de7 | ||
|  | 28e674bc6a | ||
|  | 9d84dda213 | ||
|  | d63c291f86 | ||
|  | 6ad19036e3 | ||
|  | 3bdcf5d8f0 | ||
|  | 5c1f70b5c5 | ||
|  | c7848f54ff | ||
|  | 267ed3d74b | ||
|  | d3704fdb09 | ||
|  | ca05bfaac7 | ||
|  | e4b84c7ba5 | ||
|  | 983593ddf4 | ||
|  | f14df43435 | ||
|  | f000673599 | ||
|  | 5b6c2a1e72 | ||
|  | d372068620 | ||
|  | 139d183485 | ||
|  | d7c17c32af | ||
|  | ee1486a7de | ||
|  | b8ba719f73 | ||
|  | ada8a6cb77 | ||
|  | 6c678b7472 | ||
|  | bfbfaf9f9f | ||
|  | df81bc4a97 | ||
|  | 87588fa894 | ||
|  | 74036a2c9d | ||
|  | 05b72368ed | ||
|  | 6f71cfeff9 | ||
|  | 59ca634b89 | ||
|  | 8009366231 | ||
|  | bd71327180 | ||
|  | 67b7d3d3b6 | ||
|  | 6358a169fd | ||
|  | 99b9a0e5de | ||
|  | aa235318fc | ||
|  | a0b1951791 | ||
|  | 2d45794956 | ||
|  | 453fb84c9c | ||
|  | 59804abc3d | ||
|  | 496f466d73 | ||
|  | fa033c4d5f | ||
|  | b8e166894b | ||
|  | 1f15a15621 | ||
|  | fd1e29c3f8 | ||
|  | 553e13144f | ||
|  | 494945ff4f | ||
|  | 7c0cd2597a | ||
|  | 37caf0b36e | ||
|  | cf0b753209 | ||
|  | ddc34feb58 | ||
|  | 3f5b994ff0 | ||
|  | dacdfec973 | ||
|  | 72c30f8393 | ||
|  | 4e05751346 | ||
|  | ee3e0a93f4 | ||
|  | d1290fbd8f | ||
|  | 484c9709b6 | ||
|  | d08f1112d5 | ||
|  | bcfd9a2f8e | ||
|  | 886176f854 | ||
|  | d397d0d681 | ||
|  | 20c37ed0f9 | ||
|  | 9501a87704 | ||
|  | 8f0f4a861a | ||
|  | 8c9ea9b849 | ||
|  | 4d22d03fab | ||
|  | 81584779cb | ||
|  | 61c33652ad | ||
|  | f9d398e8fb | ||
|  | 74c8ca699c | ||
|  | eddb95b012 | ||
|  | 84eb425f38 | ||
|  | a50a87457e | ||
|  | 566e0a772d | ||
|  | 11077af52f | ||
|  | 0fc73a6e47 | ||
|  | 2bd132d458 | ||
|  | 91ddd345f2 | ||
|  | 75bd141e22 | ||
|  | 0cdcf32865 | ||
|  | 629a4d0fca | ||
|  | e95983f5df | ||
|  | e37e84d210 | ||
|  | e57e6f509d | ||
|  | bea117a4b6 | ||
|  | 908b96a370 | ||
|  | 13c16b4e95 | ||
|  | 4fcc0d5ac9 | ||
|  | 3b51581f1b | ||
|  | db92eec876 | ||
|  | 44969307c7 | ||
|  | 4babdff72f | ||
|  | c997091166 | ||
|  | 005f1fd360 | ||
|  | 8d4e7504b1 | ||
|  | aa6a26a2d5 | ||
|  | d91ba3c8d0 | ||
|  | bafd22ecf4 | ||
|  | dd9d57300b | ||
|  | 8c5ad23b24 | ||
|  | 53384b0ffe | ||
|  | 1c469ca98b | ||
|  | e61ecf4091 | ||
|  | 90c00f075a | ||
|  | 38473f0aa0 | ||
|  | 24a5d13d60 | ||
|  | 383c0b7802 | ||
|  | bf8031e984 | ||
|  | ab307b816b | ||
|  | 40562fd266 | ||
|  | 5f9cb48882 | ||
|  | 2ab7dc9a55 | ||
|  | 2b9bc9c154 | ||
|  | f5bf5ebb82 | ||
|  | 26bc591572 | ||
|  | 268dd32d76 | ||
|  | bea97ea766 | ||
|  | 03f3223d72 | ||
|  | 7880671f35 | ||
|  | b5eec34230 | ||
|  | 2128682162 | ||
|  | e68c0ce5f6 | ||
|  | 54dddfe9b8 | ||
|  | aea3aff4e4 | ||
|  | 46943b64c6 | ||
|  | 302c0d2046 | ||
|  | 22b1a70274 | ||
|  | 6f75c8451d | ||
|  | b9b78549f3 | ||
|  | 438ce5809f | ||
|  | f485fa31f3 | ||
|  | 34ae4cf511 | ||
|  | 298796cc7b | ||
|  | a4859446ab | ||
|  | 7bffd16024 | ||
|  | 2bd46f442d | ||
|  | 11b706acdf | ||
|  | 33b9e8d461 | ||
|  | f025cc6782 | ||
|  | 3988f2dade | ||
|  | 1899cf5f04 | ||
|  | 5259319cf5 | ||
|  | b83bc0ae64 | ||
|  | 282427cdd9 | ||
|  | c67d3c990b | ||
|  | 2e47fe3e1a | ||
|  | e12bb39c20 | ||
|  | 5caa727e7e | ||
|  | 0a46201a66 | ||
|  | 3f248dcaae | ||
|  | baa43e40a0 | ||
|  | a6788662b0 | ||
|  | 4a5f73c8ae | ||
|  | fdcf884cf7 | ||
|  | 964035b118 | ||
|  | 5135d609b7 | ||
|  | f48cb3eb17 | ||
|  | 8325866c61 | 
							
								
								
									
										63
									
								
								.babelrc
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								.babelrc
									
									
									
									
									
								
							| @@ -1,7 +1,62 @@ | |||||||
| { | { | ||||||
|   "presets": ["es2015", "react"], |   "presets": [ | ||||||
|   "plugins": [ |     "react", | ||||||
|     "transform-decorators-legacy", |     [ | ||||||
|     "transform-object-rest-spread" |       "env", | ||||||
|  |       { | ||||||
|  |         "loose": true, | ||||||
|  |         "modules": false, | ||||||
|  |         "targets": { | ||||||
|  |           "browsers": ["last 2 versions", "IE >= 11", "iOS >= 9"] | ||||||
|  |         } | ||||||
|  |       } | ||||||
|     ] |     ] | ||||||
|  |   ], | ||||||
|  |   "plugins": [ | ||||||
|  |     "syntax-dynamic-import", | ||||||
|  |     "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 | ||||||
|   | |||||||
							
								
								
									
										109
									
								
								.env.nanobox
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								.env.nanobox
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | |||||||
|  | # Service dependencies | ||||||
|  | # You may set REDIS_URL instead for more advanced options | ||||||
|  | REDIS_HOST=$DATA_REDIS_HOST | ||||||
|  | REDIS_PORT=6379 | ||||||
|  | # REDIS_DB=0 | ||||||
|  |  | ||||||
|  | # You may set DATABASE_URL instead for more advanced options | ||||||
|  | DB_HOST=$DATA_DB_HOST | ||||||
|  | DB_USER=$DATA_DB_USER | ||||||
|  | DB_NAME=gonano | ||||||
|  | DB_PASS=$DATA_DB_PASS | ||||||
|  | DB_PORT=5432 | ||||||
|  |  | ||||||
|  | # Federation | ||||||
|  | # Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects. | ||||||
|  | # LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com. | ||||||
|  | LOCAL_DOMAIN=${APP_NAME}.nanoapp.io | ||||||
|  | LOCAL_HTTPS=false | ||||||
|  |  | ||||||
|  | # Use this only if you need to run mastodon on a different domain than the one used for federation. | ||||||
|  | # You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md | ||||||
|  | # DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING. | ||||||
|  | # WEB_DOMAIN=mastodon.example.com | ||||||
|  |  | ||||||
|  | # Use this if you want to have several aliases handler@example1.com | ||||||
|  | # handler@example2.com etc. for the same user. LOCAL_DOMAIN should not | ||||||
|  | # be added. Comma separated values | ||||||
|  | # ALTERNATE_DOMAINS=example1.com,example2.com | ||||||
|  |  | ||||||
|  | # Application secrets | ||||||
|  | # Generate each with the `rake secret` task (`nanobox run bundle exec rake secret`) | ||||||
|  | PAPERCLIP_SECRET=$PAPERCLIP_SECRET | ||||||
|  | SECRET_KEY_BASE=$SECRET_KEY_BASE | ||||||
|  | OTP_SECRET=$OTP_SECRET | ||||||
|  |  | ||||||
|  | # Registrations | ||||||
|  | # Single user mode will disable registrations and redirect frontpage to the first profile | ||||||
|  | # SINGLE_USER_MODE=true | ||||||
|  | # Prevent registrations with following e-mail domains | ||||||
|  | # EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc | ||||||
|  | # Only allow registrations with the following e-mail domains | ||||||
|  | # EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc | ||||||
|  |  | ||||||
|  | # Optionally change default language | ||||||
|  | # DEFAULT_LOCALE=de | ||||||
|  |  | ||||||
|  | # E-mail configuration | ||||||
|  | # Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers | ||||||
|  | # If you want to use an SMTP server without authentication (e.g local Postfix relay) | ||||||
|  | # then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and | ||||||
|  | # *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough). | ||||||
|  | SMTP_SERVER=$SMTP_SERVER | ||||||
|  | SMTP_PORT=587 | ||||||
|  | SMTP_LOGIN=$SMTP_LOGIN | ||||||
|  | SMTP_PASSWORD=$SMTP_PASSWORD | ||||||
|  | SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io | ||||||
|  | #SMTP_DOMAIN= # defaults to LOCAL_DOMAIN | ||||||
|  | #SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail | ||||||
|  | #SMTP_AUTH_METHOD=plain | ||||||
|  | #SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt | ||||||
|  | #SMTP_OPENSSL_VERIFY_MODE=peer | ||||||
|  | #SMTP_ENABLE_STARTTLS_AUTO=true | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files. | ||||||
|  | # PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system | ||||||
|  | # PAPERCLIP_ROOT_URL=/system | ||||||
|  |  | ||||||
|  | # Optional asset host for multi-server setups | ||||||
|  | # CDN_HOST=assets.example.com | ||||||
|  |  | ||||||
|  | # S3 (optional) | ||||||
|  | # S3_ENABLED=true | ||||||
|  | # S3_BUCKET= | ||||||
|  | # AWS_ACCESS_KEY_ID= | ||||||
|  | # AWS_SECRET_ACCESS_KEY= | ||||||
|  | # S3_REGION= | ||||||
|  | # S3_PROTOCOL=http | ||||||
|  | # S3_HOSTNAME=192.168.1.123:9000 | ||||||
|  |  | ||||||
|  | # S3 (Minio Config (optional) Please check Minio instance for details) | ||||||
|  | # S3_ENABLED=true | ||||||
|  | # S3_BUCKET= | ||||||
|  | # AWS_ACCESS_KEY_ID= | ||||||
|  | # AWS_SECRET_ACCESS_KEY= | ||||||
|  | # S3_REGION= | ||||||
|  | # S3_PROTOCOL=https | ||||||
|  | # S3_HOSTNAME= | ||||||
|  | # S3_ENDPOINT= | ||||||
|  | # S3_SIGNATURE_VERSION= | ||||||
|  |  | ||||||
|  | # Optional alias for S3 if you want to use Cloudfront or Cloudflare in front | ||||||
|  | # S3_CLOUDFRONT_HOST= | ||||||
|  |  | ||||||
|  | # Streaming API integration | ||||||
|  | # STREAMING_API_BASE_URL= | ||||||
|  |  | ||||||
|  | # Advanced settings | ||||||
|  | # If you need to use pgBouncer, you need to disable prepared statements: | ||||||
|  | # PREPARED_STATEMENTS=false | ||||||
|  |  | ||||||
|  | # Cluster number setting for streaming API server. | ||||||
|  | # If you comment out following line, cluster number will be `numOfCpuCores - 1`. | ||||||
|  | STREAMING_CLUSTER_NUM=1 | ||||||
|  |  | ||||||
|  | # Docker mastodon user | ||||||
|  | # If you use Docker, you may want to assign UID/GID manually. | ||||||
|  | # UID=1000 | ||||||
|  | # GID=1000 | ||||||
| @@ -1,7 +1,8 @@ | |||||||
| # Service dependencies | # Service dependencies | ||||||
|  | # You may set REDIS_URL instead for more advanced options | ||||||
| REDIS_HOST=redis | REDIS_HOST=redis | ||||||
| REDIS_PORT=6379 | REDIS_PORT=6379 | ||||||
| # REDIS_DB=0 | # You may set DATABASE_URL instead for more advanced options | ||||||
| DB_HOST=db | DB_HOST=db | ||||||
| DB_USER=postgres | DB_USER=postgres | ||||||
| DB_NAME=postgres | DB_NAME=postgres | ||||||
| @@ -9,13 +10,21 @@ DB_PASS= | |||||||
| DB_PORT=5432 | DB_PORT=5432 | ||||||
|  |  | ||||||
| # Federation | # Federation | ||||||
|  | # Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects. | ||||||
|  | # LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com. | ||||||
| LOCAL_DOMAIN=example.com  | LOCAL_DOMAIN=example.com  | ||||||
| LOCAL_HTTPS=true | LOCAL_HTTPS=true | ||||||
|  |  | ||||||
| # Use this only if you need to run mastodon on a different domain than the one used for federation. | # Use this only if you need to run mastodon on a different domain than the one used for federation. | ||||||
| # Do not use this unless you know exactly what you are doing. | # You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md | ||||||
|  | # DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING. | ||||||
| # WEB_DOMAIN=mastodon.example.com | # WEB_DOMAIN=mastodon.example.com | ||||||
|  |  | ||||||
|  | # Use this if you want to have several aliases handler@example1.com | ||||||
|  | # handler@example2.com etc. for the same user. LOCAL_DOMAIN should not | ||||||
|  | # be added. Comma separated values | ||||||
|  | # ALTERNATE_DOMAINS=example1.com,example2.com | ||||||
|  |  | ||||||
| # Application secrets | # Application secrets | ||||||
| # Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose) | # Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose) | ||||||
| PAPERCLIP_SECRET= | PAPERCLIP_SECRET= | ||||||
| @@ -36,8 +45,8 @@ OTP_SECRET= | |||||||
| # E-mail configuration | # E-mail configuration | ||||||
| # Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers | # Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers | ||||||
| # If you want to use an SMTP server without authentication (e.g local Postfix relay) | # If you want to use an SMTP server without authentication (e.g local Postfix relay) | ||||||
| # then set SMTP_AUTH_METHOD to 'none' and *comment* SMTP_LOGIN and SMTP_PASSWORD. | # then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and  | ||||||
| # Leaving them blank is not enough for authentication method 'none'. | # *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough). | ||||||
| SMTP_SERVER=smtp.mailgun.org | SMTP_SERVER=smtp.mailgun.org | ||||||
| SMTP_PORT=587 | SMTP_PORT=587 | ||||||
| SMTP_LOGIN= | SMTP_LOGIN= | ||||||
| @@ -46,6 +55,7 @@ SMTP_FROM_ADDRESS=notifications@example.com | |||||||
| #SMTP_DOMAIN= # defaults to LOCAL_DOMAIN | #SMTP_DOMAIN= # defaults to LOCAL_DOMAIN | ||||||
| #SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail | #SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail | ||||||
| #SMTP_AUTH_METHOD=plain | #SMTP_AUTH_METHOD=plain | ||||||
|  | #SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt | ||||||
| #SMTP_OPENSSL_VERIFY_MODE=peer | #SMTP_OPENSSL_VERIFY_MODE=peer | ||||||
| #SMTP_ENABLE_STARTTLS_AUTO=true | #SMTP_ENABLE_STARTTLS_AUTO=true | ||||||
|  |  | ||||||
| @@ -90,3 +100,8 @@ SMTP_FROM_ADDRESS=notifications@example.com | |||||||
| # Cluster number setting for streaming API server. | # Cluster number setting for streaming API server. | ||||||
| # If you comment out following line, cluster number will be `numOfCpuCores - 1`. | # If you comment out following line, cluster number will be `numOfCpuCores - 1`. | ||||||
| STREAMING_CLUSTER_NUM=1 | STREAMING_CLUSTER_NUM=1 | ||||||
|  |  | ||||||
|  | # Docker mastodon user | ||||||
|  | # If you use Docker, you may want to assign UID/GID manually. | ||||||
|  | # UID=1000 | ||||||
|  | # GID=1000 | ||||||
|   | |||||||
| @@ -1,79 +0,0 @@ | |||||||
| { |  | ||||||
|   "env": { |  | ||||||
|     "browser": true, |  | ||||||
|     "node": false, |  | ||||||
|     "es6": true |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   "parser": "babel-eslint", |  | ||||||
|  |  | ||||||
|   "plugins": [ |  | ||||||
|     "react", |  | ||||||
|     "jsx-a11y" |  | ||||||
|   ], |  | ||||||
|  |  | ||||||
|   "parserOptions": { |  | ||||||
|     "sourceType": "module", |  | ||||||
|  |  | ||||||
|     "ecmaFeatures": { |  | ||||||
|       "arrowFunctions": true, |  | ||||||
|       "jsx": true, |  | ||||||
|       "destructuring": true, |  | ||||||
|       "modules": true, |  | ||||||
|       "spread": true |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   "rules": { |  | ||||||
|     "no-cond-assign": 2, |  | ||||||
|     "no-console": 1, |  | ||||||
|     "no-irregular-whitespace": 2, |  | ||||||
|     "no-unreachable": 2, |  | ||||||
|     "valid-typeof": 2, |  | ||||||
|     "consistent-return": 2, |  | ||||||
|     "dot-notation": 2, |  | ||||||
|     "eqeqeq": 2, |  | ||||||
|     "no-fallthrough": 2, |  | ||||||
|     "no-unused-expressions": 2, |  | ||||||
|     "strict": 0, |  | ||||||
|     "no-catch-shadow": 2, |  | ||||||
|     "indent": [1, 2], |  | ||||||
|     "brace-style": 1, |  | ||||||
|     "comma-spacing": [1, {"before": false, "after": true}], |  | ||||||
|     "comma-style": [1, "last"], |  | ||||||
|     "no-mixed-spaces-and-tabs": 1, |  | ||||||
|     "no-nested-ternary": 1, |  | ||||||
|     "no-trailing-spaces": 1, |  | ||||||
|  |  | ||||||
|     "react/jsx-wrap-multilines": 2, |  | ||||||
|     "react/self-closing-comp": 2, |  | ||||||
|     "react/prop-types": 2, |  | ||||||
|     "react/no-multi-comp": 0, |  | ||||||
|  |  | ||||||
|     "jsx-a11y/accessible-emoji": 1, |  | ||||||
|     "jsx-a11y/anchor-has-content": 1, |  | ||||||
|     "jsx-a11y/aria-activedescendant-has-tabindex": 1, |  | ||||||
|     "jsx-a11y/aria-props": 1, |  | ||||||
|     "jsx-a11y/aria-proptypes": 1, |  | ||||||
|     "jsx-a11y/aria-role": 1, |  | ||||||
|     "jsx-a11y/aria-unsupported-elements": 1, |  | ||||||
|     "jsx-a11y/heading-has-content": 1, |  | ||||||
|     "jsx-a11y/href-no-hash": 1, |  | ||||||
|     "jsx-a11y/html-has-lang": 1, |  | ||||||
|     "jsx-a11y/iframe-has-title": 1, |  | ||||||
|     "jsx-a11y/img-has-alt": 1, |  | ||||||
|     "jsx-a11y/img-redundant-alt": 1, |  | ||||||
|     "jsx-a11y/label-has-for": 1, |  | ||||||
|     "jsx-a11y/mouse-events-have-key-events": 1, |  | ||||||
|     "jsx-a11y/no-access-key": 1, |  | ||||||
|     "jsx-a11y/no-distracting-elements": 1, |  | ||||||
|     "jsx-a11y/no-onchange": 1, |  | ||||||
|     "jsx-a11y/no-redundant-roles": 1, |  | ||||||
|     "jsx-a11y/onclick-has-focus": 1, |  | ||||||
|     "jsx-a11y/onclick-has-role": 1, |  | ||||||
|     "jsx-a11y/role-has-required-aria-props": 1, |  | ||||||
|     "jsx-a11y/role-supports-aria-props": 1, |  | ||||||
|     "jsx-a11y/scope": 1, |  | ||||||
|     "jsx-a11y/tabindex-no-positive": 1 |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										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 | ||||||
							
								
								
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -19,10 +19,11 @@ | |||||||
| coverage | coverage | ||||||
| public/system | public/system | ||||||
| public/assets | public/assets | ||||||
|  | public/packs | ||||||
| .env | .env | ||||||
| .env.production | .env.production | ||||||
| node_modules/ | node_modules/ | ||||||
| neo4j/ | build/ | ||||||
|  |  | ||||||
| # Ignore Vagrant files | # Ignore Vagrant files | ||||||
| .vagrant/ | .vagrant/ | ||||||
| @@ -43,3 +44,14 @@ redis | |||||||
| # Ignore vim files | # Ignore vim files | ||||||
| *~ | *~ | ||||||
| *.swp | *.swp | ||||||
|  |  | ||||||
|  | # Ignore npm debug log | ||||||
|  | npm-debug.log | ||||||
|  |  | ||||||
|  | # Ignore yarn log files | ||||||
|  | yarn-error.log | ||||||
|  | yarn-debug.log | ||||||
|  |  | ||||||
|  | # Ignore Docker option files | ||||||
|  | docker-compose.override.yml | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										108
									
								
								.haml-lint.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								.haml-lint.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  | # Whether to ignore frontmatter at the beginning of HAML documents for | ||||||
|  | # frameworks such as Jekyll/Middleman | ||||||
|  | skip_frontmatter: false | ||||||
|  |  | ||||||
|  | exclude: | ||||||
|  |   - 'vendor/**/*' | ||||||
|  |   - 'spec/**/*' | ||||||
|  |   - 'lib/templates/**/*' | ||||||
|  |   - 'app/views/kaminari/**/*' | ||||||
|  |  | ||||||
|  | linters: | ||||||
|  |   AltText: | ||||||
|  |     enabled: false | ||||||
|  |  | ||||||
|  |   ClassAttributeWithStaticValue: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   ClassesBeforeIds: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   ConsecutiveComments: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   ConsecutiveSilentScripts: | ||||||
|  |     enabled: true | ||||||
|  |     max_consecutive: 2 | ||||||
|  |  | ||||||
|  |   EmptyObjectReference: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   EmptyScript: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   FinalNewline: | ||||||
|  |     enabled: true | ||||||
|  |     present: true | ||||||
|  |  | ||||||
|  |   HtmlAttributes: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   ImplicitDiv: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   LeadingCommentSpace: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   LineLength: | ||||||
|  |     enabled: false | ||||||
|  |     max: 80 | ||||||
|  |  | ||||||
|  |   MultilinePipe: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   MultilineScript: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   ObjectReferenceAttributes: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   RuboCop: | ||||||
|  |     enabled: true | ||||||
|  |     # These cops are incredibly noisy when it comes to HAML templates, so we | ||||||
|  |     # ignore them. | ||||||
|  |     ignored_cops: | ||||||
|  |       - Lint/BlockAlignment | ||||||
|  |       - Lint/EndAlignment | ||||||
|  |       - Lint/Void | ||||||
|  |       - Metrics/BlockLength | ||||||
|  |       - Metrics/LineLength | ||||||
|  |       - Style/AlignParameters | ||||||
|  |       - Style/BlockNesting | ||||||
|  |       - Style/ElseAlignment | ||||||
|  |       - Style/EndOfLine | ||||||
|  |       - Style/FileName | ||||||
|  |       - Style/FinalNewline | ||||||
|  |       - Style/FrozenStringLiteralComment | ||||||
|  |       - Style/IfUnlessModifier | ||||||
|  |       - Style/IndentationWidth | ||||||
|  |       - Style/Next | ||||||
|  |       - Style/TrailingBlankLines | ||||||
|  |       - Style/TrailingWhitespace | ||||||
|  |       - Style/WhileUntilModifier | ||||||
|  |  | ||||||
|  |   RubyComments: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   SpaceBeforeScript: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   SpaceInsideHashAttributes: | ||||||
|  |     enabled: true | ||||||
|  |     style: space | ||||||
|  |  | ||||||
|  |   Indentation: | ||||||
|  |     enabled: true | ||||||
|  |     character: space # or tab | ||||||
|  |  | ||||||
|  |   TagName: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   TrailingWhitespace: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   UnnecessaryInterpolation: | ||||||
|  |     enabled: true | ||||||
|  |  | ||||||
|  |   UnnecessaryStringOutput: | ||||||
|  |     enabled: true | ||||||
							
								
								
									
										20
									
								
								.nanoignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.nanoignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | .DS_Store | ||||||
|  | .git/ | ||||||
|  | .gitignore | ||||||
|  |  | ||||||
|  | .bundle/ | ||||||
|  | .cache/ | ||||||
|  | config/deploy/* | ||||||
|  | coverage | ||||||
|  | docs/ | ||||||
|  | .env | ||||||
|  | log/*.log | ||||||
|  | neo4j/ | ||||||
|  | node_modules/ | ||||||
|  | public/assets/ | ||||||
|  | public/system/ | ||||||
|  | spec/ | ||||||
|  | storybook/ | ||||||
|  | tmp/ | ||||||
|  | .vagrant/ | ||||||
|  | vendor/bundle/ | ||||||
							
								
								
									
										8
									
								
								.postcssrc.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.postcssrc.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | plugins: | ||||||
|  |   postcss-smart-import: {} | ||||||
|  |   precss: {} | ||||||
|  |   autoprefixer: | ||||||
|  |     browsers: | ||||||
|  |       - last 2 versions | ||||||
|  |       - IE >= 11 | ||||||
|  |       - iOS >= 9 | ||||||
							
								
								
									
										1
									
								
								.profile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.profile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/app/.apt/lib/x86_64-linux-gnu:/app/.apt/usr/lib/x86_64-linux-gnu/mesa:/app/.apt/usr/lib/x86_64-linux-gnu/pulseaudio | ||||||
| @@ -74,9 +74,15 @@ Style/RegexpLiteral: | |||||||
| Style/Lambda: | Style/Lambda: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
|  | Style/GuardClause: | ||||||
|  |   Enabled: false | ||||||
|  |  | ||||||
| Rails/HasAndBelongsToMany: | Rails/HasAndBelongsToMany: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
|  | Bundler/OrderedGems: | ||||||
|  |   Enabled: false | ||||||
|  |  | ||||||
| AllCops: | AllCops: | ||||||
|   TargetRubyVersion: 2.3 |   TargetRubyVersion: 2.3 | ||||||
|   Exclude: |   Exclude: | ||||||
| @@ -88,3 +94,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 | ||||||
							
								
								
									
										21
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -15,7 +15,10 @@ env: | |||||||
|     - LOCAL_DOMAIN=cb6e6126.ngrok.io |     - LOCAL_DOMAIN=cb6e6126.ngrok.io | ||||||
|     - LOCAL_HTTPS=true |     - LOCAL_HTTPS=true | ||||||
|     - RAILS_ENV=test |     - RAILS_ENV=test | ||||||
|     - CXX=g++-4.8 |     - NOKOGIRI_USE_SYSTEM_LIBRARIES=true | ||||||
|  |     - PARALLEL_TEST_PROCESSORS=2 | ||||||
|  |     - "PATH=$HOME:$PATH" | ||||||
|  |  | ||||||
| addons: | addons: | ||||||
|   postgresql: 9.4 |   postgresql: 9.4 | ||||||
|   apt: |   apt: | ||||||
| @@ -23,8 +26,10 @@ addons: | |||||||
|     - ubuntu-toolchain-r-test |     - ubuntu-toolchain-r-test | ||||||
|     - trusty-media |     - trusty-media | ||||||
|     packages: |     packages: | ||||||
|     - g++-4.8 |  | ||||||
|     - ffmpeg |     - ffmpeg | ||||||
|  |     - g++-6 | ||||||
|  |     - libprotobuf-dev | ||||||
|  |     - protobuf-compiler | ||||||
|  |  | ||||||
| rvm: | rvm: | ||||||
|   - 2.3.4 |   - 2.3.4 | ||||||
| @@ -33,18 +38,18 @@ rvm: | |||||||
| services: | services: | ||||||
|   - redis-server |   - redis-server | ||||||
|  |  | ||||||
| bundler_args: --without development production --retry=3 --jobs=3 |  | ||||||
|  |  | ||||||
| install: | install: | ||||||
|   - nvm install |   - nvm install | ||||||
|   - npm install -g yarn |   - npm install -g yarn | ||||||
|   - bundle install |   - bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16 | ||||||
|   - yarn install |   - yarn install | ||||||
|  |  | ||||||
| before_script: | before_script: | ||||||
|   - bundle exec rails db:create db:migrate |   - bundle exec rake parallel:create parallel:load_schema parallel:prepare | ||||||
|  |   - bundle exec rails assets:precompile | ||||||
|  |   - ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++" | ||||||
|  |  | ||||||
| script: | script: | ||||||
|   - bundle exec rspec |   - bundle exec parallel_test spec/ --group-by filesize --type rspec | ||||||
|   - npm test |   - npm test | ||||||
|   - i18n-tasks unused |   - bundle exec i18n-tasks unused | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								Aptfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Aptfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | protobuf-compiler | ||||||
|  | libprotobuf-dev | ||||||
|  | ffmpeg | ||||||
|  | libxdamage1 | ||||||
|  | libxfixes3 | ||||||
							
								
								
									
										2
									
								
								Capfile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Capfile
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
| require 'capistrano/setup' | require 'capistrano/setup' | ||||||
| require 'capistrano/deploy' | require 'capistrano/deploy' | ||||||
| require 'capistrano/scm/git' | require 'capistrano/scm/git' | ||||||
| @@ -8,7 +9,6 @@ require 'capistrano/rbenv' | |||||||
| require 'capistrano/bundler' | require 'capistrano/bundler' | ||||||
| require 'capistrano/yarn' | require 'capistrano/yarn' | ||||||
| require 'capistrano/rails/assets' | require 'capistrano/rails/assets' | ||||||
| require 'capistrano/faster_assets' |  | ||||||
| require 'capistrano/rails/migrations' | require 'capistrano/rails/migrations' | ||||||
|  |  | ||||||
| Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } | Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } | ||||||
|   | |||||||
							
								
								
									
										52
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -3,42 +3,52 @@ FROM ruby:2.4.1-alpine | |||||||
| LABEL maintainer="https://github.com/tootsuite/mastodon" \ | LABEL maintainer="https://github.com/tootsuite/mastodon" \ | ||||||
|       description="A GNU Social-compatible microblogging server" |       description="A GNU Social-compatible microblogging server" | ||||||
|  |  | ||||||
| ENV RAILS_ENV=production \ | ENV UID=991 GID=991 \ | ||||||
|     NODE_ENV=production |     RAILS_SERVE_STATIC_FILES=true \ | ||||||
|  |     RAILS_ENV=production NODE_ENV=production | ||||||
|  |  | ||||||
| EXPOSE 3000 4000 | EXPOSE 3000 4000 | ||||||
|  |  | ||||||
| WORKDIR /mastodon | WORKDIR /mastodon | ||||||
|  |  | ||||||
| COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/ |  | ||||||
|  |  | ||||||
| RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \ | RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \ | ||||||
|  && BUILD_DEPS=" \ |  && apk -U upgrade \ | ||||||
|     postgresql-dev \ |  && apk add -t build-dependencies \ | ||||||
|  |     build-base \ | ||||||
|     libxml2-dev \ |     libxml2-dev \ | ||||||
|     libxslt-dev \ |     libxslt-dev \ | ||||||
|  |     postgresql-dev \ | ||||||
|  |     protobuf-dev \ | ||||||
|     python \ |     python \ | ||||||
|     build-base" \ |  && apk add \ | ||||||
|  && apk -U upgrade && apk add \ |     ca-certificates \ | ||||||
|     $BUILD_DEPS \ |     ffmpeg \ | ||||||
|     nodejs@edge \ |     file \ | ||||||
|     nodejs-npm@edge \ |     git \ | ||||||
|  |     imagemagick@edge \ | ||||||
|     libpq \ |     libpq \ | ||||||
|     libxml2 \ |     libxml2 \ | ||||||
|     libxslt \ |     libxslt \ | ||||||
|     ffmpeg \ |     nodejs-npm@edge \ | ||||||
|     file \ |     nodejs@edge \ | ||||||
|     imagemagick@edge \ |     protobuf \ | ||||||
|     ca-certificates \ |     su-exec \ | ||||||
|  |     tini \ | ||||||
|  && npm install -g npm@3 && npm install -g yarn \ |  && npm install -g npm@3 && npm install -g yarn \ | ||||||
|  && bundle install --deployment --without test development \ |  | ||||||
|  && yarn --ignore-optional \ |  | ||||||
|  && yarn cache clean \ |  | ||||||
|  && npm -g cache clean \ |  | ||||||
|  && update-ca-certificates \ |  && update-ca-certificates \ | ||||||
|  && apk del $BUILD_DEPS \ |  | ||||||
|  && rm -rf /tmp/* /var/cache/apk/* |  && rm -rf /tmp/* /var/cache/apk/* | ||||||
|  |  | ||||||
|  | COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/ | ||||||
|  |  | ||||||
|  | RUN bundle install --deployment --without test development \ | ||||||
|  |  && yarn --ignore-optional --pure-lockfile | ||||||
|  |  | ||||||
| COPY . /mastodon | COPY . /mastodon | ||||||
|  |  | ||||||
| VOLUME /mastodon/public/system /mastodon/public/assets | COPY docker_entrypoint.sh /usr/local/bin/run | ||||||
|  |  | ||||||
|  | RUN chmod +x /usr/local/bin/run | ||||||
|  |  | ||||||
|  | VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs | ||||||
|  |  | ||||||
|  | ENTRYPOINT ["/usr/local/bin/run"] | ||||||
|   | |||||||
							
								
								
									
										162
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -3,103 +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 'local_time' | gem 'httplog', '~> 0.99' | ||||||
| gem 'nokogiri' | gem 'kaminari', '~> 1.0' | ||||||
| gem 'oj' | gem 'link_header', '~> 0.0' | ||||||
| gem 'ostatus2', '~> 1.1' | gem 'nokogiri', '~> 1.7' | ||||||
| gem 'ox' | gem 'oj', '~> 3.0' | ||||||
| gem 'rabl' | gem 'ostatus2', '~> 2.0' | ||||||
| gem 'rack-attack' | gem 'ox', '~> 2.5' | ||||||
| gem 'rack-cors', require: 'rack/cors' | gem 'rabl', '~> 0.13' | ||||||
| gem 'rack-timeout' | gem 'rack-attack', '~> 5.0' | ||||||
| gem 'rails-i18n' | gem 'rack-cors', '~> 0.4', require: 'rack/cors' | ||||||
| gem 'rails-settings-cached' | gem 'rack-timeout', '~> 0.4' | ||||||
| gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis'] | gem 'rails-i18n', '~> 5.0' | ||||||
| gem 'rqrcode' | gem 'rails-settings-cached', '~> 0.6' | ||||||
| gem 'ruby-oembed', require: 'oembed' | gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis'] | ||||||
| gem 'sanitize' | gem 'rqrcode', '~> 0.10' | ||||||
| gem 'sidekiq' | gem 'ruby-oembed', '~> 0.12', require: 'oembed' | ||||||
| gem 'sidekiq-unique-jobs' | gem 'sanitize', '~> 4.4' | ||||||
| gem 'simple-navigation' | gem 'sidekiq', '~> 5.0' | ||||||
| gem 'simple_form' | gem 'sidekiq-scheduler', '~> 2.1' | ||||||
| gem 'sprockets-rails', require: 'sprockets/railtie' | gem 'sidekiq-unique-jobs', '~> 5.0' | ||||||
| gem 'statsd-instrument' | gem 'simple-navigation', '~> 4.0' | ||||||
| gem 'twitter-text' | gem 'simple_form', '~> 3.4' | ||||||
| gem 'tzinfo-data' | gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' | ||||||
| gem 'whatlanguage' | gem 'statsd-instrument', '~> 2.1' | ||||||
|  | gem 'twitter-text', '~> 1.14' | ||||||
| gem 'react-rails' | gem 'tzinfo-data', '~> 1.2017' | ||||||
| gem 'browserify-rails' | gem 'webpacker', '~> 1.2' | ||||||
| gem 'autoprefixer-rails' |  | ||||||
|  |  | ||||||
| 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 | ||||||
|   | |||||||
							
								
								
									
										446
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										446
									
								
								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.2) |  | ||||||
|       execjs |  | ||||||
|     av (0.9.0) |     av (0.9.0) | ||||||
|       cocaine (~> 0.5.3) |       cocaine (~> 0.5.3) | ||||||
|     aws-sdk (2.9.12) |     aws-sdk (2.9.21) | ||||||
|       aws-sdk-resources (= 2.9.12) |       aws-sdk-resources (= 2.9.21) | ||||||
|     aws-sdk-core (2.9.12) |     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.12) |     aws-sdk-resources (2.9.21) | ||||||
|       aws-sdk-core (= 2.9.12) |       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,8 +85,6 @@ 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) | ||||||
| @@ -99,7 +93,7 @@ GEM | |||||||
|       sshkit (~> 1.3) |       sshkit (~> 1.3) | ||||||
|     capistrano-yarn (2.0.2) |     capistrano-yarn (2.0.2) | ||||||
|       capistrano (~> 3.0) |       capistrano (~> 3.0) | ||||||
|     capybara (2.13.0) |     capybara (2.14.0) | ||||||
|       addressable |       addressable | ||||||
|       mime-types (>= 1.16) |       mime-types (>= 1.16) | ||||||
|       nokogiri (>= 1.3.3) |       nokogiri (>= 1.3.3) | ||||||
| @@ -107,28 +101,23 @@ GEM | |||||||
|       rack-test (>= 0.5.4) |       rack-test (>= 0.5.4) | ||||||
|       xpath (~> 2.0) |       xpath (~> 2.0) | ||||||
|     chunky_png (1.3.8) |     chunky_png (1.3.8) | ||||||
|     climate_control (0.1.0) |     cld3 (3.1.2) | ||||||
|  |       ffi (>= 1.1.0, < 1.10.0) | ||||||
|  |     climate_control (0.2.0) | ||||||
|     cocaine (0.5.8) |     cocaine (0.5.8) | ||||||
|       climate_control (>= 0.0.3, < 1.0) |       climate_control (>= 0.0.3, < 1.0) | ||||||
|     coderay (1.1.1) |     coderay (1.1.1) | ||||||
|     coffee-rails (4.2.1) |  | ||||||
|       coffee-script (>= 2.2.0) |  | ||||||
|       railties (>= 4.0.0, < 5.2.x) |  | ||||||
|     coffee-script (2.4.1) |  | ||||||
|       coffee-script-source |  | ||||||
|       execjs |  | ||||||
|     coffee-script-source (1.12.2) |  | ||||||
|     colorize (0.8.1) |     colorize (0.8.1) | ||||||
|     concurrent-ruby (1.0.5) |     concurrent-ruby (1.0.5) | ||||||
|     connection_pool (2.2.1) |     connection_pool (2.2.1) | ||||||
|     crack (0.4.3) |     crack (0.4.3) | ||||||
|       safe_yaml (~> 1.0.0) |       safe_yaml (~> 1.0.0) | ||||||
|     crass (1.0.2) |     crass (1.0.2) | ||||||
|     debug_inspector (0.0.2) |     debug_inspector (0.0.3) | ||||||
|     devise (4.2.1) |     devise (4.3.0) | ||||||
|       bcrypt (~> 3.0) |       bcrypt (~> 3.0) | ||||||
|       orm_adapter (~> 0.1) |       orm_adapter (~> 0.1) | ||||||
|       railties (>= 4.1.0, < 5.1) |       railties (>= 4.1.0, < 5.2) | ||||||
|       responders |       responders | ||||||
|       warden (~> 1.2.3) |       warden (~> 1.2.3) | ||||||
|     devise-two-factor (3.0.0) |     devise-two-factor (3.0.0) | ||||||
| @@ -143,23 +132,24 @@ 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) | ||||||
| @@ -178,7 +168,7 @@ GEM | |||||||
|       activesupport (>= 4.0.1) |       activesupport (>= 4.0.1) | ||||||
|       hamlit (>= 1.2.0) |       hamlit (>= 1.2.0) | ||||||
|       railties (>= 4.0.1) |       railties (>= 4.0.1) | ||||||
|     hashdiff (0.3.2) |     hashdiff (0.3.4) | ||||||
|     highline (1.7.8) |     highline (1.7.8) | ||||||
|     hiredis (0.6.1) |     hiredis (0.6.1) | ||||||
|     htmlentities (4.3.4) |     htmlentities (4.3.4) | ||||||
| @@ -189,14 +179,14 @@ GEM | |||||||
|       http_parser.rb (~> 0.6.0) |       http_parser.rb (~> 0.6.0) | ||||||
|     http-cookie (1.0.3) |     http-cookie (1.0.3) | ||||||
|       domain_name (~> 0.5) |       domain_name (~> 0.5) | ||||||
|     http-form_data (1.0.1) |     http-form_data (1.0.3) | ||||||
|     http_accept_language (2.1.0) |     http_accept_language (2.1.0) | ||||||
|     http_parser.rb (0.6.0) |     http_parser.rb (0.6.0) | ||||||
|     httplog (0.99.3) |     httplog (0.99.3) | ||||||
|       colorize |       colorize | ||||||
|       rack |       rack | ||||||
|     i18n (0.8.1) |     i18n (0.8.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) | ||||||
| @@ -207,10 +197,6 @@ GEM | |||||||
|       rainbow (~> 2.2) |       rainbow (~> 2.2) | ||||||
|       terminal-table (>= 1.5.1) |       terminal-table (>= 1.5.1) | ||||||
|     jmespath (1.3.1) |     jmespath (1.3.1) | ||||||
|     jquery-rails (4.3.1) |  | ||||||
|       rails-dom-testing (>= 1, < 3) |  | ||||||
|       railties (>= 4.2.0) |  | ||||||
|       thor (>= 0.14, < 2.0) |  | ||||||
|     json (2.1.0) |     json (2.1.0) | ||||||
|     kaminari (1.0.1) |     kaminari (1.0.1) | ||||||
|       activesupport (>= 4.1.0) |       activesupport (>= 4.1.0) | ||||||
| @@ -233,19 +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) | ||||||
|     local_time (1.0.3) |     lograge (0.5.1) | ||||||
|       coffee-rails |       actionpack (>= 4, < 5.2) | ||||||
|     lograge (0.4.1) |       activesupport (>= 4, < 5.2) | ||||||
|       actionpack (>= 4, < 5.1) |       railties (>= 4, < 5.2) | ||||||
|       activesupport (>= 4, < 5.1) |  | ||||||
|       railties (>= 4, < 5.1) |  | ||||||
|     loofah (2.0.3) |     loofah (2.0.3) | ||||||
|       nokogiri (>= 1.5.9) |       nokogiri (>= 1.5.9) | ||||||
|     mail (2.6.5) |     mail (2.6.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) | ||||||
| @@ -253,24 +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) | ||||||
|     nokogumbo (1.4.10) |     nokogumbo (1.4.11) | ||||||
|       nokogiri |       nokogiri | ||||||
|     oj (3.0.2) |     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.13) |     ox (2.5.0) | ||||||
|     paperclip (5.1.0) |     paperclip (5.1.0) | ||||||
|       activemodel (>= 4.2.0) |       activemodel (>= 4.2.0) | ||||||
|       activesupport (>= 4.2.0) |       activesupport (>= 4.2.0) | ||||||
| @@ -280,10 +265,13 @@ GEM | |||||||
|     paperclip-av-transcoder (0.6.4) |     paperclip-av-transcoder (0.6.4) | ||||||
|       av (~> 0.9.0) |       av (~> 0.9.0) | ||||||
|       paperclip (>= 2.5.2) |       paperclip (>= 2.5.2) | ||||||
|  |     parallel (1.11.2) | ||||||
|  |     parallel_tests (2.14.1) | ||||||
|  |       parallel | ||||||
|     parser (2.4.0.0) |     parser (2.4.0.0) | ||||||
|       ast (~> 2.2) |       ast (~> 2.2) | ||||||
|     pg (0.20.0) |     pg (0.20.0) | ||||||
|     pghero (1.6.5) |     pghero (1.7.0) | ||||||
|       activerecord |       activerecord | ||||||
|     pkg-config (1.2.0) |     pkg-config (1.2.0) | ||||||
|     powerpack (0.1.1) |     powerpack (0.1.1) | ||||||
| @@ -297,61 +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.2) |     rainbow (2.2.2) | ||||||
|       rake |       rake | ||||||
|     rake (12.0.0) |     rake (11.3.0) | ||||||
|     react-rails (1.11.0) |  | ||||||
|       babel-transpiler (>= 0.7.0) |  | ||||||
|       connection_pool |  | ||||||
|       execjs |  | ||||||
|       railties (>= 3.2) |  | ||||||
|       tilt |  | ||||||
|     redis (3.3.3) |     redis (3.3.3) | ||||||
|     redis-actionpack (5.0.1) |     redis-actionpack (5.0.1) | ||||||
|       actionpack (>= 4.0, < 6) |       actionpack (>= 4.0, < 6) | ||||||
| @@ -360,6 +337,8 @@ GEM | |||||||
|     redis-activesupport (5.0.2) |     redis-activesupport (5.0.2) | ||||||
|       activesupport (>= 3, < 6) |       activesupport (>= 3, < 6) | ||||||
|       redis-store (~> 1.3.0) |       redis-store (~> 1.3.0) | ||||||
|  |     redis-namespace (1.5.3) | ||||||
|  |       redis (~> 3.0, >= 3.0.4) | ||||||
|     redis-rack (2.0.2) |     redis-rack (2.0.2) | ||||||
|       rack (>= 1.5, < 3) |       rack (>= 1.5, < 3) | ||||||
|       redis-store (>= 1.2, < 1.4) |       redis-store (>= 1.2, < 1.4) | ||||||
| @@ -369,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) | ||||||
| @@ -402,36 +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) | ||||||
|     sanitize (4.4.0) |     sanitize (4.4.0) | ||||||
|       crass (~> 1.0.2) |       crass (~> 1.0.2) | ||||||
|       nokogiri (>= 1.4.4) |       nokogiri (>= 1.4.4) | ||||||
|       nokogumbo (~> 1.4.1) |       nokogumbo (~> 1.4.1) | ||||||
|     sass (3.4.23) |     sass (3.4.24) | ||||||
|     sass-rails (5.0.6) |     scss_lint (0.53.0) | ||||||
|       railties (>= 4.0.0, < 6) |       rake (>= 0.9, < 13) | ||||||
|       sass (~> 3.1) |       sass (~> 3.4.20) | ||||||
|       sprockets (>= 2.8, < 4.0) |     sidekiq (5.0.0) | ||||||
|       sprockets-rails (>= 2.0, < 4.0) |  | ||||||
|       tilt (>= 1.1, < 3) |  | ||||||
|     sidekiq (4.2.10) |  | ||||||
|       concurrent-ruby (~> 1.0) |       concurrent-ruby (~> 1.0) | ||||||
|       connection_pool (~> 2.2, >= 2.2.0) |       connection_pool (~> 2.2, >= 2.2.0) | ||||||
|       rack-protection (>= 1.5.0) |       rack-protection (>= 1.5.0) | ||||||
|       redis (~> 3.2, >= 3.2.1) |       redis (~> 3.3, >= 3.3.3) | ||||||
|     sidekiq-unique-jobs (5.0.0) |     sidekiq-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) | ||||||
| @@ -445,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) | ||||||
| @@ -462,7 +446,7 @@ GEM | |||||||
|     unf (0.1.4) |     unf (0.1.4) | ||||||
|       unf_ext |       unf_ext | ||||||
|     unf_ext (0.0.7.4) |     unf_ext (0.0.7.4) | ||||||
|     unicode-display_width (1.1.3) |     unicode-display_width (1.2.1) | ||||||
|     uniform_notifier (1.10.0) |     uniform_notifier (1.10.0) | ||||||
|     warden (1.2.7) |     warden (1.2.7) | ||||||
|       rack (>= 1.0) |       rack (>= 1.0) | ||||||
| @@ -470,10 +454,13 @@ GEM | |||||||
|       addressable (>= 2.3.6) |       addressable (>= 2.3.6) | ||||||
|       crack (>= 0.3.2) |       crack (>= 0.3.2) | ||||||
|       hashdiff |       hashdiff | ||||||
|  |     webpacker (1.2) | ||||||
|  |       activesupport (>= 4.2) | ||||||
|  |       multi_json (~> 1.2) | ||||||
|  |       railties (>= 4.2) | ||||||
|     websocket-driver (0.6.5) |     websocket-driver (0.6.5) | ||||||
|       websocket-extensions (>= 0.1.0) |       websocket-extensions (>= 0.1.0) | ||||||
|     websocket-extensions (0.1.2) |     websocket-extensions (0.1.2) | ||||||
|     whatlanguage (1.0.6) |  | ||||||
|     xpath (2.0.0) |     xpath (2.0.0) | ||||||
|       nokogiri (~> 1.3) |       nokogiri (~> 1.3) | ||||||
|  |  | ||||||
| @@ -481,88 +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) | ||||||
|   local_time |   microformats2 (~> 3.0) | ||||||
|   lograge |   nokogiri (~> 1.7) | ||||||
|   microformats2 |   oj (~> 3.0) | ||||||
|   nokogiri |   ostatus2 (~> 2.0) | ||||||
|   oj |   ox (~> 2.5) | ||||||
|   ostatus2 (~> 1.1) |  | ||||||
|   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) | ||||||
|   sanitize |   sanitize (~> 4.4) | ||||||
|   sass-rails (~> 5.0) |   scss_lint (~> 0.53) | ||||||
|   sidekiq |   sidekiq (~> 5.0) | ||||||
|   sidekiq-unique-jobs |   sidekiq-scheduler (~> 2.1) | ||||||
|   simple-navigation |   sidekiq-unique-jobs (~> 5.0) | ||||||
|   simple_form |   simple-navigation (~> 4.0) | ||||||
|   simplecov |   simple_form (~> 3.4) | ||||||
|   sprockets-rails |   simplecov (~> 0.14) | ||||||
|   statsd-instrument |   sprockets-rails (~> 3.2) | ||||||
|   twitter-text |   statsd-instrument (~> 2.1) | ||||||
|   tzinfo-data |   twitter-text (~> 1.14) | ||||||
|   uglifier (>= 1.3.0) |   tzinfo-data (~> 1.2017) | ||||||
|   webmock |   uglifier (~> 3.2) | ||||||
|   whatlanguage |   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). | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,8 @@ | |||||||
| # -*- mode: ruby -*- | # -*- mode: ruby -*- | ||||||
| # vi: set ft=ruby : | # vi: set ft=ruby : | ||||||
|  |  | ||||||
|  | ENV["PORT"] ||= "3000" | ||||||
|  |  | ||||||
| $provision = <<SCRIPT | $provision = <<SCRIPT | ||||||
|  |  | ||||||
| cd /vagrant # This is where the host folder/repo is mounted | cd /vagrant # This is where the host folder/repo is mounted | ||||||
| @@ -10,10 +12,10 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - | |||||||
| sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' | sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' | ||||||
|  |  | ||||||
| # Add repo for NodeJS | # Add repo for NodeJS | ||||||
| curl -sL https://deb.nodesource.com/setup_4.x | sudo bash - | curl -sL https://deb.nodesource.com/setup_6.x | sudo bash - | ||||||
|  |  | ||||||
| # Add firewall rule to redirect 80 to 3000 and save | # Add firewall rule to redirect 80 to PORT and save | ||||||
| sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000 | sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} | ||||||
| echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections | echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections | ||||||
| echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections | echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections | ||||||
| sudo apt-get install iptables-persistent -y | sudo apt-get install iptables-persistent -y | ||||||
| @@ -31,12 +33,13 @@ sudo apt-get install \ | |||||||
|   redis-tools \ |   redis-tools \ | ||||||
|   postgresql \ |   postgresql \ | ||||||
|   postgresql-contrib \ |   postgresql-contrib \ | ||||||
|  |   protobuf-compiler \ | ||||||
|   yarn \ |   yarn \ | ||||||
|  |   libprotobuf-dev \ | ||||||
|   libreadline-dev \ |   libreadline-dev \ | ||||||
|   -y |   -y | ||||||
|  |  | ||||||
| # Install rvm | # Install rvm | ||||||
| cd /vagrant |  | ||||||
| read RUBY_VERSION < .ruby-version | read RUBY_VERSION < .ruby-version | ||||||
| gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 | gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 | ||||||
| curl -sSL https://get.rvm.io | bash -s stable --ruby=$RUBY_VERSION | curl -sSL https://get.rvm.io | bash -s stable --ruby=$RUBY_VERSION | ||||||
| @@ -47,22 +50,23 @@ sudo -u postgres createuser -U postgres vagrant -s | |||||||
| sudo -u postgres createdb -U postgres mastodon_development | sudo -u postgres createdb -U postgres mastodon_development | ||||||
|  |  | ||||||
| # Install gems and node modules | # Install gems and node modules | ||||||
| gem install bundler | gem install bundler foreman | ||||||
| bundle install | bundle install | ||||||
| yarn install | yarn install | ||||||
|  |  | ||||||
| # Build Mastodon | # Build Mastodon | ||||||
| export $(cat ".env.vagrant" | xargs) | export $(cat ".env.vagrant" | xargs) | ||||||
| bundle exec rails db:setup | bundle exec rails db:setup | ||||||
| bundle exec rails assets:precompile |  | ||||||
|  | # Configure automatic loading of environment variable | ||||||
|  | echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile | ||||||
|  |  | ||||||
| SCRIPT | SCRIPT | ||||||
|  |  | ||||||
| $start = <<SCRIPT | $start = <<SCRIPT | ||||||
|  |  | ||||||
| cd /vagrant | echo 'To start server' | ||||||
| export $(cat ".env.vagrant" | xargs) | echo '  $ vagrant ssh -c "cd /vagrant && foreman start"' | ||||||
| rails s -d -b 0.0.0.0 |  | ||||||
|  |  | ||||||
| SCRIPT | SCRIPT | ||||||
|  |  | ||||||
| @@ -74,7 +78,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| | |||||||
|  |  | ||||||
|   config.vm.provider :virtualbox do |vb| |   config.vm.provider :virtualbox do |vb| | ||||||
|     vb.name = "mastodon" |     vb.name = "mastodon" | ||||||
|     vb.customize ["modifyvm", :id, "--memory", "1024"] |     vb.customize ["modifyvm", :id, "--memory", "2048"] | ||||||
|  |  | ||||||
|     # Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions. |     # Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions. | ||||||
|     # https://github.com/mitchellh/vagrant/issues/1172 |     # https://github.com/mitchellh/vagrant/issues/1172 | ||||||
| @@ -104,8 +108,10 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| | |||||||
|     config.vm.synced_folder ".", "/vagrant" |     config.vm.synced_folder ".", "/vagrant" | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   # Otherwise, you can access the site at http://localhost:3000 |   # Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080 | ||||||
|   config.vm.network :forwarded_port, guest: 80, host: 3000 |   config.vm.network :forwarded_port, guest: 3000, host: 3000 | ||||||
|  |   config.vm.network :forwarded_port, guest: 4000, host: 4000 | ||||||
|  |   config.vm.network :forwarded_port, guest: 8080, host: 8080 | ||||||
|  |  | ||||||
|   # Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision' |   # Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision' | ||||||
|   config.vm.provision :shell, inline: $provision, privileged: false |   config.vm.provision :shell, inline: $provision, privileged: false | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								app.json
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								app.json
									
									
									
									
									
								
							| @@ -94,6 +94,9 @@ | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "buildpacks": [ |   "buildpacks": [ | ||||||
|  |     { | ||||||
|  |       "url": "https://github.com/heroku/heroku-buildpack-apt" | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       "url": "heroku/nodejs" |       "url": "heroku/nodejs" | ||||||
|     }, |     }, | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 244 KiB | 
| @@ -1,15 +0,0 @@ | |||||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files |  | ||||||
| // listed below. |  | ||||||
| // |  | ||||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, |  | ||||||
| // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. |  | ||||||
| // |  | ||||||
| // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the |  | ||||||
| // compiled file. |  | ||||||
| // |  | ||||||
| // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details |  | ||||||
| // about supported directives. |  | ||||||
| // |  | ||||||
| //= require jquery2 |  | ||||||
| //= require jquery_ujs |  | ||||||
| //= require components |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| //= require jquery2 |  | ||||||
| //= require jquery_ujs |  | ||||||
| //= require extras |  | ||||||
| //= require best_in_place |  | ||||||
| //= require local_time |  | ||||||
|  |  | ||||||
| $(function () { |  | ||||||
|   $(".best_in_place").best_in_place(); |  | ||||||
| }); |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| //= require_self |  | ||||||
| //= require react_ujs |  | ||||||
|  |  | ||||||
| window.React    = require('react'); |  | ||||||
| window.ReactDOM = require('react-dom'); |  | ||||||
| window.Perf     = require('react-addons-perf'); |  | ||||||
|  |  | ||||||
| if (!window.Intl) { |  | ||||||
|   require('intl'); |  | ||||||
|   require('intl/locale-data/jsonp/en.js'); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| //= require_tree ./components |  | ||||||
|  |  | ||||||
| window.Mastodon = require('./components/containers/mastodon'); |  | ||||||
| @@ -1,63 +0,0 @@ | |||||||
| import PropTypes from 'prop-types'; |  | ||||||
|  |  | ||||||
| class Avatar extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.state = { |  | ||||||
|       hovering: false |  | ||||||
|     }; |  | ||||||
|     this.handleMouseEnter = this.handleMouseEnter.bind(this); |  | ||||||
|     this.handleMouseLeave = this.handleMouseLeave.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleMouseEnter () { |  | ||||||
|     this.setState({ hovering: true }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleMouseLeave () { |  | ||||||
|     this.setState({ hovering: false }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { src, size, staticSrc, animate } = this.props; |  | ||||||
|     const { hovering } = this.state; |  | ||||||
|  |  | ||||||
|     const style = { |  | ||||||
|       ...this.props.style, |  | ||||||
|       width: `${size}px`, |  | ||||||
|       height: `${size}px`, |  | ||||||
|       backgroundSize: `${size}px ${size}px` |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if (hovering || animate) { |  | ||||||
|       style.backgroundImage = `url(${src})`; |  | ||||||
|     } else { |  | ||||||
|       style.backgroundImage = `url(${staticSrc})`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div |  | ||||||
|         className='account__avatar' |  | ||||||
|         onMouseEnter={this.handleMouseEnter} |  | ||||||
|         onMouseLeave={this.handleMouseLeave} |  | ||||||
|         style={style} |  | ||||||
|       /> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Avatar.propTypes = { |  | ||||||
|   src: PropTypes.string.isRequired, |  | ||||||
|   staticSrc: PropTypes.string, |  | ||||||
|   size: PropTypes.number.isRequired, |  | ||||||
|   style: PropTypes.object, |  | ||||||
|   animate: PropTypes.bool |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Avatar.defaultProps = { |  | ||||||
|   animate: false |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default Avatar; |  | ||||||
| @@ -1,49 +0,0 @@ | |||||||
| import PropTypes from 'prop-types'; |  | ||||||
|  |  | ||||||
| class Button extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.handleClick = this.handleClick.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleClick (e) { |  | ||||||
|     if (!this.props.disabled) { |  | ||||||
|       this.props.onClick(); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const style = { |  | ||||||
|       display: this.props.block ? 'block' : 'inline-block', |  | ||||||
|       width: this.props.block ? '100%' : 'auto', |  | ||||||
|       padding: `0 ${this.props.size / 2.25}px`, |  | ||||||
|       height: `${this.props.size}px`, |  | ||||||
|       lineHeight: `${this.props.size}px` |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <button className={`button ${this.props.secondary ? 'button-secondary' : ''}`} disabled={this.props.disabled} onClick={this.handleClick} style={{ ...style, ...this.props.style }}> |  | ||||||
|         {this.props.text || this.props.children} |  | ||||||
|       </button> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Button.propTypes = { |  | ||||||
|   text: PropTypes.node, |  | ||||||
|   onClick: PropTypes.func, |  | ||||||
|   disabled: PropTypes.bool, |  | ||||||
|   block: PropTypes.bool, |  | ||||||
|   secondary: PropTypes.bool, |  | ||||||
|   size: PropTypes.number, |  | ||||||
|   style: PropTypes.object, |  | ||||||
|   children: PropTypes.node |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Button.defaultProps = { |  | ||||||
|   size: 36 |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default Button; |  | ||||||
| @@ -1,56 +0,0 @@ | |||||||
| import { Motion, spring } from 'react-motion'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
|  |  | ||||||
| class ColumnCollapsable extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.state = { |  | ||||||
|       collapsed: true |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     this.handleToggleCollapsed = this.handleToggleCollapsed.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleToggleCollapsed () { |  | ||||||
|     const currentState = this.state.collapsed; |  | ||||||
|  |  | ||||||
|     this.setState({ collapsed: !currentState }); |  | ||||||
|  |  | ||||||
|     if (!currentState && this.props.onCollapse) { |  | ||||||
|       this.props.onCollapse(); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { icon, title, fullHeight, children } = this.props; |  | ||||||
|     const { collapsed } = this.state; |  | ||||||
|     const collapsedClassName = collapsed ? 'collapsable-collapsed' : 'collapsable'; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div className='column-collapsable'> |  | ||||||
|         <div role='button' tabIndex='0' title={`${title}`} className={`column-icon ${collapsedClassName}`} onClick={this.handleToggleCollapsed}> |  | ||||||
|           <i className={`fa fa-${icon}`} /> |  | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         <Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(collapsed ? 0 : 100), height: spring(collapsed ? 0 : fullHeight, collapsed ? undefined : { stiffness: 150, damping: 9 }) }}> |  | ||||||
|           {({ opacity, height }) => |  | ||||||
|             <div style={{ overflow: height === fullHeight ? 'auto' : 'hidden', height: `${height}px`, opacity: opacity / 100, maxHeight: '70vh' }}> |  | ||||||
|               {children} |  | ||||||
|             </div> |  | ||||||
|           } |  | ||||||
|         </Motion> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ColumnCollapsable.propTypes = { |  | ||||||
|   icon: PropTypes.string.isRequired, |  | ||||||
|   title: PropTypes.string, |  | ||||||
|   fullHeight: PropTypes.number.isRequired, |  | ||||||
|   children: PropTypes.node, |  | ||||||
|   onCollapse: PropTypes.func |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default ColumnCollapsable; |  | ||||||
| @@ -1,78 +0,0 @@ | |||||||
| import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
|  |  | ||||||
| class DropdownMenu extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.state = { |  | ||||||
|       direction: 'left' |  | ||||||
|     }; |  | ||||||
|     this.setRef = this.setRef.bind(this); |  | ||||||
|     this.renderItem = this.renderItem.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   setRef (c) { |  | ||||||
|     this.dropdown = c; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleClick (i, e) { |  | ||||||
|     const { action } = this.props.items[i]; |  | ||||||
|  |  | ||||||
|     if (typeof action === 'function') { |  | ||||||
|       e.preventDefault(); |  | ||||||
|       action(); |  | ||||||
|       this.dropdown.hide(); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   renderItem (item, i) { |  | ||||||
|     if (item === null) { |  | ||||||
|       return <li key={ 'sep' + i } className='dropdown__sep' />; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const { text, action, href = '#' } = item; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <li className='dropdown__content-list-item' key={ text + i }> |  | ||||||
|         <a href={href} target='_blank' rel='noopener' onClick={this.handleClick.bind(this, i)} className='dropdown__content-list-link'> |  | ||||||
|           {text} |  | ||||||
|         </a> |  | ||||||
|       </li> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { icon, items, size, direction, ariaLabel } = this.props; |  | ||||||
|     const directionClass = (direction === "left") ? "dropdown__left" : "dropdown__right"; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <Dropdown ref={this.setRef}> |  | ||||||
|         <DropdownTrigger className='icon-button' style={{ fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` }} aria-label={ariaLabel}> |  | ||||||
|           <i className={ `fa fa-fw fa-${icon} dropdown__icon` }  aria-hidden={true} /> |  | ||||||
|         </DropdownTrigger> |  | ||||||
|  |  | ||||||
|         <DropdownContent className={directionClass}> |  | ||||||
|           <ul className='dropdown__content-list'> |  | ||||||
|             {items.map(this.renderItem)} |  | ||||||
|           </ul> |  | ||||||
|         </DropdownContent> |  | ||||||
|       </Dropdown> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| DropdownMenu.propTypes = { |  | ||||||
|   icon: PropTypes.string.isRequired, |  | ||||||
|   items: PropTypes.array.isRequired, |  | ||||||
|   size: PropTypes.number.isRequired, |  | ||||||
|   direction: PropTypes.string, |  | ||||||
|   ariaLabel: PropTypes.string |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| DropdownMenu.defaultProps = { |  | ||||||
|   ariaLabel: "Menu" |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default DropdownMenu; |  | ||||||
| @@ -1,36 +0,0 @@ | |||||||
| import PropTypes from 'prop-types'; |  | ||||||
|  |  | ||||||
| class Permalink extends React.Component { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.handleClick = this.handleClick.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleClick (e) { |  | ||||||
|     if (e.button === 0) { |  | ||||||
|       e.preventDefault(); |  | ||||||
|       this.context.router.push(this.props.to); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { href, children, className, ...other } = this.props; |  | ||||||
|  |  | ||||||
|     return <a href={href} onClick={this.handleClick} {...other} className={'permalink ' + className}>{children}</a>; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Permalink.contextTypes = { |  | ||||||
|   router: PropTypes.object |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Permalink.propTypes = { |  | ||||||
|   className: PropTypes.string, |  | ||||||
|   href: PropTypes.string.isRequired, |  | ||||||
|   to: PropTypes.string.isRequired, |  | ||||||
|   children: PropTypes.node |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default Permalink; |  | ||||||
| @@ -1,19 +0,0 @@ | |||||||
| import { injectIntl, FormattedRelative } from 'react-intl'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
|  |  | ||||||
| const RelativeTimestamp = ({ intl, timestamp }) => { |  | ||||||
|   const date = new Date(timestamp); |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     <time dateTime={timestamp} title={intl.formatDate(date, { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' })}> |  | ||||||
|       <FormattedRelative value={date} /> |  | ||||||
|     </time> |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| RelativeTimestamp.propTypes = { |  | ||||||
|   intl: PropTypes.object.isRequired, |  | ||||||
|   timestamp: PropTypes.string.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default injectIntl(RelativeTimestamp); |  | ||||||
| @@ -1,121 +0,0 @@ | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import Avatar from './avatar'; |  | ||||||
| import RelativeTimestamp from './relative_timestamp'; |  | ||||||
| import DisplayName from './display_name'; |  | ||||||
| import MediaGallery from './media_gallery'; |  | ||||||
| import VideoPlayer from './video_player'; |  | ||||||
| import AttachmentList from './attachment_list'; |  | ||||||
| import StatusContent from './status_content'; |  | ||||||
| import StatusActionBar from './status_action_bar'; |  | ||||||
| import { FormattedMessage } from 'react-intl'; |  | ||||||
| import emojify from '../emoji'; |  | ||||||
| import escapeTextContentForBrowser from 'escape-html'; |  | ||||||
|  |  | ||||||
| class Status extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.handleClick = this.handleClick.bind(this); |  | ||||||
|     this.handleAccountClick = this.handleAccountClick.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleClick () { |  | ||||||
|     const { status } = this.props; |  | ||||||
|     this.context.router.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleAccountClick (id, e) { |  | ||||||
|     if (e.button === 0) { |  | ||||||
|       e.preventDefault(); |  | ||||||
|       this.context.router.push(`/accounts/${id}`); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     let media = ''; |  | ||||||
|     const { status, ...other } = this.props; |  | ||||||
|  |  | ||||||
|     if (status === null) { |  | ||||||
|       return <div />; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') { |  | ||||||
|       let displayName = status.getIn(['account', 'display_name']); |  | ||||||
|  |  | ||||||
|       if (displayName.length === 0) { |  | ||||||
|         displayName = status.getIn(['account', 'username']); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) }; |  | ||||||
|  |  | ||||||
|       return ( |  | ||||||
|         <div className='status__wrapper'> |  | ||||||
|           <div className='status__prepend'> |  | ||||||
|             <div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-retweet status__prepend-icon' /></div> |  | ||||||
|             <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name muted'><strong dangerouslySetInnerHTML={displayNameHTML} /></a> }} /> |  | ||||||
|           </div> |  | ||||||
|  |  | ||||||
|           <Status {...other} wrapped={true} status={status.get('reblog')} /> |  | ||||||
|         </div> |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (status.get('media_attachments').size > 0 && !this.props.muted) { |  | ||||||
|       if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) { |  | ||||||
|  |  | ||||||
|       } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { |  | ||||||
|         media = <VideoPlayer media={status.getIn(['media_attachments', 0])} sensitive={status.get('sensitive')} onOpenVideo={this.props.onOpenVideo} />; |  | ||||||
|       } else { |  | ||||||
|         media = <MediaGallery media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} autoPlayGif={this.props.autoPlayGif} />; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div className={this.props.muted ? 'status muted' : 'status'}> |  | ||||||
|         <div className='status__info'> |  | ||||||
|           <div className='status__info-time'> |  | ||||||
|             <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a> |  | ||||||
|           </div> |  | ||||||
|  |  | ||||||
|           <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name'> |  | ||||||
|             <div className='status__avatar'> |  | ||||||
|               <Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} /> |  | ||||||
|             </div> |  | ||||||
|  |  | ||||||
|             <DisplayName account={status.get('account')} /> |  | ||||||
|           </a> |  | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         <StatusContent status={status} onClick={this.handleClick} /> |  | ||||||
|  |  | ||||||
|         {media} |  | ||||||
|  |  | ||||||
|         <StatusActionBar {...this.props} /> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Status.contextTypes = { |  | ||||||
|   router: PropTypes.object |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Status.propTypes = { |  | ||||||
|   status: ImmutablePropTypes.map, |  | ||||||
|   wrapped: PropTypes.bool, |  | ||||||
|   onReply: PropTypes.func, |  | ||||||
|   onFavourite: PropTypes.func, |  | ||||||
|   onReblog: PropTypes.func, |  | ||||||
|   onDelete: PropTypes.func, |  | ||||||
|   onOpenMedia: PropTypes.func, |  | ||||||
|   onOpenVideo: PropTypes.func, |  | ||||||
|   onBlock: PropTypes.func, |  | ||||||
|   me: PropTypes.number, |  | ||||||
|   boostModal: PropTypes.bool, |  | ||||||
|   autoPlayGif: PropTypes.bool, |  | ||||||
|   muted: PropTypes.bool |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default Status; |  | ||||||
| @@ -1,318 +0,0 @@ | |||||||
| import { Provider } from 'react-redux'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import configureStore from '../store/configureStore'; |  | ||||||
| import { |  | ||||||
|   refreshTimelineSuccess, |  | ||||||
|   updateTimeline, |  | ||||||
|   deleteFromTimelines, |  | ||||||
|   refreshTimeline, |  | ||||||
|   connectTimeline, |  | ||||||
|   disconnectTimeline |  | ||||||
| } from '../actions/timelines'; |  | ||||||
| import { showOnboardingOnce } from '../actions/onboarding'; |  | ||||||
| import { updateNotifications, refreshNotifications } from '../actions/notifications'; |  | ||||||
| import createBrowserHistory from 'history/lib/createBrowserHistory'; |  | ||||||
| import { |  | ||||||
|   applyRouterMiddleware, |  | ||||||
|   useRouterHistory, |  | ||||||
|   Router, |  | ||||||
|   Route, |  | ||||||
|   IndexRedirect, |  | ||||||
|   IndexRoute |  | ||||||
| } from 'react-router'; |  | ||||||
| import { useScroll } from 'react-router-scroll'; |  | ||||||
| import UI from '../features/ui'; |  | ||||||
| import Status from '../features/status'; |  | ||||||
| import GettingStarted from '../features/getting_started'; |  | ||||||
| import PublicTimeline from '../features/public_timeline'; |  | ||||||
| import CommunityTimeline from '../features/community_timeline'; |  | ||||||
| import AccountTimeline from '../features/account_timeline'; |  | ||||||
| import HomeTimeline from '../features/home_timeline'; |  | ||||||
| import Compose from '../features/compose'; |  | ||||||
| import Followers from '../features/followers'; |  | ||||||
| import Following from '../features/following'; |  | ||||||
| import Reblogs from '../features/reblogs'; |  | ||||||
| import Favourites from '../features/favourites'; |  | ||||||
| import HashtagTimeline from '../features/hashtag_timeline'; |  | ||||||
| import Notifications from '../features/notifications'; |  | ||||||
| import FollowRequests from '../features/follow_requests'; |  | ||||||
| import GenericNotFound from '../features/generic_not_found'; |  | ||||||
| import FavouritedStatuses from '../features/favourited_statuses'; |  | ||||||
| import Blocks from '../features/blocks'; |  | ||||||
| import Mutes from '../features/mutes'; |  | ||||||
| import Report from '../features/report'; |  | ||||||
| import { IntlProvider, addLocaleData } from 'react-intl'; |  | ||||||
| import ar from 'react-intl/locale-data/ar'; |  | ||||||
| import en from 'react-intl/locale-data/en'; |  | ||||||
| import de from 'react-intl/locale-data/de'; |  | ||||||
| import eo from 'react-intl/locale-data/eo'; |  | ||||||
| import es from 'react-intl/locale-data/es'; |  | ||||||
| import fa from 'react-intl/locale-data/fa'; |  | ||||||
| import fi from 'react-intl/locale-data/fi'; |  | ||||||
| import fr from 'react-intl/locale-data/fr'; |  | ||||||
| import hu from 'react-intl/locale-data/hu'; |  | ||||||
| import it from 'react-intl/locale-data/it'; |  | ||||||
| import ja from 'react-intl/locale-data/ja'; |  | ||||||
| import pt from 'react-intl/locale-data/pt'; |  | ||||||
| import nl from 'react-intl/locale-data/nl'; |  | ||||||
| import no from 'react-intl/locale-data/no'; |  | ||||||
| import ru from 'react-intl/locale-data/ru'; |  | ||||||
| import uk from 'react-intl/locale-data/uk'; |  | ||||||
| import zh from 'react-intl/locale-data/zh'; |  | ||||||
| import bg from 'react-intl/locale-data/bg'; |  | ||||||
| import id from 'react-intl/locale-data/id'; |  | ||||||
| import { localeData as zh_hk } from '../locales/zh-hk'; |  | ||||||
| import { localeData as zh_cn } from '../locales/zh-cn'; |  | ||||||
| import pt_br from '../locales/pt-br'; |  | ||||||
| import getMessagesForLocale from '../locales'; |  | ||||||
| import { hydrateStore } from '../actions/store'; |  | ||||||
| import createStream from '../stream'; |  | ||||||
|  |  | ||||||
| const store = configureStore(); |  | ||||||
| const initialState = JSON.parse(document.getElementById("initial-state").textContent); |  | ||||||
| store.dispatch(hydrateStore(initialState)); |  | ||||||
|  |  | ||||||
| const browserHistory = useRouterHistory(createBrowserHistory)({ |  | ||||||
|   basename: '/web' |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| addLocaleData([ |  | ||||||
|   ...en, |  | ||||||
|   ...ar, |  | ||||||
|   ...de, |  | ||||||
|   ...eo, |  | ||||||
|   ...es, |  | ||||||
|   ...fa, |  | ||||||
|   ...fi, |  | ||||||
|   ...fr, |  | ||||||
|   ...hu, |  | ||||||
|   ...it, |  | ||||||
|   ...ja, |  | ||||||
|   ...pt, |  | ||||||
|   ...pt_br, |  | ||||||
|   ...nl, |  | ||||||
|   ...no, |  | ||||||
|   ...ru, |  | ||||||
|   ...uk, |  | ||||||
|   ...zh, |  | ||||||
|   ...zh_hk, |  | ||||||
|   ...zh_cn, |  | ||||||
|   ...bg, |  | ||||||
|   ...id, |  | ||||||
| ]); |  | ||||||
|  |  | ||||||
| const getTopWhenReplacing = (previous, { location }) => location && location.action === 'REPLACE' && [0, 0]; |  | ||||||
|  |  | ||||||
| const hiddenColumnContainerStyle = { |  | ||||||
|   position: 'absolute', |  | ||||||
|   left: '0', |  | ||||||
|   top:  '0', |  | ||||||
|   visibility: 'hidden' |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class Container extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor(props) { |  | ||||||
|     super(props); |  | ||||||
|  |  | ||||||
|     this.state = { |  | ||||||
|       renderedPersistents: [], |  | ||||||
|       unrenderedPersistents: [], |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   componentWillMount () { |  | ||||||
|     this.unlistenHistory = null; |  | ||||||
|  |  | ||||||
|     this.setState(() => { |  | ||||||
|       return { |  | ||||||
|         mountImpersistent: false, |  | ||||||
|         renderedPersistents: [], |  | ||||||
|         unrenderedPersistents: [ |  | ||||||
|           {pathname: '/timelines/home', component: HomeTimeline}, |  | ||||||
|           {pathname: '/timelines/public', component: PublicTimeline}, |  | ||||||
|           {pathname: '/timelines/public/local', component: CommunityTimeline}, |  | ||||||
|  |  | ||||||
|           {pathname: '/notifications', component: Notifications}, |  | ||||||
|           {pathname: '/favourites', component: FavouritedStatuses} |  | ||||||
|         ], |  | ||||||
|       }; |  | ||||||
|     }, () => { |  | ||||||
|       if (this.unlistenHistory) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       this.unlistenHistory = browserHistory.listen(location => { |  | ||||||
|         const pathname = location.pathname.replace(/\/$/, '').toLowerCase(); |  | ||||||
|  |  | ||||||
|         this.setState(oldState => { |  | ||||||
|           let persistentMatched = false; |  | ||||||
|  |  | ||||||
|           const newState = { |  | ||||||
|             renderedPersistents: oldState.renderedPersistents.map(persistent => { |  | ||||||
|               const givenMatched = persistent.pathname === pathname; |  | ||||||
|  |  | ||||||
|               if (givenMatched) { |  | ||||||
|                 persistentMatched = true; |  | ||||||
|               } |  | ||||||
|  |  | ||||||
|               return { |  | ||||||
|                 hidden: !givenMatched, |  | ||||||
|                 pathname: persistent.pathname, |  | ||||||
|                 component: persistent.component |  | ||||||
|               }; |  | ||||||
|             }), |  | ||||||
|           }; |  | ||||||
|  |  | ||||||
|           if (!persistentMatched) { |  | ||||||
|             newState.unrenderedPersistents = []; |  | ||||||
|  |  | ||||||
|             oldState.unrenderedPersistents.forEach(persistent => { |  | ||||||
|               if (persistent.pathname === pathname) { |  | ||||||
|                 persistentMatched = true; |  | ||||||
|  |  | ||||||
|                 newState.renderedPersistents.push({ |  | ||||||
|                   hidden: false, |  | ||||||
|                   pathname: persistent.pathname, |  | ||||||
|                   component: persistent.component |  | ||||||
|                 }); |  | ||||||
|               } else { |  | ||||||
|                 newState.unrenderedPersistents.push(persistent); |  | ||||||
|               } |  | ||||||
|             }); |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           newState.mountImpersistent = !persistentMatched; |  | ||||||
|  |  | ||||||
|           return newState; |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   componentWillUnmount () { |  | ||||||
|     if (this.unlistenHistory) { |  | ||||||
|       this.unlistenHistory(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     this.unlistenHistory = "done"; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     // Hide some components rather than unmounting them to allow to show again |  | ||||||
|     // quickly and keep the view state such as the scrolled offset. |  | ||||||
|     const persistentsView = this.state.renderedPersistents.map((persistent) => |  | ||||||
|       <div aria-hidden={persistent.hidden} key={persistent.pathname} className='mastodon-column-container' style={persistent.hidden ? hiddenColumnContainerStyle : null}> |  | ||||||
|         <persistent.component shouldUpdateScroll={persistent.hidden ? Function.prototype : getTopWhenReplacing} /> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <UI> |  | ||||||
|         {this.state.mountImpersistent && this.props.children} |  | ||||||
|         {persistentsView} |  | ||||||
|       </UI> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Container.propTypes = { |  | ||||||
|   children: PropTypes.node, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class Mastodon extends React.Component { |  | ||||||
|  |  | ||||||
|   componentDidMount() { |  | ||||||
|     const { locale }  = this.props; |  | ||||||
|     const streamingAPIBaseURL = store.getState().getIn(['meta', 'streaming_api_base_url']); |  | ||||||
|     const accessToken = store.getState().getIn(['meta', 'access_token']); |  | ||||||
|  |  | ||||||
|     this.subscription = createStream(streamingAPIBaseURL, accessToken, 'user', { |  | ||||||
|  |  | ||||||
|       connected () { |  | ||||||
|         store.dispatch(connectTimeline('home')); |  | ||||||
|       }, |  | ||||||
|  |  | ||||||
|       disconnected () { |  | ||||||
|         store.dispatch(disconnectTimeline('home')); |  | ||||||
|       }, |  | ||||||
|  |  | ||||||
|       received (data) { |  | ||||||
|         switch(data.event) { |  | ||||||
|         case 'update': |  | ||||||
|           store.dispatch(updateTimeline('home', JSON.parse(data.payload))); |  | ||||||
|           break; |  | ||||||
|         case 'delete': |  | ||||||
|           store.dispatch(deleteFromTimelines(data.payload)); |  | ||||||
|           break; |  | ||||||
|         case 'notification': |  | ||||||
|           store.dispatch(updateNotifications(JSON.parse(data.payload), getMessagesForLocale(locale), locale)); |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|  |  | ||||||
|       reconnected () { |  | ||||||
|         store.dispatch(connectTimeline('home')); |  | ||||||
|         store.dispatch(refreshTimeline('home')); |  | ||||||
|         store.dispatch(refreshNotifications()); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     // Desktop notifications |  | ||||||
|     if (typeof window.Notification !== 'undefined' && Notification.permission === 'default') { |  | ||||||
|       Notification.requestPermission(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     store.dispatch(showOnboardingOnce()); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   componentWillUnmount () { |  | ||||||
|     if (typeof this.subscription !== 'undefined') { |  | ||||||
|       this.subscription.close(); |  | ||||||
|       this.subscription = null; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { locale } = this.props; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <IntlProvider locale={locale} messages={getMessagesForLocale(locale)}> |  | ||||||
|         <Provider store={store}> |  | ||||||
|           <Router history={browserHistory} render={applyRouterMiddleware(useScroll())}> |  | ||||||
|             <Route path='/' component={Container}> |  | ||||||
|               <IndexRedirect to="/getting-started" /> |  | ||||||
|  |  | ||||||
|               <Route path='getting-started' component={GettingStarted} /> |  | ||||||
|               <Route path='timelines/tag/:id' component={HashtagTimeline} /> |  | ||||||
|  |  | ||||||
|               <Route path='statuses/new' component={Compose} /> |  | ||||||
|               <Route path='statuses/:statusId' component={Status} /> |  | ||||||
|               <Route path='statuses/:statusId/reblogs' component={Reblogs} /> |  | ||||||
|               <Route path='statuses/:statusId/favourites' component={Favourites} /> |  | ||||||
|  |  | ||||||
|               <Route path='accounts/:accountId' component={AccountTimeline} /> |  | ||||||
|               <Route path='accounts/:accountId/followers' component={Followers} /> |  | ||||||
|               <Route path='accounts/:accountId/following' component={Following} /> |  | ||||||
|  |  | ||||||
|               <Route path='follow_requests' component={FollowRequests} /> |  | ||||||
|               <Route path='blocks' component={Blocks} /> |  | ||||||
|               <Route path='mutes' component={Mutes} /> |  | ||||||
|               <Route path='report' component={Report} /> |  | ||||||
|  |  | ||||||
|               <Route path='*' component={GenericNotFound} /> |  | ||||||
|             </Route> |  | ||||||
|           </Router> |  | ||||||
|         </Provider> |  | ||||||
|       </IntlProvider> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Mastodon.propTypes = { |  | ||||||
|   locale: PropTypes.string.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default Mastodon; |  | ||||||
| @@ -1,81 +0,0 @@ | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import InnerHeader from '../../account/components/header'; |  | ||||||
| import ActionBar from '../../account/components/action_bar'; |  | ||||||
| import MissingIndicator from '../../../components/missing_indicator'; |  | ||||||
|  |  | ||||||
| class Header extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.handleFollow = this.handleFollow.bind(this); |  | ||||||
|     this.handleBlock = this.handleBlock.bind(this); |  | ||||||
|     this.handleMention = this.handleMention.bind(this); |  | ||||||
|     this.handleReport = this.handleReport.bind(this); |  | ||||||
|     this.handleMute = this.handleMute.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleFollow () { |  | ||||||
|     this.props.onFollow(this.props.account); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleBlock () { |  | ||||||
|     this.props.onBlock(this.props.account); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleMention () { |  | ||||||
|     this.props.onMention(this.props.account, this.context.router); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleReport () { |  | ||||||
|     this.props.onReport(this.props.account); |  | ||||||
|     this.context.router.push('/report'); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleMute() { |  | ||||||
|     this.props.onMute(this.props.account); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { account, me } = this.props; |  | ||||||
|  |  | ||||||
|     if (account === null) { |  | ||||||
|       return <MissingIndicator />; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div className='account-timeline__header'> |  | ||||||
|         <InnerHeader |  | ||||||
|           account={account} |  | ||||||
|           me={me} |  | ||||||
|           onFollow={this.handleFollow} |  | ||||||
|         /> |  | ||||||
|  |  | ||||||
|         <ActionBar |  | ||||||
|           account={account} |  | ||||||
|           me={me} |  | ||||||
|           onBlock={this.handleBlock} |  | ||||||
|           onMention={this.handleMention} |  | ||||||
|           onReport={this.handleReport} |  | ||||||
|           onMute={this.handleMute} |  | ||||||
|         /> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Header.propTypes = { |  | ||||||
|   account: ImmutablePropTypes.map, |  | ||||||
|   me: PropTypes.number.isRequired, |  | ||||||
|   onFollow: PropTypes.func.isRequired, |  | ||||||
|   onBlock: PropTypes.func.isRequired, |  | ||||||
|   onMention: PropTypes.func.isRequired, |  | ||||||
|   onReport: PropTypes.func.isRequired, |  | ||||||
|   onMute: PropTypes.func.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Header.contextTypes = { |  | ||||||
|   router: PropTypes.object |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default Header; |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| import Avatar from '../../../components/avatar'; |  | ||||||
| import DisplayName from '../../../components/display_name'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
|  |  | ||||||
| const AutosuggestAccount = ({ account }) => ( |  | ||||||
|   <div className='autosuggest-account'> |  | ||||||
|     <div className='autosuggest-account-icon'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={18} /></div> |  | ||||||
|     <DisplayName account={account} /> |  | ||||||
|   </div> |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| AutosuggestAccount.propTypes = { |  | ||||||
|   account: ImmutablePropTypes.map.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default AutosuggestAccount; |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| import { FormattedMessage } from 'react-intl'; |  | ||||||
| import DisplayName from '../../../components/display_name'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
|  |  | ||||||
| const AutosuggestStatus = ({ status }) => ( |  | ||||||
|   <div className='autosuggest-status'> |  | ||||||
|     <FormattedMessage id='search.status_by' defaultMessage='Status by {name}' values={{ name: <strong>@{status.getIn(['account', 'acct'])}</strong> }} /> |  | ||||||
|   </div> |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| AutosuggestStatus.propTypes = { |  | ||||||
|   status: ImmutablePropTypes.map.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default AutosuggestStatus; |  | ||||||
| @@ -1,60 +0,0 @@ | |||||||
| import IconButton from '../../../components/icon_button'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   upload: { id: 'upload_button.label', defaultMessage: 'Add media' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| const iconStyle = { |  | ||||||
|   height: null, |  | ||||||
|   lineHeight: '27px' |  | ||||||
| } |  | ||||||
|  |  | ||||||
| class UploadButton extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.handleChange = this.handleChange.bind(this); |  | ||||||
|     this.handleClick = this.handleClick.bind(this); |  | ||||||
|     this.setRef = this.setRef.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleChange (e) { |  | ||||||
|     if (e.target.files.length > 0) { |  | ||||||
|       this.props.onSelectFile(e.target.files); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleClick () { |  | ||||||
|     this.fileElement.click(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   setRef (c) { |  | ||||||
|     this.fileElement = c; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|  |  | ||||||
|     const { intl, resetFileKey, disabled } = this.props; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div className='compose-form__upload-button'> |  | ||||||
|         <IconButton icon='camera' title={intl.formatMessage(messages.upload)} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle}/> |  | ||||||
|         <input key={resetFileKey} ref={this.setRef} type='file' multiple={false} onChange={this.handleChange} disabled={disabled} style={{ display: 'none' }} /> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| UploadButton.propTypes = { |  | ||||||
|   disabled: PropTypes.bool, |  | ||||||
|   onSelectFile: PropTypes.func.isRequired, |  | ||||||
|   style: PropTypes.object, |  | ||||||
|   resetFileKey: PropTypes.number, |  | ||||||
|   intl: PropTypes.object.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default injectIntl(UploadButton); |  | ||||||
| @@ -1,44 +0,0 @@ | |||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import Permalink from '../../../components/permalink'; |  | ||||||
| import Avatar from '../../../components/avatar'; |  | ||||||
| import DisplayName from '../../../components/display_name'; |  | ||||||
| import emojify from '../../../emoji'; |  | ||||||
| import IconButton from '../../../components/icon_button'; |  | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' }, |  | ||||||
|   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => { |  | ||||||
|   const content = { __html: emojify(account.get('note')) }; |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     <div className='account-authorize__wrapper'> |  | ||||||
|       <div className='account-authorize'> |  | ||||||
|         <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'> |  | ||||||
|           <div className='account-authorize__avatar'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div> |  | ||||||
|           <DisplayName account={account} /> |  | ||||||
|         </Permalink> |  | ||||||
|  |  | ||||||
|         <div className='account__header__content' dangerouslySetInnerHTML={content} /> |  | ||||||
|       </div> |  | ||||||
|  |  | ||||||
|       <div className='account--panel'> |  | ||||||
|         <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div> |  | ||||||
|         <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div> |  | ||||||
|       </div> |  | ||||||
|     </div> |  | ||||||
|   ) |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| AccountAuthorize.propTypes = { |  | ||||||
|   account: ImmutablePropTypes.map.isRequired, |  | ||||||
|   onAuthorize: PropTypes.func.isRequired, |  | ||||||
|   onReject: PropTypes.func.isRequired, |  | ||||||
|   intl: PropTypes.object.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default injectIntl(AccountAuthorize); |  | ||||||
| @@ -1,66 +0,0 @@ | |||||||
| import Column from '../ui/components/column'; |  | ||||||
| import ColumnLink from '../ui/components/column_link'; |  | ||||||
| import ColumnSubheading from '../ui/components/column_subheading'; |  | ||||||
| import { Link } from 'react-router'; |  | ||||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; |  | ||||||
| import { connect } from 'react-redux'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, |  | ||||||
|   public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' }, |  | ||||||
|   navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation'}, |  | ||||||
|   settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings'}, |  | ||||||
|   community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' }, |  | ||||||
|   preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, |  | ||||||
|   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, |  | ||||||
|   sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, |  | ||||||
|   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, |  | ||||||
|   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, |  | ||||||
|   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, |  | ||||||
|   info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const mapStateToProps = state => ({ |  | ||||||
|   me: state.getIn(['accounts', state.getIn(['meta', 'me'])]) |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const GettingStarted = ({ intl, me }) => { |  | ||||||
|   let followRequests = ''; |  | ||||||
|  |  | ||||||
|   if (me.get('locked')) { |  | ||||||
|     followRequests = <ColumnLink icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     <Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile={true}> |  | ||||||
|       <div className='getting-started__wrapper'> |  | ||||||
|         <ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)}/> |  | ||||||
|         <ColumnLink icon='users' hideOnMobile={true} text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' /> |  | ||||||
|         <ColumnLink icon='globe' hideOnMobile={true} text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' /> |  | ||||||
|         <ColumnLink icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' /> |  | ||||||
|         {followRequests} |  | ||||||
|         <ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' /> |  | ||||||
|         <ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' /> |  | ||||||
|         <ColumnSubheading text={intl.formatMessage(messages.settings_subheading)}/> |  | ||||||
|         <ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' /> |  | ||||||
|         <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' /> |  | ||||||
|         <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' /> |  | ||||||
|       </div> |  | ||||||
|  |  | ||||||
|       <div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}> |  | ||||||
|         <div className='static-content getting-started'> |  | ||||||
|           <p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </Column> |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| GettingStarted.propTypes = { |  | ||||||
|   intl: PropTypes.object.isRequired, |  | ||||||
|   me: ImmutablePropTypes.map.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default connect(mapStateToProps)(injectIntl(GettingStarted)); |  | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| import { connect } from 'react-redux'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import StatusListContainer from '../ui/containers/status_list_container'; |  | ||||||
| import Column from '../ui/components/column'; |  | ||||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; |  | ||||||
| import ColumnSettingsContainer from './containers/column_settings_container'; |  | ||||||
| import { Link } from 'react-router'; |  | ||||||
|  |  | ||||||
| const messages = defineMessages({ |  | ||||||
|   title: { id: 'column.home', defaultMessage: 'Home' } |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const mapStateToProps = state => ({ |  | ||||||
|   hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0 |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| class HomeTimeline extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { intl, hasUnread } = this.props; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <Column icon='home' active={hasUnread} heading={intl.formatMessage(messages.title)}> |  | ||||||
|         <ColumnSettingsContainer /> |  | ||||||
|         <StatusListContainer {...this.props} scrollKey='home_timeline' type='home' emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage="You aren't following anyone yet. Visit {public} or use search to get started and meet other users." values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />} /> |  | ||||||
|       </Column> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| HomeTimeline.propTypes = { |  | ||||||
|   intl: PropTypes.object.isRequired, |  | ||||||
|   hasUnread: PropTypes.bool |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default connect(mapStateToProps)(injectIntl(HomeTimeline)); |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; |  | ||||||
| import Toggle from 'react-toggle'; |  | ||||||
|  |  | ||||||
| const SettingToggle = ({ settings, settingKey, label, onChange, htmlFor = '' }) => ( |  | ||||||
|   <label htmlFor={htmlFor} className='setting-toggle__label'> |  | ||||||
|     <Toggle checked={settings.getIn(settingKey)} onChange={(e) => onChange(settingKey, e.target.checked)} /> |  | ||||||
|     <span className='setting-toggle'>{label}</span> |  | ||||||
|   </label> |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| SettingToggle.propTypes = { |  | ||||||
|   settings: ImmutablePropTypes.map.isRequired, |  | ||||||
|   settingKey: PropTypes.array.isRequired, |  | ||||||
|   label: PropTypes.node.isRequired, |  | ||||||
|   onChange: PropTypes.func.isRequired, |  | ||||||
|   htmlFor: PropTypes.string |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default SettingToggle; |  | ||||||
| @@ -1,50 +0,0 @@ | |||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; |  | ||||||
| import Button from '../../../components/button'; |  | ||||||
|  |  | ||||||
| class ConfirmationModal extends React.PureComponent { |  | ||||||
|  |  | ||||||
|   constructor (props, context) { |  | ||||||
|     super(props, context); |  | ||||||
|     this.handleClick = this.handleClick.bind(this); |  | ||||||
|     this.handleCancel = this.handleCancel.bind(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleClick () { |  | ||||||
|     this.props.onClose(); |  | ||||||
|     this.props.onConfirm(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   handleCancel (e) { |  | ||||||
|     e.preventDefault(); |  | ||||||
|     this.props.onClose(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   render () { |  | ||||||
|     const { intl, message, confirm, onConfirm, onClose } = this.props; |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|       <div className='modal-root__modal confirmation-modal'> |  | ||||||
|         <div className='confirmation-modal__container'> |  | ||||||
|           {message} |  | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         <div className='confirmation-modal__action-bar'> |  | ||||||
|           <div><a href='#' onClick={this.handleCancel}><FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' /></a></div> |  | ||||||
|           <Button text={confirm} onClick={this.handleClick} /> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ConfirmationModal.propTypes = { |  | ||||||
|   message: PropTypes.node.isRequired, |  | ||||||
|   confirm: PropTypes.string.isRequired, |  | ||||||
|   onClose: PropTypes.func.isRequired, |  | ||||||
|   onConfirm: PropTypes.func.isRequired, |  | ||||||
|   intl: PropTypes.object.isRequired |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default injectIntl(ConfirmationModal); |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| import Link from 'http-link-header'; |  | ||||||
| import querystring from 'querystring'; |  | ||||||
|  |  | ||||||
| Link.parseAttrs = (link, parts) => { |  | ||||||
|   let match = null |  | ||||||
|   let attr  = '' |  | ||||||
|   let value = '' |  | ||||||
|   let attrs = '' |  | ||||||
|  |  | ||||||
|   let uriAttrs = /<(.*)>;\s*(.*)/gi.exec(parts) |  | ||||||
|  |  | ||||||
|   if(uriAttrs) { |  | ||||||
|     attrs = uriAttrs[2] |  | ||||||
|     link  = Link.parseParams(link, uriAttrs[1]) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   while(match = Link.attrPattern.exec(attrs)) { // eslint-disable-line no-cond-assign |  | ||||||
|     attr  = match[1].toLowerCase() |  | ||||||
|     value = match[4] || match[3] || match[2] |  | ||||||
|  |  | ||||||
|     if( /\*$/.test(attr)) { |  | ||||||
|       Link.setAttr(link, attr, Link.parseExtendedValue(value)) |  | ||||||
|     } else if(/%/.test(value)) { |  | ||||||
|       Link.setAttr(link, attr, querystring.decode(value)) |  | ||||||
|     } else { |  | ||||||
|       Link.setAttr(link, attr, value) |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return link |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default Link; |  | ||||||
| @@ -1,120 +0,0 @@ | |||||||
| /** |  | ||||||
|   * ملاحظة للمساهمين و المساهمات : |  | ||||||
|   * لجعل مهمة المساهمين الآخرين أسهل، رجاءا تذكر : |  | ||||||
|   * 1. إضافة سلسلة جديدة هنا؛ و |  | ||||||
|   * 2. لإزالة السلاسل القديمة التي لم تعد هناك حاجة إليها. و |  | ||||||
|   * 3. لفرز السلاسل تبعا للأبجدية |  | ||||||
|   * شكر! |  | ||||||
|  */ |  | ||||||
| const ar = { |  | ||||||
|   "account.block": "حظر @{name}", |  | ||||||
|   "account.disclaimer": "هذا المستخدم من مثيل خادم آخر. قد يكون هذا الرقم أكبر.", |  | ||||||
|   "account.edit_profile": "تعديل الملف الشخصي", |  | ||||||
|   "account.follow": "إتبع", |  | ||||||
|   "account.followers": "المتابعون", |  | ||||||
|   "account.follows_you": "يتابعك", |  | ||||||
|   "account.follows": "يتبع", |  | ||||||
|   "account.mention": "أُذكُر @{name}", |  | ||||||
|   "account.mute": "أكتم @{name}", |  | ||||||
|   "account.posts": "المشاركات", |  | ||||||
|   "account.report": "أبلغ عن @{name}", |  | ||||||
|   "account.requested": "في انتظار الموافقة", |  | ||||||
|   "account.unblock": "إلغاء الحظر عن @{name}", |  | ||||||
|   "account.unfollow": "إلغاء المتابعة", |  | ||||||
|   "account.unmute": "إلغاء الكتم عن @{name}", |  | ||||||
|   "boost_modal.combo": "يمكنك الضغط على {combo} لتخطي هذا مرة أخرى", |  | ||||||
|   "column_back_button.label": "العودة", |  | ||||||
|   "column.blocks": "الحسابات المحجوبة", |  | ||||||
|   "column.community": "الخيط العام المحلي", |  | ||||||
|   "column.favourites": "المفضلة", |  | ||||||
|   "column.follow_requests": "طلبات المتابعة", |  | ||||||
|   "column.home": "الرئيسية", |  | ||||||
|   "column.mutes": "الحسابات المكتومة", |  | ||||||
|   "column.notifications": "الإشعارات", |  | ||||||
|   "column.public": "الخيط العام الموحد", |  | ||||||
|   "compose_form.placeholder": "ماذا يدور في ذهنك ؟", |  | ||||||
|   "compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.", |  | ||||||
|   "compose_form.publish": "بَوِّق", |  | ||||||
|   "compose_form.sensitive": "ضع علامة حساس على الوسائط", |  | ||||||
|   "compose_form.spoiler_placeholder": "تنبيه عن المحتوى", |  | ||||||
|   "compose_form.spoiler": "إخفاء النص وراء التحذير", |  | ||||||
|   "emoji_button.label": "إيموجي", |  | ||||||
|   "emoji_button.search": "بحث ...", |  | ||||||
|   "emoji_button.people": "أشخاص", |  | ||||||
|   "emoji_button.nature": "طبيعة", |  | ||||||
|   "emoji_button.food": "أكل و شرب", |  | ||||||
|   "emoji_button.activity": "أنشطة", |  | ||||||
|   "emoji_button.travel": "أماكن و أسفار", |  | ||||||
|   "emoji_button.objects": "أشياء", |  | ||||||
|   "emoji_button.symbols": "رموز", |  | ||||||
|   "emoji_button.flags": "أعلام", |  | ||||||
|   "empty_column.community": "الخيط العام المحلي فارغ. قم بتحرير شيء ما كبداية.", |  | ||||||
|   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.", |  | ||||||
|   "empty_column.home.public_timeline": "الخيط العام", |  | ||||||
|   "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.", |  | ||||||
|   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.", |  | ||||||
|   "empty_column.public": "لا يوجد شيء هنا ! قم بتحرير شيء ما بشكل عام، أو اتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام.", |  | ||||||
|   "follow_request.authorize": "ترخيص", |  | ||||||
|   "follow_request.reject": "رفض", |  | ||||||
|   "getting_started.apps": "عدة تطبيقات مختلفة متوفرة", |  | ||||||
|   "getting_started.heading": "إستعدّ للبدء", |  | ||||||
|   "getting_started.about_addressing": "يمكنك متابعة الأشخاص إذا كنت تعرف اسم المستخدم الخاص بهم والنطاق الذي هم عليه عن طريق إدخال عنوان شبيه بالبريد الإلكتروني في الحقل المخصص للبحث.", |  | ||||||
|   "getting_started.about_shortcuts": "إذا كان المستخدم المستهدف في نفس النطاق الذي تستخدمه، فإسم المستخدم وحده يكفي. وتنطبق نفس القاعدة على ذكر الأشخاص في المنشورات و التبويقات.", |  | ||||||
|   "getting_started.open_source_notice": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على GitHub {github}. {apps}.", |  | ||||||
|   "home.column_settings.advanced": "متقدمة", |  | ||||||
|   "home.column_settings.basic": "أساسية", |  | ||||||
|   "home.column_settings.filter_regex": "تصفية حسب التعبيرات العادية", |  | ||||||
|   "home.column_settings.show_reblogs": "عرض الترقيات", |  | ||||||
|   "home.column_settings.show_replies": "عرض الردود", |  | ||||||
|   "home.settings": "إعدادات العمود", |  | ||||||
|   "lightbox.close": "إغلاق", |  | ||||||
|   "loading_indicator.label": "تحميل ...", |  | ||||||
|   "media_gallery.toggle_visible": "Toggle visibility", |  | ||||||
|   "missing_indicator.label": "تعذر العثور عليه", |  | ||||||
|   "navigation_bar.blocks": "الحسابات المحجوبة", |  | ||||||
|   "navigation_bar.community_timeline": "الخيط العام المحلي", |  | ||||||
|   "navigation_bar.edit_profile": "تعديل الملف الشخصي", |  | ||||||
|   "navigation_bar.preferences": "التفضيلات", |  | ||||||
|   "navigation_bar.community_timeline": "الخيط العام المحلي", |  | ||||||
|   "navigation_bar.public_timeline": "الخيط العام الموحد", |  | ||||||
|   "navigation_bar.logout": "خروج", |  | ||||||
|   "reply_indicator.cancel": "إلغاء", |  | ||||||
|   "search.placeholder": "ابحث", |  | ||||||
|   "search.account": "حساب", |  | ||||||
|   "search.hashtag": "وسم", |  | ||||||
|   "status.mention": "أذكُر @{name}", |  | ||||||
|   "status.delete": "إحذف", |  | ||||||
|   "status.reply": "ردّ", |  | ||||||
|   "status.reblog": "رَقِّي", |  | ||||||
|   "status.favourite": "أضف إلى المفضلة", |  | ||||||
|   "status.reblogged_by": "{name} رقى", |  | ||||||
|   "status.sensitive_warning": "محتوى حساس", |  | ||||||
|   "status.sensitive_toggle": "اضغط للعرض", |  | ||||||
|   "status.show_more": "أظهر المزيد", |  | ||||||
|   "status.show_less": "إعرض أقلّ", |  | ||||||
|   "status.open": "وسع هذه المشاركة", |  | ||||||
|   "status.report": "إبلِغ عن @{name}", |  | ||||||
|   "tabs_bar.compose": "تحرير", |  | ||||||
|   "tabs_bar.home": "الرئيسية", |  | ||||||
|   "tabs_bar.mentions": "الإشارات", |  | ||||||
|   "tabs_bar.public": "الخيط العام الموحد", |  | ||||||
|   "tabs_bar.notifications": "الإشعارات", |  | ||||||
|   "upload_button.label": "إضافة وسائط", |  | ||||||
|   "upload_form.undo": "إلغاء", |  | ||||||
|   "notification.follow": "{name} يتبعك", |  | ||||||
|   "notification.favourite": "{name} أعجب بمنشورك", |  | ||||||
|   "notification.reblog": "{name} قام بترقية تبويقك", |  | ||||||
|   "notification.mention": "{name} ذكرك", |  | ||||||
|   "notifications.column_settings.alert": "إشعارات سطح المكتب", |  | ||||||
|   "notifications.column_settings.show": "إعرِضها في عمود", |  | ||||||
|   "notifications.column_settings.follow": "متابعُون جُدُد :", |  | ||||||
|   "notifications.column_settings.favourite": "المُفَضَّلة :", |  | ||||||
|   "notifications.column_settings.mention": "الإشارات :", |  | ||||||
|   "notifications.column_settings.reblog": "الترقيّات:", |  | ||||||
|   "video_player.toggle_sound": "تبديل الصوت", |  | ||||||
|   "video_player.toggle_visible": "إظهار / إخفاء الفيديو", |  | ||||||
|   "video_player.expand": "توسيع الفيديو", |  | ||||||
|   "video_player.video_error": "تعذر تشغيل الفيديو", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default ar; |  | ||||||
| @@ -1,68 +0,0 @@ | |||||||
| const bg = { |  | ||||||
|   "column_back_button.label": "Назад", |  | ||||||
|   "lightbox.close": "Затвори", |  | ||||||
|   "loading_indicator.label": "Зареждане...", |  | ||||||
|   "status.mention": "Споменаване", |  | ||||||
|   "status.delete": "Изтриване", |  | ||||||
|   "status.reply": "Отговор", |  | ||||||
|   "status.reblog": "Споделяне", |  | ||||||
|   "status.favourite": "Предпочитани", |  | ||||||
|   "status.reblogged_by": "{name} сподели", |  | ||||||
|   "status.sensitive_warning": "Деликатно съдържание", |  | ||||||
|   "status.sensitive_toggle": "Покажи", |  | ||||||
|   "video_player.toggle_sound": "Звук", |  | ||||||
|   "account.mention": "Споменаване", |  | ||||||
|   "account.edit_profile": "Редактирай профила си", |  | ||||||
|   "account.unblock": "Не блокирай", |  | ||||||
|   "account.unfollow": "Не следвай", |  | ||||||
|   "account.block": "Блокирай", |  | ||||||
|   "account.follow": "Последвай", |  | ||||||
|   "account.posts": "Публикации", |  | ||||||
|   "account.follows": "Следвам", |  | ||||||
|   "account.followers": "Последователи", |  | ||||||
|   "account.follows_you": "Твой последовател", |  | ||||||
|   "account.requested": "В очакване на одобрение", |  | ||||||
|   "getting_started.heading": "Първи стъпки", |  | ||||||
|   "getting_started.about_addressing": "Можеш да последваш потребител, ако знаеш потребителското му име и домейна, на който се намира, като в полето за търсене ги въведеш по този начин: име@домейн", |  | ||||||
|   "getting_started.about_shortcuts": "Ако с търсения потребител се намирате на един и същ домейн, достатъчно е да въведеш само името. Същото важи и за споменаване на хора в публикации.", |  | ||||||
|   "getting_started.about_developer": "Можеш да потърсиш разработчика на този проект като: Gargron@mastodon.social", |  | ||||||
|   "getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.", |  | ||||||
|   "column.home": "Начало", |  | ||||||
|   "column.mentions": "Споменавания", |  | ||||||
|   "column.public": "Публичен канал", |  | ||||||
|   "column.notifications": "Известия", |  | ||||||
|   "tabs_bar.compose": "Съставяне", |  | ||||||
|   "tabs_bar.home": "Начало", |  | ||||||
|   "tabs_bar.mentions": "Споменавания", |  | ||||||
|   "tabs_bar.public": "Публичен канал", |  | ||||||
|   "tabs_bar.notifications": "Известия", |  | ||||||
|   "compose_form.placeholder": "Какво си мислиш?", |  | ||||||
|   "compose_form.publish": "Раздумай", |  | ||||||
|   "compose_form.sensitive": "Отбележи съдържанието като деликатно", |  | ||||||
|   "compose_form.spoiler": "Скрий текста зад предупреждение", |  | ||||||
|   "compose_form.private": "Отбележи като поверително", |  | ||||||
|   "compose_form.privacy_disclaimer": "Поверителни публикации ще бъдат изпратени до споменатите потребители на {domains}. Доверяваш ли се на {domainsCount, plural, one {that server} other {those servers}}, че няма да издаде твоята публикация?", |  | ||||||
|   "compose_form.unlisted": "Не показвай в публичния канал", |  | ||||||
|   "navigation_bar.edit_profile": "Редактирай профил", |  | ||||||
|   "navigation_bar.preferences": "Предпочитания", |  | ||||||
|   "navigation_bar.public_timeline": "Публичен канал", |  | ||||||
|   "navigation_bar.logout": "Излизане", |  | ||||||
|   "reply_indicator.cancel": "Отказ", |  | ||||||
|   "search.placeholder": "Търсене", |  | ||||||
|   "search.account": "Акаунт", |  | ||||||
|   "search.hashtag": "Хаштаг", |  | ||||||
|   "upload_button.label": "Добави медия", |  | ||||||
|   "upload_form.undo": "Отмяна", |  | ||||||
|   "notification.follow": "{name} те последва", |  | ||||||
|   "notification.favourite": "{name} хареса твоята публикация", |  | ||||||
|   "notification.reblog": "{name} сподели твоята публикация", |  | ||||||
|   "notification.mention": "{name} те спомена", |  | ||||||
|   "notifications.column_settings.alert": "Десктоп известия", |  | ||||||
|   "notifications.column_settings.show": "Покажи в колона", |  | ||||||
|   "notifications.column_settings.follow": "Нови последователи:", |  | ||||||
|   "notifications.column_settings.favourite": "Предпочитани:", |  | ||||||
|   "notifications.column_settings.mention": "Споменавания:", |  | ||||||
|   "notifications.column_settings.reblog": "Споделяния:", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default bg; |  | ||||||
| @@ -1,68 +0,0 @@ | |||||||
| const eo = { |  | ||||||
|   "column_back_button.label": "Reveni", |  | ||||||
|   "lightbox.close": "Fermi", |  | ||||||
|   "loading_indicator.label": "Ŝarĝanta...", |  | ||||||
|   "status.mention": "Mencii @{name}", |  | ||||||
|   "status.delete": "Forigi", |  | ||||||
|   "status.reply": "Respondi", |  | ||||||
|   "status.reblog": "Diskonigi", |  | ||||||
|   "status.favourite": "Favori", |  | ||||||
|   "status.reblogged_by": "{name} diskonigita", |  | ||||||
|   "status.sensitive_warning": "Tikla enhavo", |  | ||||||
|   "status.sensitive_toggle": "Alklaki por vidi", |  | ||||||
|   "video_player.toggle_sound": "Aktivigi sonojn", |  | ||||||
|   "account.mention": "Mencii @{name}", |  | ||||||
|   "account.edit_profile": "Redakti la profilon", |  | ||||||
|   "account.unblock": "Malbloki @{name}", |  | ||||||
|   "account.unfollow": "Malsekvi", |  | ||||||
|   "account.block": "Bloki @{name}", |  | ||||||
|   "account.follow": "Sekvi", |  | ||||||
|   "account.posts": "Mesaĝoj", |  | ||||||
|   "account.follows": "Sekvatoj", |  | ||||||
|   "account.followers": "Sekvantoj", |  | ||||||
|   "account.follows_you": "Sekvas vin", |  | ||||||
|   "account.requested": "Atendas aprobon", |  | ||||||
|   "getting_started.heading": "Por komenci", |  | ||||||
|   "getting_started.about_addressing": "Vi povas sekvi homojn se vi konas la uzantnomon kaj domajnon tajpinte retpoŝtecan adreson en la serĉilon.", |  | ||||||
|   "getting_started.about_shortcuts": "Se la celita uzanto troviĝas en la sama domajno de vi, uzi nur la uzantnomon sufiĉos. La sama regulo validas por mencii aliajn uzantojn en mesaĝo.", |  | ||||||
|   "getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en github je {github}. {apps}.", |  | ||||||
|   "column.home": "Hejmo", |  | ||||||
|   "column.community": "Loka tempolinio", |  | ||||||
|   "column.public": "Fratara tempolinio", |  | ||||||
|   "column.notifications": "Sciigoj", |  | ||||||
|   "tabs_bar.compose": "Ekskribi", |  | ||||||
|   "tabs_bar.home": "Hejmo", |  | ||||||
|   "tabs_bar.mentions": "Sciigoj", |  | ||||||
|   "tabs_bar.public": "Fratara tempolinio", |  | ||||||
|   "tabs_bar.notifications": "Sciigoj", |  | ||||||
|   "compose_form.placeholder": "Pri kio vi pensas?", |  | ||||||
|   "compose_form.publish": "Hup", |  | ||||||
|   "compose_form.sensitive": "Marki ke la enhavo estas tikla", |  | ||||||
|   "compose_form.spoiler": "Kaŝi la tekston malantaŭ averto", |  | ||||||
|   "compose_form.private": "Marki ke la enhavo estas privata", |  | ||||||
|   "compose_form.privacy_disclaimer": "Via privata mesaĝo estos sendita nur al menciitaj uzantoj en {domains}. Ĉu vi fidas {domainsCount, plural, one {tiun servilon} other {tiujn servilojn}}? Mesaĝa privateco funkcias nur en aperaĵoj de Mastodon. Se {domains} {domainsCount, plural, one {ne estas aperaĵo de Mastodon} other {ne estas aperaĵoj de Mastodon}}, estos neniu indiko ke via mesaĝo estas privata, kaj ĝi povus esti diskonigita aŭ videbligita al necelitaj ricevantoj.", |  | ||||||
|   "compose_form.unlisted": "Ne afiŝi en publikaj tempolinioj", |  | ||||||
|   "navigation_bar.edit_profile": "Redakti la profilon", |  | ||||||
|   "navigation_bar.preferences": "Preferoj", |  | ||||||
|   "navigation_bar.community_timeline": "Loka tempolinio", |  | ||||||
|   "navigation_bar.public_timeline": "Fratara tempolinio", |  | ||||||
|   "navigation_bar.logout": "Elsaluti", |  | ||||||
|   "reply_indicator.cancel": "Rezigni", |  | ||||||
|   "search.placeholder": "Serĉi", |  | ||||||
|   "search.account": "Konto", |  | ||||||
|   "search.hashtag": "Kradvorto", |  | ||||||
|   "upload_button.label": "Aldoni enhavaĵon", |  | ||||||
|   "upload_form.undo": "Malfari", |  | ||||||
|   "notification.follow": "{name} sekvis vin", |  | ||||||
|   "notification.favourite": "{name} favoris vian mesaĝon", |  | ||||||
|   "notification.reblog": "{name} diskonigis vian mesaĝon", |  | ||||||
|   "notification.mention": "{name} menciis vin", |  | ||||||
|   "notifications.column_settings.alert": "Retumilaj atentigoj", |  | ||||||
|   "notifications.column_settings.show": "Montri en kolono", |  | ||||||
|   "notifications.column_settings.follow": "Novaj sekvantoj:", |  | ||||||
|   "notifications.column_settings.favourite": "Favoroj:", |  | ||||||
|   "notifications.column_settings.mention": "Mencioj:", |  | ||||||
|   "notifications.column_settings.reblog": "Diskonigoj:", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default eo; |  | ||||||
| @@ -1,93 +0,0 @@ | |||||||
| const es = { |  | ||||||
|   "column_back_button.label": "Atrás", |  | ||||||
|   "lightbox.close": "Cerrar", |  | ||||||
|   "loading_indicator.label": "Cargando...", |  | ||||||
|   "status.mention": "Mencionar", |  | ||||||
|   "status.delete": "Borrar", |  | ||||||
|   "status.reply": "Responder", |  | ||||||
|   "status.reblog": "Retoot", |  | ||||||
|   "status.favourite": "Favorito", |  | ||||||
|   "status.reblogged_by": "Retooteado por {name}", |  | ||||||
|   "status.sensitive_warning": "Contenido sensible", |  | ||||||
|   "status.sensitive_toggle": "Click para ver", |  | ||||||
|   "status.show_more": "Mostrar más", |  | ||||||
|   "status.show_less": "Mostrar menos", |  | ||||||
|   "status.open": "Expandir estado", |  | ||||||
|   "status.report": "Reportar", |  | ||||||
|   "video_player.toggle_sound": "Act/Desac. sonido", |  | ||||||
|   "account.mention": "Mencionar", |  | ||||||
|   "account.edit_profile": "Editar perfil", |  | ||||||
|   "account.unblock": "Desbloquear", |  | ||||||
|   "account.unfollow": "Dejar de seguir", |  | ||||||
|   "account.mute": "Silenciar", |  | ||||||
|   "account.block": "Bloquear", |  | ||||||
|   "account.follow": "Seguir", |  | ||||||
|   "account.posts": "Publicaciones", |  | ||||||
|   "account.follows": "Seguir", |  | ||||||
|   "account.followers": "Seguidores", |  | ||||||
|   "account.follows_you": "Te sigue", |  | ||||||
|   "account.requested": "Esperando aprobación", |  | ||||||
|   "getting_started.heading": "Primeros pasos", |  | ||||||
|   "getting_started.about_addressing": "Puedes seguir a gente si conoces su nombre de usuario y el dominio en el que están registrados, introduciendo algo similar a una dirección de correo electrónico en el formulario en la parte superior de la barra lateral.", |  | ||||||
|   "getting_started.about_shortcuts": "Si el usuario que buscas está en el mismo dominio que tú, simplemente funcionará introduciendo el nombre de usuario. La misma regla se aplica para mencionar a usuarios.", |  | ||||||
|   "getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}. {apps}.", |  | ||||||
|   "column.home": "Inicio", |  | ||||||
|   "column.community": "Historia local", |  | ||||||
|   "column.public": "Historia federada", |  | ||||||
|   "column.notifications": "Notificaciones", |  | ||||||
|   "column.blocks": "Usuarios bloqueados", |  | ||||||
|   "column.favourites": "Favoritos", |  | ||||||
|   "column.follow_requests": "Solicitudes para seguirte", |  | ||||||
|   "column.mutes": "Usuarios silenciados", |  | ||||||
|   "tabs_bar.compose": "Redactar", |  | ||||||
|   "tabs_bar.home": "Inicio", |  | ||||||
|   "tabs_bar.mentions": "Menciones", |  | ||||||
|   "tabs_bar.public": "Público", |  | ||||||
|   "tabs_bar.notifications": "Notificaciones", |  | ||||||
|   "compose_form.placeholder": "¿En qué estás pensando?", |  | ||||||
|   "compose_form.publish": "Tootear", |  | ||||||
|   "compose_form.sensitive": "Marcar contenido como sensible", |  | ||||||
|   "compose_form.spoiler": "Ocultar texto tras advertencia", |  | ||||||
|   "compose_form.spoiler_placeholder": "Advertencia de contenido", |  | ||||||
|   "composer_form.private": "Marcar como privado", |  | ||||||
|   "composer_form.privacy_disclaimer": "Tu estado se mostrará a los usuarios mencionados en {domains}. Tu estado podrá ser visto en otras instancias, quizás no quieras que tu estado sea visto por otros usuarios.", |  | ||||||
|   "compose_form.unlisted": "No mostrar en la historia federada", |  | ||||||
|   "navigation_bar.edit_profile": "Editar perfil", |  | ||||||
|   "navigation_bar.preferences": "Preferencias", |  | ||||||
|   "navigation_bar.community_timeline": "Historia local", |  | ||||||
|   "navigation_bar.public_timeline": "Historia federada", |  | ||||||
|   "navigation_bar.favourites": "Favoritos", |  | ||||||
|   "navigation_bar.blocks": "Usuarios bloqueados", |  | ||||||
|   "navigation_bar.info": "Información adicional", |  | ||||||
|   "navigation_bar.logout": "Cerrar sesión", |  | ||||||
|   "navigation_bar.follow_requests": "Solicitudes para seguirte", |  | ||||||
|   "navigation_bar.mutes": "Usuarios silenciados", |  | ||||||
|   "reply_indicator.cancel": "Cancelar", |  | ||||||
|   "search.placeholder": "Buscar", |  | ||||||
|   "search.account": "Cuenta", |  | ||||||
|   "search.hashtag": "Etiqueta", |  | ||||||
|   "upload_button.label": "Subir multimedia", |  | ||||||
|   "upload_form.undo": "Deshacer", |  | ||||||
|   "notification.follow": "{name} te empezó a seguir", |  | ||||||
|   "notification.favourite": "{name} marcó tu estado como favorito", |  | ||||||
|   "notification.reblog": "{name} ha retooteado tu estado", |  | ||||||
|   "notification.mention": "{name} te ha mencionado", |  | ||||||
|   "notifications.column_settings.alert": "Notificaciones de escritorio", |  | ||||||
|   "notifications.column_settings.show": "Mostrar en columna", |  | ||||||
|   "notifications.column_settings.follow": "Nuevos seguidores:", |  | ||||||
|   "notifications.column_settings.favourite": "Favoritos:", |  | ||||||
|   "notifications.column_settings.mention": "Menciones:", |  | ||||||
|   "notifications.column_settings.reblog": "Retoots:", |  | ||||||
|   "emoji_button.label": "Insertar emoji", |  | ||||||
|   "privacy.public.short": "Público", |  | ||||||
|   "privacy.public.long": "Mostrar en la historia federada", |  | ||||||
|   "privacy.unlisted.short": "Sin federar", |  | ||||||
|   "privacy.unlisted.long": "No mostrar en la historia federada", |  | ||||||
|   "privacy.private.short": "Privado", |  | ||||||
|   "privacy.private.long": "Sólo mostrar a seguidores", |  | ||||||
|   "privacy.direct.short": "Directo", |  | ||||||
|   "privacy.direct.long": "Sólo mostrar a los usuarios mencionados", |  | ||||||
|   "privacy.change": "Ajustar privacidad" |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default es; |  | ||||||
| @@ -1,68 +0,0 @@ | |||||||
| const fi = { |  | ||||||
|   "column_back_button.label": "Takaisin", |  | ||||||
|   "lightbox.close": "Sulje", |  | ||||||
|   "loading_indicator.label": "Ladataan...", |  | ||||||
|   "status.mention": "Mainitse @{name}", |  | ||||||
|   "status.delete": "Poista", |  | ||||||
|   "status.reply": "Vastaa", |  | ||||||
|   "status.reblog": "Buustaa", |  | ||||||
|   "status.favourite": "Tykkää", |  | ||||||
|   "status.reblogged_by": "{name} buustasi", |  | ||||||
|   "status.sensitive_warning": "Arkaluontoista sisältöä", |  | ||||||
|   "status.sensitive_toggle": "Klikkaa nähdäksesi", |  | ||||||
|   "video_player.toggle_sound": "Äänet päälle/pois", |  | ||||||
|   "account.mention": "Mainitse @{name}", |  | ||||||
|   "account.edit_profile": "Muokkaa", |  | ||||||
|   "account.unblock": "Salli @{name}", |  | ||||||
|   "account.unfollow": "Lopeta seuraaminen", |  | ||||||
|   "account.block": "Estä @{name}", |  | ||||||
|   "account.follow": "Seuraa", |  | ||||||
|   "account.posts": "Postit", |  | ||||||
|   "account.follows": "Seuraa", |  | ||||||
|   "account.followers": "Seuraajia", |  | ||||||
|   "account.follows_you": "Seuraa sinua", |  | ||||||
|   "account.requested": "Odottaa hyväksyntää", |  | ||||||
|   "getting_started.heading": "Aloitus", |  | ||||||
|   "getting_started.about_addressing": "Voit seurata ihmisiä jos tiedät heidän käyttäjänimensä ja domainin missä he ovat syöttämällä e-mail-esque osoitteen Etsi kenttään.", |  | ||||||
|   "getting_started.about_shortcuts": "Jos etsimäsi henkilö on samassa domainissa kuin sinä, pelkkä käyttäjänimi kelpaa. Sama pätee kun mainitset ihmisiä statuksessasi", |  | ||||||
|   "getting_started.open_source_notice": "Mastodon Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}. {apps}.", |  | ||||||
|   "column.home": "Koti", |  | ||||||
|   "column.community": "Paikallinen aikajana", |  | ||||||
|   "column.public": "Yleinen aikajana", |  | ||||||
|   "column.notifications": "Ilmoitukset", |  | ||||||
|   "tabs_bar.compose": "Luo", |  | ||||||
|   "tabs_bar.home": "Koti", |  | ||||||
|   "tabs_bar.mentions": "Maininnat", |  | ||||||
|   "tabs_bar.public": "Yleinen aikajana", |  | ||||||
|   "tabs_bar.notifications": "Ilmoitukset", |  | ||||||
|   "compose_form.placeholder": "Mitä sinulla on mielessä?", |  | ||||||
|   "compose_form.publish": "Toot", |  | ||||||
|   "compose_form.sensitive": "Merkitse media herkäksi", |  | ||||||
|   "compose_form.spoiler": "Piiloita teksti varoituksen taakse", |  | ||||||
|   "compose_form.private": "Merkitse yksityiseksi", |  | ||||||
|   "compose_form.privacy_disclaimer": "Sinun yksityinen status toimitetaan mainitsemallesi käyttäjille domaineissa {domains}. Luotatko {domainsCount, plural, one {tähän palvelimeen} other {näihin palvelimiin}}? Postauksen yksityisyys toimii van Mastodon palvelimilla. Jos {domains} {domainsCount, plural, one {ei ole Mastodon palvelin} other {eivät ole Mastodon palvelin}}, viestiin ei tule Yksityinen-merkintää, ja sitä voidaan boostata tai muuten tehdä näkyväksi muille vastaanottajille.", |  | ||||||
|   "compose_form.unlisted": "Älä näytä yleisillä aikajanoilla", |  | ||||||
|   "navigation_bar.edit_profile": "Muokkaa profiilia", |  | ||||||
|   "navigation_bar.preferences": "Ominaisuudet", |  | ||||||
|   "navigation_bar.community_timeline": "Paikallinen aikajana", |  | ||||||
|   "navigation_bar.public_timeline": "Yleinen aikajana", |  | ||||||
|   "navigation_bar.logout": "Kirjaudu ulos", |  | ||||||
|   "reply_indicator.cancel": "Peruuta", |  | ||||||
|   "search.placeholder": "Hae", |  | ||||||
|   "search.account": "Tili", |  | ||||||
|   "search.hashtag": "Hashtag", |  | ||||||
|   "upload_button.label": "Lisää mediaa", |  | ||||||
|   "upload_form.undo": "Peru", |  | ||||||
|   "notification.follow": "{name} seurasi sinua", |  | ||||||
|   "notification.favourite": "{name} tykkäsi statuksestasi", |  | ||||||
|   "notification.reblog": "{name} buustasi statustasi", |  | ||||||
|   "notification.mention": "{name} mainitsi sinut", |  | ||||||
|   "notifications.column_settings.alert": "Työpöytä ilmoitukset", |  | ||||||
|   "notifications.column_settings.show": "Näytä sarakkeessa", |  | ||||||
|   "notifications.column_settings.follow": "Uusia seuraajia:", |  | ||||||
|   "notifications.column_settings.favourite": "Tykkäyksiä:", |  | ||||||
|   "notifications.column_settings.mention": "Mainintoja:", |  | ||||||
|   "notifications.column_settings.reblog": "Buusteja:", |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default fi; |  | ||||||
| @@ -1,57 +0,0 @@ | |||||||
| const hu = { |  | ||||||
|   "column_back_button.label": "Vissza", |  | ||||||
|   "lightbox.close": "Bezárás", |  | ||||||
|   "loading_indicator.label": "Betöltés...", |  | ||||||
|   "status.mention": "Említés", |  | ||||||
|   "status.delete": "Törlés", |  | ||||||
|   "status.reply": "Válasz", |  | ||||||
|   "status.reblog": "Reblog", |  | ||||||
|   "status.favourite": "Kedvenc", |  | ||||||
|   "status.reblogged_by": "{name} reblogolta", |  | ||||||
|   "status.sensitive_warning": "Érzékeny tartalom", |  | ||||||
|   "status.sensitive_toggle": "Katt a megtekintéshez", |  | ||||||
|   "video_player.toggle_sound": "Hang kapcsolása", |  | ||||||
|   "account.mention": "Említés", |  | ||||||
|   "account.edit_profile": "Profil szerkesztése", |  | ||||||
|   "account.unblock": "Blokkolás levétele", |  | ||||||
|   "account.unfollow": "Követés abbahagyása", |  | ||||||
|   "account.block": "Blokkolás", |  | ||||||
|   "account.follow": "Követés", |  | ||||||
|   "account.posts": "Posts", |  | ||||||
|   "account.follows": "Követve", |  | ||||||
|   "account.followers": "Követők", |  | ||||||
|   "account.follows_you": "Követnek téged", |  | ||||||
|   "getting_started.heading": "Első lépések", |  | ||||||
|   "getting_started.about_addressing": "Követhetsz embereket felhasználónevük és a doménjük ismeretében, amennyiben megadod ezt az e-mail-szerű címet az oldalsáv tetején lévő rubrikában.", |  | ||||||
|   "getting_started.about_shortcuts": "Ha a célzott személy azonos doménen tartózkodik, a felhasználónév elegendő. Ugyanez érvényes mikor személyeket említesz az állapotokban.", |  | ||||||
|   "getting_started.about_developer": "A projekt fejlesztője követhető, mint Gargron@mastodon.social", |  | ||||||
|   "column.home": "Kezdőlap", |  | ||||||
|   "column.mentions": "Említések", |  | ||||||
|   "column.public": "Nyilvános", |  | ||||||
|   "column.notifications": "Értesítések", |  | ||||||
|   "tabs_bar.compose": "Összeállítás", |  | ||||||
|   "tabs_bar.home": "Kezdőlap", |  | ||||||
|   "tabs_bar.mentions": "Említések", |  | ||||||
|   "tabs_bar.public": "Nyilvános", |  | ||||||
|   "tabs_bar.notifications": "Notifications", |  | ||||||
|   "compose_form.placeholder": "Mire gondolsz?", |  | ||||||
|   "compose_form.publish": "Tülk!", |  | ||||||
|   "compose_form.sensitive": "Tartalom érzékenynek jelölése", |  | ||||||
|   "compose_form.unlisted": "Listázatlan mód", |  | ||||||
|   "navigation_bar.edit_profile": "Profil szerkesztése", |  | ||||||
|   "navigation_bar.preferences": "Beállítások", |  | ||||||
|   "navigation_bar.public_timeline": "Nyilvános időfolyam", |  | ||||||
|   "navigation_bar.logout": "Kijelentkezés", |  | ||||||
|   "reply_indicator.cancel": "Mégsem", |  | ||||||
|   "search.placeholder": "Keresés", |  | ||||||
|   "search.account": "Fiók", |  | ||||||
|   "search.hashtag": "Hashtag", |  | ||||||
|   "upload_button.label": "Média hozzáadása", |  | ||||||
|   "upload_form.undo": "Mégsem", |  | ||||||
|   "notification.follow": "{name} követ téged", |  | ||||||
|   "notification.favourite": "{name} kedvencnek jelölte az állapotod", |  | ||||||
|   "notification.reblog": "{name} reblogolta az állapotod", |  | ||||||
|   "notification.mention": "{name} megemlített" |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default hu; |  | ||||||
| @@ -1,55 +0,0 @@ | |||||||
| import ar from './ar'; |  | ||||||
| import en from './en'; |  | ||||||
| import de from './de'; |  | ||||||
| import es from './es'; |  | ||||||
| import fa from './fa'; |  | ||||||
| import hr from './hr'; |  | ||||||
| import hu from './hu'; |  | ||||||
| import io from './io'; |  | ||||||
| import it from './it'; |  | ||||||
| import fr from './fr'; |  | ||||||
| import nl from './nl'; |  | ||||||
| import no from './no'; |  | ||||||
| import oc from './oc'; |  | ||||||
| import pt from './pt'; |  | ||||||
| import pt_br from './pt-br'; |  | ||||||
| import uk from './uk'; |  | ||||||
| import fi from './fi'; |  | ||||||
| import eo from './eo'; |  | ||||||
| import ru from './ru'; |  | ||||||
| import ja from './ja'; |  | ||||||
| import zh_hk from './zh-hk'; |  | ||||||
| import zh_cn from './zh-cn'; |  | ||||||
| import bg from './bg'; |  | ||||||
| import id from './id'; |  | ||||||
|  |  | ||||||
| const locales = { |  | ||||||
|   ar, |  | ||||||
|   en, |  | ||||||
|   de, |  | ||||||
|   es, |  | ||||||
|   fa, |  | ||||||
|   hr, |  | ||||||
|   hu, |  | ||||||
|   io, |  | ||||||
|   it, |  | ||||||
|   fr, |  | ||||||
|   nl, |  | ||||||
|   no, |  | ||||||
|   oc, |  | ||||||
|   pt, |  | ||||||
|   'pt-BR': pt_br, |  | ||||||
|   uk, |  | ||||||
|   fi, |  | ||||||
|   eo, |  | ||||||
|   ru, |  | ||||||
|   ja, |  | ||||||
|   'zh-HK': zh_hk, |  | ||||||
|   'zh-CN': zh_cn, |  | ||||||
|   bg, |  | ||||||
|   id, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default function getMessagesForLocale (locale) { |  | ||||||
|   return locales[locale]; |  | ||||||
| }; |  | ||||||
| @@ -1,57 +0,0 @@ | |||||||
| const uk = { |  | ||||||
|   "column_back_button.label": "Назад", |  | ||||||
|   "lightbox.close": "Закрити", |  | ||||||
|   "loading_indicator.label": "Завантаження...", |  | ||||||
|   "status.mention": "Згадати", |  | ||||||
|   "status.delete": "Видалити", |  | ||||||
|   "status.reply": "Відповісти", |  | ||||||
|   "status.reblog": "Передмухнути", |  | ||||||
|   "status.favourite": "Подобається", |  | ||||||
|   "status.reblogged_by": "{name} передмухнув(-ла)", |  | ||||||
|   "status.sensitive_warning": "Непристойний зміст", |  | ||||||
|   "status.sensitive_toggle": "Натисніть, щоб подивитися", |  | ||||||
|   "video_player.toggle_sound": "Увімкнути/вимкнути звук", |  | ||||||
|   "account.mention": "Згадати", |  | ||||||
|   "account.edit_profile": "Налаштування профілю", |  | ||||||
|   "account.unblock": "Розблокувати", |  | ||||||
|   "account.unfollow": "Відписатися", |  | ||||||
|   "account.block": "Заблокувати", |  | ||||||
|   "account.follow": "Підписатися", |  | ||||||
|   "account.posts": "Пости", |  | ||||||
|   "account.follows": "Підписки", |  | ||||||
|   "account.followers": "Підписники", |  | ||||||
|   "account.follows_you": "Підписаний", |  | ||||||
|   "getting_started.heading": "Ласкаво просимо", |  | ||||||
|   "getting_started.about_addressing": "Ви можете підписуватись на людей, якщо ви знаєте їх ім'я користувача чи домен, шляхом введення email-подібної адреси у верхньому рядку бокової панелі.", |  | ||||||
|   "getting_started.about_shortcuts": "Якщо користувач, якого ви шукаєте, знаходиться на тому ж домені, що й ви, можна просто ввести ім'я користувача. Це правило стосується й згадування людей у статусах.", |  | ||||||
|   "getting_started.about_developer": "Розробник проекту знаходиться за адресою Gargron@mastodon.social", |  | ||||||
|   "column.home": "Головна", |  | ||||||
|   "column.mentions": "Згадування", |  | ||||||
|   "column.public": "Стіна", |  | ||||||
|   "column.notifications": "Сповіщення", |  | ||||||
|   "tabs_bar.compose": "Написати", |  | ||||||
|   "tabs_bar.home": "Головна", |  | ||||||
|   "tabs_bar.mentions": "Згадування", |  | ||||||
|   "tabs_bar.public": "Стіна", |  | ||||||
|   "tabs_bar.notifications": "Сповіщення", |  | ||||||
|   "compose_form.placeholder": "Що у Вас на думці?", |  | ||||||
|   "compose_form.publish": "Дмухнути", |  | ||||||
|   "compose_form.sensitive": "Непристойний зміст", |  | ||||||
|   "compose_form.unlisted": "Таємний режим", |  | ||||||
|   "navigation_bar.edit_profile": "Редагувати профіль", |  | ||||||
|   "navigation_bar.preferences": "Налаштування", |  | ||||||
|   "navigation_bar.public_timeline": "Публічна стіна", |  | ||||||
|   "navigation_bar.logout": "Вийти", |  | ||||||
|   "reply_indicator.cancel": "Відмінити", |  | ||||||
|   "search.placeholder": "Пошук", |  | ||||||
|   "search.account": "Аккаунт", |  | ||||||
|   "search.hashtag": "Хештеґ", |  | ||||||
|   "upload_button.label": "Додати медіа", |  | ||||||
|   "upload_form.undo": "Відмінити", |  | ||||||
|   "notification.follow": "{name} підписався(-лась) на Вас", |  | ||||||
|   "notification.favourite": "{name} сподобався ваш допис", |  | ||||||
|   "notification.reblog": "{name} передмухнув(-ла) Ваш статус", |  | ||||||
|   "notification.mention": "{name} згадав(-ла) Вас" |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default uk; |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| const play = audio => { |  | ||||||
|   if (!audio.paused) { |  | ||||||
|     audio.pause(); |  | ||||||
|     audio.fastSeek(0); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   audio.play(); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export default function soundsMiddleware() { |  | ||||||
|   const soundCache = { |  | ||||||
|     boop: new Audio(['/sounds/boop.mp3']) |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   return ({ dispatch }) => next => (action) => { |  | ||||||
|     if (action.meta && action.meta.sound && soundCache[action.meta.sound]) { |  | ||||||
|       play(soundCache[action.meta.sound]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return next(action); |  | ||||||
|   }; |  | ||||||
| }; |  | ||||||
| @@ -1,49 +0,0 @@ | |||||||
| import emojify from './components/emoji'; |  | ||||||
| import { length } from 'stringz'; |  | ||||||
|  |  | ||||||
| $(() => { |  | ||||||
|   $.each($('.emojify'), (_, content) => { |  | ||||||
|     const $content = $(content); |  | ||||||
|     $content.html(emojify($content.html())); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   $('.video-player video').on('click', e => { |  | ||||||
|     if (e.target.paused) { |  | ||||||
|       e.target.play(); |  | ||||||
|     } else { |  | ||||||
|       e.target.pause(); |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   $('.media-spoiler').on('click', e => { |  | ||||||
|     $(e.target).hide(); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   $('.webapp-btn').on('click', e => { |  | ||||||
|     if (e.button === 0) { |  | ||||||
|       e.preventDefault(); |  | ||||||
|       window.location.href = $(e.target).attr('href'); |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   $('.status__content__spoiler-link').on('click', e => { |  | ||||||
|     e.preventDefault(); |  | ||||||
|     const contentEl = $(e.target).parent().parent().find('div'); |  | ||||||
|  |  | ||||||
|     if (contentEl.is(':visible')) { |  | ||||||
|       contentEl.hide(); |  | ||||||
|       $(e.target).parent().attr('style', 'margin-bottom: 0'); |  | ||||||
|     } else { |  | ||||||
|       contentEl.show(); |  | ||||||
|       $(e.target).parent().attr('style', null); |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   // used on /settings/profile |  | ||||||
|   $('.account_display_name').on('input', e => { |  | ||||||
|     $('.name-counter').text(30 - length($(e.target).val())); |  | ||||||
|   }); |  | ||||||
|   $('.account_note').on('input', e => { |  | ||||||
|     $('.note-counter').text(160 - length($(e.target).val())); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,11 +0,0 @@ | |||||||
| @font-face { |  | ||||||
|   font-family: 'Montserrat'; |  | ||||||
|   src: local('Montserrat'); |  | ||||||
|   src: font-url('montserrat/Montserrat-Regular.eot'); |  | ||||||
|   src: font-url('montserrat/Montserrat-Regular.eot?#iefix') format('embedded-opentype'), |  | ||||||
|   font-url('montserrat/Montserrat-Regular.woff2') format('woff2'), |  | ||||||
|   font-url('montserrat/Montserrat-Regular.woff') format('woff'), |  | ||||||
|   font-url('montserrat/Montserrat-Regular.ttf') format('truetype'); |  | ||||||
|   font-weight: 400; |  | ||||||
|   font-style: normal; |  | ||||||
| } |  | ||||||
| @@ -1,12 +0,0 @@ | |||||||
| @font-face { |  | ||||||
|   font-family: 'Roboto Mono'; |  | ||||||
|   src: local('Roboto Mono'); |  | ||||||
|   src: font-url('roboto-mono/robotomono-regular-webfont.eot'); |  | ||||||
|   src: font-url('roboto-mono/robotomono-regular-webfont.eot?#iefix') format('embedded-opentype'), |  | ||||||
|   font-url('roboto-mono/robotomono-regular-webfont.woff2') format('woff2'), |  | ||||||
|   font-url('roboto-mono/robotomono-regular-webfont.woff') format('woff'), |  | ||||||
|   font-url('roboto-mono/robotomono-regular-webfont.ttf') format('truetype'), |  | ||||||
|   font-url('roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular') format('svg'); |  | ||||||
|   font-weight: 400; |  | ||||||
|   font-style: normal; |  | ||||||
| } |  | ||||||
| @@ -1,52 +0,0 @@ | |||||||
| @font-face { |  | ||||||
|   font-family: 'Roboto'; |  | ||||||
|   src: local('Roboto'); |  | ||||||
|   src: font-url('roboto/roboto-italic-webfont.eot'); |  | ||||||
|   src: font-url('roboto/roboto-italic-webfont.eot?#iefix') format('embedded-opentype'), |  | ||||||
|     font-url('roboto/roboto-italic-webfont.woff2') format('woff2'), |  | ||||||
|     font-url('roboto/roboto-italic-webfont.woff') format('woff'), |  | ||||||
|     font-url('roboto/roboto-italic-webfont.ttf') format('truetype'), |  | ||||||
|     font-url('roboto/roboto-italic-webfont.svg#roboto-italic-webfont') format('svg'); |  | ||||||
|   font-weight: normal; |  | ||||||
|   font-style: italic; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @font-face { |  | ||||||
|   font-family: 'Roboto'; |  | ||||||
|   src: local('Roboto'); |  | ||||||
|   src: font-url('roboto/roboto-bold-webfont.eot'); |  | ||||||
|   src: local('Roboto bold'), local('roboto-bold'), |  | ||||||
|     font-url('roboto/roboto-bold-webfont.eot?#iefix') format('embedded-opentype'), |  | ||||||
|     font-url('roboto/roboto-bold-webfont.woff2') format('woff2'), |  | ||||||
|     font-url('roboto/roboto-bold-webfont.woff') format('woff'), |  | ||||||
|     font-url('roboto/roboto-bold-webfont.ttf') format('truetype'), |  | ||||||
|     font-url('roboto/roboto-bold-webfont.svg#roboto-bold-webfont') format('svg'); |  | ||||||
|   font-weight: bold; |  | ||||||
|   font-style: normal; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @font-face { |  | ||||||
|   font-family: 'Roboto'; |  | ||||||
|   src: local('Roboto'); |  | ||||||
|   src: font-url('roboto/roboto-medium-webfont.eot'); |  | ||||||
|   src: font-url('roboto/roboto-medium-webfont.eot?#iefix') format('embedded-opentype'), |  | ||||||
|     font-url('roboto/roboto-medium-webfont.woff2') format('woff2'), |  | ||||||
|     font-url('roboto/roboto-medium-webfont.woff') format('woff'), |  | ||||||
|     font-url('roboto/roboto-medium-webfont.ttf') format('truetype'), |  | ||||||
|     font-url('roboto/roboto-medium-webfont.svg#roboto-medium-webfont') format('svg'); |  | ||||||
|   font-weight: 500; |  | ||||||
|   font-style: normal; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @font-face { |  | ||||||
|   font-family: 'Roboto'; |  | ||||||
|   src: local('Roboto'); |  | ||||||
|   src: font-url('roboto/roboto-regular-webfont.eot'); |  | ||||||
|   src: font-url('roboto/roboto-regular-webfont.eot?#iefix') format('embedded-opentype'), |  | ||||||
|     font-url('roboto/roboto-regular-webfont.woff2') format('woff2'), |  | ||||||
|     font-url('roboto/roboto-regular-webfont.woff') format('woff'), |  | ||||||
|     font-url('roboto/roboto-regular-webfont.ttf') format('truetype'), |  | ||||||
|     font-url('roboto/roboto-regular-webfont.svg#roboto-regular-webfont') format('svg'); |  | ||||||
|   font-weight: normal; |  | ||||||
|   font-style: normal; |  | ||||||
| } |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| $color1: #282c37 !default; // darkest |  | ||||||
| $color2: #d9e1e8 !default; // lightest |  | ||||||
| $color3: #9baec8 !default; // lighter |  | ||||||
| $color4: #2b90d9 !default; // vibrant |  | ||||||
| $color5: #ffffff !default; // white |  | ||||||
| $color6: #df405a !default; // error red |  | ||||||
| $color7: #79bd9a !default; // succ green |  | ||||||
| $color8: #000000 !default; // black |  | ||||||
| @@ -6,12 +6,12 @@ class AccountsController < ApplicationController | |||||||
|   def show |   def show | ||||||
|     respond_to do |format| |     respond_to do |format| | ||||||
|       format.html do |       format.html do | ||||||
|         @statuses = @account.statuses.permitted_for(@account, current_account).order('id desc').paginate_by_max_id(20, params[:max_id], params[:since_id]) |         @statuses = @account.statuses.permitted_for(@account, current_account).recent.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.recent.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 | ||||||
|   | |||||||
| @@ -2,17 +2,15 @@ | |||||||
|  |  | ||||||
| module Admin | module Admin | ||||||
|   class ConfirmationsController < BaseController |   class ConfirmationsController < BaseController | ||||||
|     before_action :set_account |  | ||||||
|  |  | ||||||
|     def create |     def create | ||||||
|       @account.user.confirm |       account_user.confirm | ||||||
|       redirect_to admin_accounts_path |       redirect_to admin_accounts_path | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     private |     private | ||||||
|  |  | ||||||
|     def set_account |     def account_user | ||||||
|       @account = Account.find(params[:account_id]) |       Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -2,8 +2,10 @@ | |||||||
|  |  | ||||||
| module Admin | module Admin | ||||||
|   class DomainBlocksController < BaseController |   class DomainBlocksController < BaseController | ||||||
|  |     before_action :set_domain_block, only: [:show, :destroy] | ||||||
|  |  | ||||||
|     def index |     def index | ||||||
|       @blocks = DomainBlock.page(params[:page]) |       @domain_blocks = DomainBlock.page(params[:page]) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def new |     def new | ||||||
| @@ -21,20 +23,25 @@ module Admin | |||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def show |     def show; end | ||||||
|       @domain_block = DomainBlock.find(params[:id]) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def destroy |     def destroy | ||||||
|       @domain_block = DomainBlock.find(params[:id]) |       UnblockDomainService.new.call(@domain_block, retroactive_unblock?) | ||||||
|       UnblockDomainService.new.call(@domain_block, resource_params[:retroactive]) |  | ||||||
|       redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg') |       redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg') | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     private |     private | ||||||
|  |  | ||||||
|  |     def set_domain_block | ||||||
|  |       @domain_block = DomainBlock.find(params[:id]) | ||||||
|  |     end | ||||||
|  |  | ||||||
|     def resource_params |     def resource_params | ||||||
|       params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive) |       params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  |     def retroactive_unblock? | ||||||
|  |       ActiveRecord::Type.lookup(:boolean).cast(resource_params[:retroactive]) | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -3,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 | ||||||
|   | |||||||
| @@ -2,17 +2,31 @@ | |||||||
|  |  | ||||||
| module Admin | module Admin | ||||||
|   class ReportedStatusesController < BaseController |   class ReportedStatusesController < BaseController | ||||||
|     def destroy |     before_action :set_report | ||||||
|       status = Status.find params[:id] |     before_action :set_status | ||||||
|  |  | ||||||
|       RemovalWorker.perform_async(status.id) |     def update | ||||||
|       redirect_to admin_report_path(report) |       @status.update(status_params) | ||||||
|  |       redirect_to admin_report_path(@report) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def destroy | ||||||
|  |       RemovalWorker.perform_async(@status.id) | ||||||
|  |       redirect_to admin_report_path(@report) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     private |     private | ||||||
|  |  | ||||||
|     def report |     def status_params | ||||||
|       Report.find(params[:report_id]) |       params.require(:status).permit(:sensitive) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def set_report | ||||||
|  |       @report = Report.find(params[:report_id]) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def set_status | ||||||
|  |       @status = @report.statuses.find(params[:id]) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ module Admin | |||||||
|     end |     end | ||||||
|  |  | ||||||
|     def filtered_reports |     def filtered_reports | ||||||
|       ReportFilter.new(filter_params).results.order('id desc').includes( |       ReportFilter.new(filter_params).results.order(id: :desc).includes( | ||||||
|         :account, |         :account, | ||||||
|         :target_account |         :target_account | ||||||
|       ) |       ) | ||||||
|   | |||||||
| @@ -2,38 +2,43 @@ | |||||||
|  |  | ||||||
| module Admin | module Admin | ||||||
|   class SettingsController < BaseController |   class SettingsController < BaseController | ||||||
|  |     ADMIN_SETTINGS = %w( | ||||||
|  |       site_contact_username | ||||||
|  |       site_contact_email | ||||||
|  |       site_title | ||||||
|  |       site_description | ||||||
|  |       site_extended_description | ||||||
|  |       open_registrations | ||||||
|  |       closed_registrations_message | ||||||
|  |     ).freeze | ||||||
|     BOOLEAN_SETTINGS = %w(open_registrations).freeze |     BOOLEAN_SETTINGS = %w(open_registrations).freeze | ||||||
|  |  | ||||||
|     def index |     def edit | ||||||
|       @settings = Setting.all_as_records |       @settings = Setting.all_as_records | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def update |     def update | ||||||
|       @setting = Setting.where(var: params[:id]).first_or_initialize(var: params[:id]) |       settings_params.each do |key, value| | ||||||
|       @setting.update(value: value_for_update) |         setting = Setting.where(var: key).first_or_initialize(var: key) | ||||||
|  |         setting.update(value: value_for_update(key, value)) | ||||||
|       respond_to do |format| |  | ||||||
|         format.html { redirect_to admin_settings_path } |  | ||||||
|         format.json { respond_with_bip(@setting) } |  | ||||||
|       end |       end | ||||||
|  |  | ||||||
|  |       flash[:notice] = 'Success!' | ||||||
|  |       redirect_to edit_admin_settings_path | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     private |     private | ||||||
|  |  | ||||||
|     def settings_params |     def settings_params | ||||||
|       params.require(:setting).permit(:value) |       params.permit(ADMIN_SETTINGS) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def value_for_update |     def value_for_update(key, value) | ||||||
|       if updating_boolean_setting? |       if BOOLEAN_SETTINGS.include?(key) | ||||||
|         settings_params[:value] == 'true' |         value == 'true' | ||||||
|       else |       else | ||||||
|         settings_params[:value] |         value | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def updating_boolean_setting? |  | ||||||
|       BOOLEAN_SETTINGS.include?(params[:id]) |  | ||||||
|     end |  | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -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 | ||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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' | ||||||
|  |       Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds) | ||||||
|  |     when 'unsubscribe' | ||||||
|  |       Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback) | ||||||
|  |     else | ||||||
|  |       ["Unknown mode: #{hub_mode}", 422] | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|     uri    = Addressable::URI.parse(topic_url).normalize |   def hub_mode | ||||||
|     params = Rails.application.routes.recognize_path(uri.path) |     params['hub.mode'] | ||||||
|     domain = uri.host + (uri.port ? ":#{uri.port}" : '') |   end | ||||||
|  |  | ||||||
|     return unless TagManager.instance.web_domain?(domain) && params[:controller] == 'accounts' && params[:action] == 'show' && params[:format] == 'atom' |   def hub_topic | ||||||
|  |     params['hub.topic'] | ||||||
|  |   end | ||||||
|  |  | ||||||
|     Account.find_local(params[:username]) |   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 | ||||||
|   | |||||||
| @@ -5,13 +5,13 @@ class Api::SalmonController < ApiController | |||||||
|   respond_to :txt |   respond_to :txt | ||||||
|  |  | ||||||
|   def update |   def update | ||||||
|     body = request.body.read |     payload = request.body.read | ||||||
|  |  | ||||||
|     if body.nil? |     if !payload.nil? && verify?(payload) | ||||||
|       head 200 |       SalmonWorker.perform_async(@account.id, payload.force_encoding('UTF-8')) | ||||||
|     else |  | ||||||
|       SalmonWorker.perform_async(@account.id, body.force_encoding('UTF-8')) |  | ||||||
|       head 201 |       head 201 | ||||||
|  |     else | ||||||
|  |       head 202 | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -20,4 +20,8 @@ class Api::SalmonController < ApiController | |||||||
|   def set_account |   def set_account | ||||||
|     @account = Account.find(params[:id]) |     @account = Account.find(params[:id]) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def verify?(payload) | ||||||
|  |     VerifySalmonService.new.call(payload) | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -19,10 +19,9 @@ class Api::SubscriptionsController < ApiController | |||||||
|  |  | ||||||
|     if subscription.verify(body, request.headers['HTTP_X_HUB_SIGNATURE']) |     if subscription.verify(body, request.headers['HTTP_X_HUB_SIGNATURE']) | ||||||
|       ProcessingWorker.perform_async(@account.id, body.force_encoding('UTF-8')) |       ProcessingWorker.perform_async(@account.id, body.force_encoding('UTF-8')) | ||||||
|       head 201 |  | ||||||
|     else |  | ||||||
|       head 202 |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  |     head 200 | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|   | |||||||
| @@ -23,12 +23,14 @@ class Api::V1::AccountsController < ApiController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def following |   def following | ||||||
|     results   = Follow.where(account: @account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) |     @accounts = Account.includes(:passive_relationships) | ||||||
|     accounts  = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h |                        .references(:passive_relationships) | ||||||
|     @accounts = results.map { |f| accounts[f.target_account_id] } |                        .merge(Follow.where(account: @account) | ||||||
|  |                                     .paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])) | ||||||
|  |                        .to_a | ||||||
|  |  | ||||||
|     next_path = following_api_v1_account_url(pagination_params(max_id: results.last.id))    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) |     next_path = following_api_v1_account_url(pagination_params(max_id: @accounts.last.passive_relationships.first.id))     if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) | ||||||
|     prev_path = following_api_v1_account_url(pagination_params(since_id: results.first.id)) unless results.empty? |     prev_path = following_api_v1_account_url(pagination_params(since_id: @accounts.first.passive_relationships.first.id)) unless @accounts.empty? | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
|  |  | ||||||
| @@ -36,12 +38,16 @@ class Api::V1::AccountsController < ApiController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def followers |   def followers | ||||||
|     results   = Follow.where(target_account: @account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) |     @accounts = Account.includes(:active_relationships) | ||||||
|     accounts  = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h |                        .references(:active_relationships) | ||||||
|     @accounts = results.map { |f| accounts[f.account_id] } |                        .merge(Follow.where(target_account: @account) | ||||||
|  |                                     .paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), | ||||||
|  |                                                         params[:max_id], | ||||||
|  |                                                         params[:since_id])) | ||||||
|  |                        .to_a | ||||||
|  |  | ||||||
|     next_path = followers_api_v1_account_url(pagination_params(max_id: results.last.id))    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) |     next_path = followers_api_v1_account_url(pagination_params(max_id: @accounts.last.active_relationships.first.id))     if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) | ||||||
|     prev_path = followers_api_v1_account_url(pagination_params(since_id: results.first.id)) unless results.empty? |     prev_path = followers_api_v1_account_url(pagination_params(since_id: @accounts.first.active_relationships.first.id)) unless @accounts.empty? | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
|  |  | ||||||
| @@ -56,7 +62,7 @@ class Api::V1::AccountsController < ApiController | |||||||
|  |  | ||||||
|     set_maps(@statuses) |     set_maps(@statuses) | ||||||
|  |  | ||||||
|     next_path = statuses_api_v1_account_url(statuses_pagination_params(max_id: @statuses.last.id))    unless @statuses.empty? |     next_path = statuses_api_v1_account_url(statuses_pagination_params(max_id: @statuses.last.id))    if @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT) | ||||||
|     prev_path = statuses_api_v1_account_url(statuses_pagination_params(since_id: @statuses.first.id)) unless @statuses.empty? |     prev_path = statuses_api_v1_account_url(statuses_pagination_params(since_id: @statuses.first.id)) unless @statuses.empty? | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
| @@ -75,7 +81,8 @@ class Api::V1::AccountsController < ApiController | |||||||
|     @followed_by     = { @account.id => false } |     @followed_by     = { @account.id => false } | ||||||
|     @blocking        = { @account.id => true } |     @blocking        = { @account.id => true } | ||||||
|     @requested       = { @account.id => false } |     @requested       = { @account.id => false } | ||||||
|     @muting      = { @account.id => current_user.account.muting?(@account.id) } |     @muting          = { @account.id => current_account.muting?(@account.id) } | ||||||
|  |     @domain_blocking = { @account.id => current_account.domain_blocking?(@account.domain) } | ||||||
|  |  | ||||||
|     render :relationship |     render :relationship | ||||||
|   end |   end | ||||||
| @@ -113,6 +120,7 @@ class Api::V1::AccountsController < ApiController | |||||||
|     @blocking        = Account.blocking_map(ids, current_user.account_id) |     @blocking        = Account.blocking_map(ids, current_user.account_id) | ||||||
|     @muting          = Account.muting_map(ids, current_user.account_id) |     @muting          = Account.muting_map(ids, current_user.account_id) | ||||||
|     @requested       = Account.requested_map(ids, current_user.account_id) |     @requested       = Account.requested_map(ids, current_user.account_id) | ||||||
|  |     @domain_blocking = Account.domain_blocking_map(ids, current_user.account_id) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def search |   def search | ||||||
| @@ -133,6 +141,7 @@ class Api::V1::AccountsController < ApiController | |||||||
|     @blocking        = Account.blocking_map([@account.id], current_user.account_id) |     @blocking        = Account.blocking_map([@account.id], current_user.account_id) | ||||||
|     @muting          = Account.muting_map([@account.id], current_user.account_id) |     @muting          = Account.muting_map([@account.id], current_user.account_id) | ||||||
|     @requested       = Account.requested_map([@account.id], current_user.account_id) |     @requested       = Account.requested_map([@account.id], current_user.account_id) | ||||||
|  |     @domain_blocking = Account.domain_blocking_map([@account.id], current_user.account_id) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def pagination_params(core_params) |   def pagination_params(core_params) | ||||||
|   | |||||||
| @@ -7,12 +7,14 @@ class Api::V1::BlocksController < ApiController | |||||||
|   respond_to :json |   respond_to :json | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     results   = Block.where(account: current_account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) |     @accounts = Account.includes(:blocked_by) | ||||||
|     accounts  = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h |                        .references(:blocked_by) | ||||||
|     @accounts = results.map { |f| accounts[f.target_account_id] }.compact |                        .merge(Block.where(account: current_account) | ||||||
|  |                                    .paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])) | ||||||
|  |                        .to_a | ||||||
|  |  | ||||||
|     next_path = api_v1_blocks_url(pagination_params(max_id: results.last.id))    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) |     next_path = api_v1_blocks_url(pagination_params(max_id: @accounts.last.blocked_by_ids.last))     if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) | ||||||
|     prev_path = api_v1_blocks_url(pagination_params(since_id: results.first.id)) unless results.empty? |     prev_path = api_v1_blocks_url(pagination_params(since_id: @accounts.first.blocked_by_ids.first)) unless @accounts.empty? | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
|   end |   end | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								app/controllers/api/v1/domain_blocks_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/controllers/api/v1/domain_blocks_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | class Api::V1::DomainBlocksController < ApiController | ||||||
|  |   before_action -> { doorkeeper_authorize! :follow } | ||||||
|  |   before_action :require_user! | ||||||
|  |  | ||||||
|  |   respond_to :json | ||||||
|  |  | ||||||
|  |   def show | ||||||
|  |     @blocks = AccountDomainBlock.where(account: current_account).paginate_by_max_id(limit_param(100), params[:max_id], params[:since_id]) | ||||||
|  |  | ||||||
|  |     next_path = api_v1_domain_blocks_url(pagination_params(max_id: @blocks.last.id))    if @blocks.size == limit_param(100) | ||||||
|  |     prev_path = api_v1_domain_blocks_url(pagination_params(since_id: @blocks.first.id)) unless @blocks.empty? | ||||||
|  |  | ||||||
|  |     set_pagination_headers(next_path, prev_path) | ||||||
|  |     render json: @blocks.map(&:domain) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def create | ||||||
|  |     BlockDomainFromAccountService.new.call(current_account, domain_block_params[:domain]) | ||||||
|  |     render_empty | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def destroy | ||||||
|  |     current_account.unblock_domain!(domain_block_params[:domain]) | ||||||
|  |     render_empty | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def pagination_params(core_params) | ||||||
|  |     params.permit(:limit).merge(core_params) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def domain_block_params | ||||||
|  |     params.permit(:domain) | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -5,12 +5,14 @@ class Api::V1::FollowRequestsController < ApiController | |||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     results   = FollowRequest.where(target_account: current_account).paginate_by_max_id(DEFAULT_ACCOUNTS_LIMIT, params[:max_id], params[:since_id]) |     @accounts = Account.includes(:follow_requests) | ||||||
|     accounts  = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h |                        .references(:follow_requests) | ||||||
|     @accounts = results.map { |f| accounts[f.account_id] } |                        .merge(FollowRequest.where(target_account: current_account) | ||||||
|  |                                            .paginate_by_max_id(DEFAULT_ACCOUNTS_LIMIT, params[:max_id], params[:since_id])) | ||||||
|  |                        .to_a | ||||||
|  |  | ||||||
|     next_path = api_v1_follow_requests_url(pagination_params(max_id: results.last.id))    if results.size == DEFAULT_ACCOUNTS_LIMIT |     next_path = api_v1_follow_requests_url(pagination_params(max_id: @accounts.last.follow_requests.last.id))     if @accounts.size == DEFAULT_ACCOUNTS_LIMIT | ||||||
|     prev_path = api_v1_follow_requests_url(pagination_params(since_id: results.first.id)) unless results.empty? |     prev_path = api_v1_follow_requests_url(pagination_params(since_id: @accounts.first.follow_requests.first.id)) unless @accounts.empty? | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -7,12 +7,14 @@ class Api::V1::MutesController < ApiController | |||||||
|   respond_to :json |   respond_to :json | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     results   = Mute.where(account: current_account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) |     @accounts = Account.includes(:muted_by) | ||||||
|     accounts  = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h |                        .references(:muted_by) | ||||||
|     @accounts = results.map { |f| accounts[f.target_account_id] } |                        .merge(Mute.where(account: current_account) | ||||||
|  |                                   .paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])) | ||||||
|  |                        .to_a | ||||||
|  |  | ||||||
|     next_path = api_v1_mutes_url(pagination_params(max_id: results.last.id))    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) |     next_path = api_v1_mutes_url(pagination_params(max_id: @accounts.last.muted_by_ids.last))     if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) | ||||||
|     prev_path = api_v1_mutes_url(pagination_params(since_id: results.first.id)) unless results.empty? |     prev_path = api_v1_mutes_url(pagination_params(since_id: @accounts.first.muted_by_ids.first)) unless @accounts.empty? | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
|  |  | ||||||
| class Api::V1::StatusesController < ApiController | class Api::V1::StatusesController < ApiController | ||||||
|   before_action :authorize_if_got_token, except:            [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite] |   before_action :authorize_if_got_token, except:            [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite, :mute, :unmute] | ||||||
|   before_action -> { doorkeeper_authorize! :write }, only:  [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite] |   before_action -> { doorkeeper_authorize! :write }, only:  [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite, :mute, :unmute] | ||||||
|   before_action :require_user!, except:  [:show, :context, :card, :reblogged_by, :favourited_by] |   before_action :require_user!, except:  [:show, :context, :card, :reblogged_by, :favourited_by] | ||||||
|   before_action :set_status, only:      [:show, :context, :card, :reblogged_by, :favourited_by] |   before_action :set_status, only:       [:show, :context, :card, :reblogged_by, :favourited_by, :mute, :unmute] | ||||||
|  |   before_action :set_conversation, only: [:mute, :unmute] | ||||||
|  |  | ||||||
|   respond_to :json |   respond_to :json | ||||||
|  |  | ||||||
| @@ -31,12 +32,14 @@ class Api::V1::StatusesController < ApiController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def reblogged_by |   def reblogged_by | ||||||
|     results   = @status.reblogs.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) |     @accounts = Account.includes(:statuses) | ||||||
|     accounts  = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h |                        .references(:statuses) | ||||||
|     @accounts = results.map { |r| accounts[r.account_id] } |                        .merge(Status.where(reblog_of_id: @status.id) | ||||||
|  |                                     .paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])) | ||||||
|  |                        .to_a | ||||||
|  |  | ||||||
|     next_path = reblogged_by_api_v1_status_url(pagination_params(max_id: results.last.id))    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) |     next_path = reblogged_by_api_v1_status_url(pagination_params(max_id: @accounts.last.statuses.last.id))     if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) | ||||||
|     prev_path = reblogged_by_api_v1_status_url(pagination_params(since_id: results.first.id)) unless results.empty? |     prev_path = reblogged_by_api_v1_status_url(pagination_params(since_id: @accounts.first.statuses.first.id)) unless @accounts.empty? | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
|  |  | ||||||
| @@ -44,12 +47,14 @@ class Api::V1::StatusesController < ApiController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def favourited_by |   def favourited_by | ||||||
|     results   = @status.favourites.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) |     @accounts = Account.includes(:favourites) | ||||||
|     accounts  = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h |                        .references(:favourites) | ||||||
|     @accounts = results.map { |f| accounts[f.account_id] } |                        .where(favourites: { status_id: @status.id }) | ||||||
|  |                        .merge(Favourite.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])) | ||||||
|  |                        .to_a | ||||||
|  |  | ||||||
|     next_path = favourited_by_api_v1_status_url(pagination_params(max_id: results.last.id))    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) |     next_path = favourited_by_api_v1_status_url(pagination_params(max_id: @accounts.last.favourites.last.id))     if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) | ||||||
|     prev_path = favourited_by_api_v1_status_url(pagination_params(since_id: results.first.id)) unless results.empty? |     prev_path = favourited_by_api_v1_status_url(pagination_params(since_id: @accounts.first.favourites.first.id)) unless @accounts.empty? | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
|  |  | ||||||
| @@ -105,6 +110,22 @@ class Api::V1::StatusesController < ApiController | |||||||
|     render :show |     render :show | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def mute | ||||||
|  |     current_account.mute_conversation!(@conversation) | ||||||
|  |  | ||||||
|  |     @mutes_map = { @conversation.id => true } | ||||||
|  |  | ||||||
|  |     render :show | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def unmute | ||||||
|  |     current_account.unmute_conversation!(@conversation) | ||||||
|  |  | ||||||
|  |     @mutes_map = { @conversation.id => false } | ||||||
|  |  | ||||||
|  |     render :show | ||||||
|  |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_status |   def set_status | ||||||
| @@ -112,6 +133,11 @@ class Api::V1::StatusesController < ApiController | |||||||
|     raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account) |     raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def set_conversation | ||||||
|  |     @conversation = @status.conversation | ||||||
|  |     raise Mastodon::ValidationError if @conversation.nil? | ||||||
|  |   end | ||||||
|  |  | ||||||
|   def status_params |   def status_params | ||||||
|     params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: []) |     params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: []) | ||||||
|   end |   end | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								app/controllers/api/v1/timelines/base_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/controllers/api/v1/timelines/base_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | module Api::V1::Timelines | ||||||
|  |   class BaseController < ApiController | ||||||
|  |     respond_to :json | ||||||
|  |     after_action :insert_pagination_headers, unless: -> { @statuses.empty? } | ||||||
|  |  | ||||||
|  |     private | ||||||
|  |  | ||||||
|  |     def cache_collection(raw) | ||||||
|  |       super(raw, Status) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def pagination_params(core_params) | ||||||
|  |       params.permit(:local, :limit).merge(core_params) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def insert_pagination_headers | ||||||
|  |       set_pagination_headers(next_path, prev_path) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def next_path | ||||||
|  |       raise 'Override in child controllers' | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def prev_path | ||||||
|  |       raise 'Override in child controllers' | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										44
									
								
								app/controllers/api/v1/timelines/home_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								app/controllers/api/v1/timelines/home_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | module Api::V1::Timelines | ||||||
|  |   class HomeController < BaseController | ||||||
|  |     before_action -> { doorkeeper_authorize! :read }, only: [:show] | ||||||
|  |     before_action :require_user!, only: [:show] | ||||||
|  |  | ||||||
|  |     def show | ||||||
|  |       @statuses = load_statuses | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     private | ||||||
|  |  | ||||||
|  |     def load_statuses | ||||||
|  |       cached_home_statuses.tap do |statuses| | ||||||
|  |         set_maps(statuses) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def cached_home_statuses | ||||||
|  |       cache_collection home_statuses | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def home_statuses | ||||||
|  |       account_home_feed.get( | ||||||
|  |         limit_param(DEFAULT_STATUSES_LIMIT), | ||||||
|  |         params[:max_id], | ||||||
|  |         params[:since_id] | ||||||
|  |       ) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def account_home_feed | ||||||
|  |       Feed.new(:home, current_account) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def next_path | ||||||
|  |       api_v1_timelines_home_url pagination_params(max_id: @statuses.last.id) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def prev_path | ||||||
|  |       api_v1_timelines_home_url pagination_params(since_id: @statuses.first.id) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										41
									
								
								app/controllers/api/v1/timelines/public_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								app/controllers/api/v1/timelines/public_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | module Api::V1::Timelines | ||||||
|  |   class PublicController < BaseController | ||||||
|  |     def show | ||||||
|  |       @statuses = load_statuses | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     private | ||||||
|  |  | ||||||
|  |     def load_statuses | ||||||
|  |       cached_public_statuses.tap do |statuses| | ||||||
|  |         set_maps(statuses) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def cached_public_statuses | ||||||
|  |       cache_collection public_statuses | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def public_statuses | ||||||
|  |       public_timeline_statuses.paginate_by_max_id( | ||||||
|  |         limit_param(DEFAULT_STATUSES_LIMIT), | ||||||
|  |         params[:max_id], | ||||||
|  |         params[:since_id] | ||||||
|  |       ) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def public_timeline_statuses | ||||||
|  |       Status.as_public_timeline(current_account, params[:local]) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def next_path | ||||||
|  |       api_v1_timelines_public_url pagination_params(max_id: @statuses.last.id) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def prev_path | ||||||
|  |       api_v1_timelines_public_url pagination_params(since_id: @statuses.first.id) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										51
									
								
								app/controllers/api/v1/timelines/tag_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/controllers/api/v1/timelines/tag_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | module Api::V1::Timelines | ||||||
|  |   class TagController < BaseController | ||||||
|  |     before_action :load_tag | ||||||
|  |  | ||||||
|  |     def show | ||||||
|  |       @statuses = load_statuses | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     private | ||||||
|  |  | ||||||
|  |     def load_tag | ||||||
|  |       @tag = Tag.find_by(name: params[:id].downcase) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def load_statuses | ||||||
|  |       cached_tagged_statuses.tap do |statuses| | ||||||
|  |         set_maps(statuses) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def cached_tagged_statuses | ||||||
|  |       cache_collection tagged_statuses | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def tagged_statuses | ||||||
|  |       if @tag.nil? | ||||||
|  |         [] | ||||||
|  |       else | ||||||
|  |         tag_timeline_statuses.paginate_by_max_id( | ||||||
|  |           limit_param(DEFAULT_STATUSES_LIMIT), | ||||||
|  |           params[:max_id], | ||||||
|  |           params[:since_id] | ||||||
|  |         ) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def tag_timeline_statuses | ||||||
|  |       Status.as_tag_timeline(@tag, current_account, params[:local]) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def next_path | ||||||
|  |       api_v1_timelines_tag_url params[:id], pagination_params(max_id: @statuses.last.id) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     def prev_path | ||||||
|  |       api_v1_timelines_tag_url params[:id], pagination_params(since_id: @statuses.first.id) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -1,61 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::TimelinesController < ApiController |  | ||||||
|   before_action -> { doorkeeper_authorize! :read }, only: [:home] |  | ||||||
|   before_action :require_user!, only: [:home] |  | ||||||
|  |  | ||||||
|   respond_to :json |  | ||||||
|  |  | ||||||
|   def home |  | ||||||
|     @statuses = Feed.new(:home, current_account).get(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id]) |  | ||||||
|     @statuses = cache_collection(@statuses) |  | ||||||
|  |  | ||||||
|     set_maps(@statuses) |  | ||||||
|  |  | ||||||
|     next_path = api_v1_home_timeline_url(pagination_params(max_id: @statuses.last.id))    unless @statuses.empty? |  | ||||||
|     prev_path = api_v1_home_timeline_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty? |  | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |  | ||||||
|  |  | ||||||
|     render :index |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def public |  | ||||||
|     @statuses = Status.as_public_timeline(current_account, params[:local]).paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id]) |  | ||||||
|     @statuses = cache_collection(@statuses) |  | ||||||
|  |  | ||||||
|     set_maps(@statuses) |  | ||||||
|  |  | ||||||
|     next_path = api_v1_public_timeline_url(pagination_params(max_id: @statuses.last.id))    unless @statuses.empty? |  | ||||||
|     prev_path = api_v1_public_timeline_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty? |  | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |  | ||||||
|  |  | ||||||
|     render :index |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def tag |  | ||||||
|     @tag      = Tag.find_by(name: params[:id].downcase) |  | ||||||
|     @statuses = @tag.nil? ? [] : Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id]) |  | ||||||
|     @statuses = cache_collection(@statuses) |  | ||||||
|  |  | ||||||
|     set_maps(@statuses) |  | ||||||
|  |  | ||||||
|     next_path = api_v1_hashtag_timeline_url(params[:id], pagination_params(max_id: @statuses.last.id))    unless @statuses.empty? |  | ||||||
|     prev_path = api_v1_hashtag_timeline_url(params[:id], pagination_params(since_id: @statuses.first.id)) unless @statuses.empty? |  | ||||||
|  |  | ||||||
|     set_pagination_headers(next_path, prev_path) |  | ||||||
|  |  | ||||||
|     render :index |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def cache_collection(raw) |  | ||||||
|     super(raw, Status) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def pagination_params(core_params) |  | ||||||
|     params.permit(:local, :limit).merge(core_params) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -6,10 +6,15 @@ class Api::Web::SettingsController < ApiController | |||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|  |  | ||||||
|   def update |   def update | ||||||
|     setting      = ::Web::Setting.where(user: current_user).first_or_initialize(user: current_user) |  | ||||||
|     setting.data = params[:data] |     setting.data = params[:data] | ||||||
|     setting.save! |     setting.save! | ||||||
|  |  | ||||||
|     render_empty |     render_empty | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def setting | ||||||
|  |     @_setting ||= ::Web::Setting.where(user: current_user).first_or_initialize(user: current_user) | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -93,11 +93,14 @@ class ApiController < ApplicationController | |||||||
|     if current_account.nil? |     if current_account.nil? | ||||||
|       @reblogs_map    = {} |       @reblogs_map    = {} | ||||||
|       @favourites_map = {} |       @favourites_map = {} | ||||||
|  |       @mutes_map      = {} | ||||||
|       return |       return | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     status_ids       = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq |     status_ids       = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq | ||||||
|  |     conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq | ||||||
|     @reblogs_map     = Status.reblogs_map(status_ids, current_account) |     @reblogs_map     = Status.reblogs_map(status_ids, current_account) | ||||||
|     @favourites_map  = Status.favourites_map(status_ids, current_account) |     @favourites_map  = Status.favourites_map(status_ids, current_account) | ||||||
|  |     @mutes_map       = Status.mutes_map(conversation_ids, current_account) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ class ApplicationController < ActionController::Base | |||||||
|   force_ssl if: :https_enabled? |   force_ssl if: :https_enabled? | ||||||
|  |  | ||||||
|   include Localized |   include Localized | ||||||
|  |   include UserTrackingConcern | ||||||
|  |  | ||||||
|   helper_method :current_account |   helper_method :current_account | ||||||
|   helper_method :single_user_mode? |   helper_method :single_user_mode? | ||||||
| @@ -17,7 +18,6 @@ class ApplicationController < ActionController::Base | |||||||
|   rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity |   rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity | ||||||
|  |  | ||||||
|   before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? |   before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? | ||||||
|   before_action :set_user_activity |  | ||||||
|   before_action :check_suspension, if: :user_signed_in? |   before_action :check_suspension, if: :user_signed_in? | ||||||
|  |  | ||||||
|   def raise_not_found |   def raise_not_found | ||||||
| @@ -38,52 +38,30 @@ class ApplicationController < ActionController::Base | |||||||
|     redirect_to root_path unless current_user&.admin? |     redirect_to root_path unless current_user&.admin? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_user_activity |  | ||||||
|     return unless !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago) |  | ||||||
|  |  | ||||||
|     # Mark user as signed-in today |  | ||||||
|     current_user.update_tracked_fields(request) |  | ||||||
|  |  | ||||||
|     # If the sign in is after a two week break, we need to regenerate their feed |  | ||||||
|     RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def check_suspension |   def check_suspension | ||||||
|     head 403 if current_user.account.suspended? |     forbidden if current_user.account.suspended? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   protected |   protected | ||||||
|  |  | ||||||
|   def not_found |   def forbidden | ||||||
|     respond_to do |format| |     respond_with_error(403) | ||||||
|       format.any  { head 404 } |  | ||||||
|       format.html { respond_with_error(404) } |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def not_found | ||||||
|  |     respond_with_error(404) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def gone |   def gone | ||||||
|     respond_to do |format| |     respond_with_error(410) | ||||||
|       format.any  { head 410 } |  | ||||||
|       format.html { respond_with_error(410) } |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def forbidden |  | ||||||
|     respond_to do |format| |  | ||||||
|       format.any  { head 403 } |  | ||||||
|       format.html { render 'errors/403', layout: 'error', status: 403 } |  | ||||||
|     end |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def unprocessable_entity |   def unprocessable_entity | ||||||
|     respond_to do |format| |     respond_with_error(422) | ||||||
|       format.any  { head 422 } |  | ||||||
|       format.html { respond_with_error(422) } |  | ||||||
|     end |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def single_user_mode? |   def single_user_mode? | ||||||
|     @single_user_mode ||= Rails.configuration.x.single_user_mode && Account.first |     @single_user_mode ||= Rails.configuration.x.single_user_mode && Account.exists? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def current_account |   def current_account | ||||||
| @@ -115,7 +93,12 @@ class ApplicationController < ActionController::Base | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def respond_with_error(code) |   def respond_with_error(code) | ||||||
|  |     respond_to do |format| | ||||||
|  |       format.any  { head code } | ||||||
|  |       format.html do | ||||||
|         set_locale |         set_locale | ||||||
|         render "errors/#{code}", layout: 'error', status: code |         render "errors/#{code}", layout: 'error', status: code | ||||||
|       end |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -6,6 +6,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController | |||||||
|   before_action :check_enabled_registrations, only: [:new, :create] |   before_action :check_enabled_registrations, only: [:new, :create] | ||||||
|   before_action :configure_sign_up_params, only: [:create] |   before_action :configure_sign_up_params, only: [:create] | ||||||
|  |  | ||||||
|  |   def destroy | ||||||
|  |     not_found | ||||||
|  |   end | ||||||
|  |  | ||||||
|   protected |   protected | ||||||
|  |  | ||||||
|   def build_resource(hash = nil) |   def build_resource(hash = nil) | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ class Auth::SessionsController < Devise::SessionsController | |||||||
|   layout 'auth' |   layout 'auth' | ||||||
|  |  | ||||||
|   skip_before_action :require_no_authentication, only: [:create] |   skip_before_action :require_no_authentication, only: [:create] | ||||||
|  |   skip_before_action :check_suspension, only: [:destroy] | ||||||
|   prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] |   prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] | ||||||
|  |  | ||||||
|   def create |   def create | ||||||
| @@ -34,10 +35,10 @@ class Auth::SessionsController < Devise::SessionsController | |||||||
|     params.require(:user).permit(:email, :password, :otp_attempt) |     params.require(:user).permit(:email, :password, :otp_attempt) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def after_sign_in_path_for(_resource) |   def after_sign_in_path_for(resource) | ||||||
|     last_url = stored_location_for(:user) |     last_url = stored_location_for(:user) | ||||||
|  |  | ||||||
|     if [about_path].include?(last_url) |     if home_paths(resource).include?(last_url) | ||||||
|       root_path |       root_path | ||||||
|     else |     else | ||||||
|       last_url || root_path |       last_url || root_path | ||||||
| @@ -51,7 +52,7 @@ class Auth::SessionsController < Devise::SessionsController | |||||||
|   def valid_otp_attempt?(user) |   def valid_otp_attempt?(user) | ||||||
|     user.validate_and_consume_otp!(user_params[:otp_attempt]) || |     user.validate_and_consume_otp!(user_params[:otp_attempt]) || | ||||||
|       user.invalidate_otp_backup_code!(user_params[:otp_attempt]) |       user.invalidate_otp_backup_code!(user_params[:otp_attempt]) | ||||||
|   rescue OpenSSL::Cipher::CipherError => error |   rescue OpenSSL::Cipher::CipherError => _error | ||||||
|     false |     false | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -80,4 +81,14 @@ class Auth::SessionsController < Devise::SessionsController | |||||||
|     session[:otp_user_id] = user.id |     session[:otp_user_id] = user.id | ||||||
|     render :two_factor |     render :two_factor | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def home_paths(resource) | ||||||
|  |     paths = [about_path] | ||||||
|  |     if single_user_mode? && resource.is_a?(User) | ||||||
|  |       paths << short_account_path(username: resource.account) | ||||||
|  |     end | ||||||
|  |     paths | ||||||
|  |   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