Compare commits

..

507 Commits

Author SHA1 Message Date
David Yip
1b3ff6a92f Cascade mute/block destruction to notes (#193) 2018-01-27 01:31:22 -06:00
David Yip
2ce4af84e9 Put the less-specific column on the left-hand side (#193)
Some database systems can make better use of an index if the index
columns are ordered left-to-right in increasing specificity.  I'm not
sure if Postgres is one of those databases, but it also makes sense to
me to read an index as "ordered by type and then ID".
2018-01-27 01:25:22 -06:00
David Yip
c17c07158a Introduce Glitch::AccountRelationships (#193)
Where possible, it is a good idea to add new backend code in ways that
will minimize conflict with upstream changes.  We can't do that
everywhere, but we can move our custom methods to a module that won't
be modified upstream, and that's a start.

This commit also gets the block/mute note maps into
REST::RelationshipSerializer under the "block_notes" and "mute_notes"
keys.
2018-01-27 01:23:14 -06:00
David Yip
8f476ee39e Do not render note in RelationshipSerializer (#193)
RelationshipSerializer is used to render an Account's
block/mute/follow/etc. relationships; there won't be a note present on
an Account.  We'll need to look at a different serializer to render
notes.
2018-01-26 10:13:17 -06:00
David Yip
041168df31 Make Account#mute! spec expect returned Mute (#193) 2018-01-26 10:09:20 -06:00
David Yip
5e6a09498a Add notes to blocks (#193)
In this commit, we also push note creation into the AccountInteractions
concern, where we can:

1. create and persist model objects inside transactions without having
   to include job queuing inside the transaction
2. commonize the don't-add-a-blank-note check

Finally, this commit resolves a invocation-on-nil in
REST::RelationshipSerializer.
2018-01-26 04:05:42 -06:00
David Yip
1704e0066a Don't save mute notes if the note is blank (#193) 2018-01-25 22:42:19 -06:00
David Yip
27f8d2c739 Add notes to /api/v1/accounts/:id/mute (#193) 2018-01-25 22:38:02 -06:00
David Yip
47ec2eff65 Add notes to domain blocks and mutes (#193) 2018-01-25 22:07:14 -06:00
David Yip
a37753179d Introduce Glitch::Note (#193)
This is a model for attaching a single note to a single other entity.
For #193, we'll use it for annotating blocks, mutes, and account-level
domain blocks.
2018-01-25 22:07:11 -06:00
David Yip
00ce2be148 Merge remote-tracking branch 'origin/master' into gs-master
Conflicts:
	.travis.yml
2018-01-24 23:10:19 -06:00
Akihiko Odaki
7e07e61a30 Do not manually update system RubyGems (#6355)
Travis CI ships compatible system RubyGems now:
https://github.com/travis-ci/travis-ci/issues/8969#issuecomment-360288970
> I have repackaged the 2.5.0 archive for Linux to include RubyGems 2.7.4,
> which should have the fix for this issue. Please restart the affected
> jobs, and let us know how they go for you.
2018-01-25 04:23:29 +01:00
Akihiko Odaki
4a974c6db1 Do not require sudo on Travis CI (#6356)
The issue which the workaround for is now addressed:
https://github.com/travis-ci/travis-ci/issues/7941#issuecomment-310667894
> We've pushed out new stable trusty images to production with a patch.
2018-01-25 04:23:08 +01:00
Jenkins
0e10667fbe Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-24 19:17:11 +00:00
David Yip
1b4d79acf8 Merge pull request #346 from KnzkDev/fix/travis
Fix travis.yml
2018-01-24 13:09:13 -06:00
beatrix
5a4de1cf0f Merge pull request #349 from codl/patch-1
remove escapes from server-side ɣaml handler
2018-01-24 08:56:38 -05:00
abcang
a3c0a20373 Fix initial_state me in push_notification_register (#6349) 2018-01-24 14:37:46 +01:00
codl
9abb5becf4 remove escapes from server-side γaml handler
this matches a change that was made in the client-side equivalent
2018-01-24 14:23:25 +01:00
abcang
8cd2828e91 Fix hide and show media button on admin page (#6347) 2018-01-24 13:29:46 +01:00
Yamagishi Kazutoshi
3d881eed27 Add packs volume to docker-compose.yml (#6348) 2018-01-24 13:29:32 +01:00
Quenty31
7650506b39 l10n Occitan language update (#6346)
* Small changes

* update for email templates

* Digest changed

* Update oc.yml

waiting to finish

* Update oc.yml

* Update oc.yml

* Update oc.yml

* Update oc.json
2018-01-24 13:29:03 +01:00
Eugen Rochko
e6db3427b7 Bump to 2.2.0rc2 2018-01-24 04:16:30 +01:00
Eugen Rochko
daefbd66a6 Fix style of legacy column headers (#6342)
* Fix regression from #6199: Style of legacy column headers

* Fix tests

* Clean up variables
2018-01-24 04:03:51 +01:00
Eugen Rochko
b1daa71da5 Fix #6311: Replace relative URLs in CSS only for Premailer (#6335) 2018-01-24 02:57:14 +01:00
Eugen Rochko
1cc44cba81 Fix #6331 (#6341)
UserTrackingConcern is circumvented by SessionsController#create
because it calls warden, which calls the User#update_tracked_fields!
method directly. Move returning user logic to that method.
2018-01-23 20:52:30 +01:00
David Yip
c53d7196e6 Merge pull request #334 from ThibG/glitch-soc/cleanup-leftover-code
Remove some leftover code
2018-01-23 10:27:13 -06:00
ThibG
4ec9d8b4d9 Display deleted users' role as “Suspended” (#6339)
Follow-up to 6eb60260b1 which missed a spot where
the same error occurs.
2018-01-23 14:31:31 +01:00
Eugen Rochko
d966878e87 Update goldfinger, ostatus2 and http.rb versions (#6337) 2018-01-23 14:30:42 +01:00
Eugen Rochko
2fc2725076 Fix e-mail icon for reblog being stretched (#6336) 2018-01-23 14:30:28 +01:00
Thibaut Girka
2107891edb Remove some leftover code, I guess 2018-01-23 09:20:18 +01:00
ncls7615
16d5f9e162 Fix travis.yml 2018-01-23 15:50:59 +09:00
Eugen Rochko
69f13e7bca Fix regression from #6199: Make entire column header clickable (#6334) 2018-01-23 05:06:29 +01:00
Akihiko Odaki
613e7c7521 Rename ResolveRemoteAccountService to ResolveAccountService (#6327)
The service used to be named ResolveRemoteAccountService resolves local
accounts as well.
2018-01-22 14:25:09 +01:00
Akihiko Odaki
17cecd75ca Rename FetchRemoteResourceService to ResolveURLService (#6328)
The service used to be named FetchRemoteResourceService resolves local
URL as well.
2018-01-22 14:24:22 +01:00
Akihiko Odaki
8cc65cde27 Resolve URL for local account in follow authorization success view (#6324) 2018-01-22 14:22:14 +01:00
Renato "Lond" Cerqueira
b7f6ddeaf1 Weblate translations (#6323)
* Translated using Weblate (Galician)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/gl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (543 of 543 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (543 of 543 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/pt_BR/

* Translated using Weblate (Catalan)

Currently translated at 95.2% (517 of 543 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Catalan)

Currently translated at 99.6% (259 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/

* Translated using Weblate (Dutch)

Currently translated at 97.1% (543 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (559 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Translated using Weblate (Japanese)

Currently translated at 99.1% (554 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (French)

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.8% (558 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Polish)

Currently translated at 99.8% (558 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/

* Translated using Weblate (Catalan)

Currently translated at 94.6% (529 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Catalan)

Currently translated at 95.3% (533 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Dutch)

Currently translated at 98.2% (55 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Dutch)

Currently translated at 98.3% (550 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Catalan)

Currently translated at 95.3% (533 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Japanese)

Currently translated at 0.0% (0 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/

* Translated using Weblate (Japanese)

Currently translated at 1.7% (1 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/

* Translated using Weblate (Japanese)

Currently translated at 1.7% (52 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/

* Translated using Weblate (Japanese)

Currently translated at 99.8% (558 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Japanese)

Currently translated at 99.8% (558 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Galician)

Currently translated at 100.0% (261 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (261 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (261 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pl/

* Translated using Weblate (Catalan)

Currently translated at 99.6% (260 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/

* Translated using Weblate (Japanese)

Currently translated at 99.6% (260 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/

* Translated using Weblate (Japanese)

Currently translated at 99.8% (558 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Japanese)

Currently translated at 99.6% (260 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/

* Translated using Weblate (French)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (261 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (261 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/

* Translated using Weblate (French)

Currently translated at 99.2% (555 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (French)

Currently translated at 99.8% (558 of 559 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (261 of 261 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/

* Normalize translations
Ran i18n-tasks normalize && yarn manage:translations

* Add back values removed by weblate
2018-01-22 14:20:58 +01:00
David Yip
a90d0419d9 Merge pull request #343 from glitch-soc/merge-upstream-20180121
Merge upstream
2018-01-21 18:00:08 -06:00
Jeong Arm
143fb54ab9 Korean translation (#6322)
Related to #6273
2018-01-22 08:49:57 +09:00
David Yip
48d79007f6 Merge remote-tracking branch 'origin/master' into merge-upstream-20180121 2018-01-21 16:26:50 -06:00
David Yip
81b78fac54 Update Vagrantfile to ubuntu/xenial64.
The version of Redis that ships with Ubuntu Trusty is too old to support
the HyperLogLog commands.  I guess we could add a custom apt repo for
this, but it seems a lot easier to just use the latest Ubuntu LTS
release (i.e. xenial), which has a recent-enough Redis in its archives.
2018-01-21 16:09:40 -06:00
Norayr Chilingarian
48cd6dc6ca armenian translations initial commit (#6320)
* adding armenian translations

* updating config files for armenian translations

* armenian translation fix

* fixing spaces
2018-01-22 05:02:23 +09:00
David Yip
1253279feb Merge remote-tracking branch 'origin/master' into merge-upstream
Conflicts:
	app/controllers/settings/two_factor_authentication/confirmations_controller.rb
2018-01-21 13:36:10 -06:00
codl
cfd2b06821 fix filter search index being too strict when looking for line breaks (#6318) 2018-01-21 13:23:17 +01:00
ThibG
d613dda91d Fix assets loading when WEB_DOMAIN ≠ LOCAL_DOMAIN (#6319)
Since 872a0d5bd8, assets URL are absolute and
not relative. Unfortunately, the domain used to build such URLs is the wrong
one: LOCAL_DOMAIN, and not WEB_DOMAIN, where the assets are stored.
2018-01-21 13:22:31 +01:00
Aboobacker MK
112b1fa265 Redirect to 2FA creation page when otp_secret is not available (#6314) 2018-01-21 13:21:28 +01:00
Eugen Rochko
31d1485887 Display number of follow requests in getting started menu, and (#6313)
if notifications column is not mounted, also display number of
unread notifications there.
2018-01-21 13:20:20 +01:00
Eugen Rochko
1287de1b83 Fix invalid value passed to full_asset_url in mailers (#6312) 2018-01-21 13:20:00 +01:00
David Yip
2c578a01d8 Merge remote-tracking branch 'origin/master' into gs-master 2018-01-20 16:42:57 -06:00
David Yip
e7a0840f8c Merge pull request #336 from ThibG/glitch-soc/features/tag-warning
Display a warning when composing unlisted toots with something looking like a hashtag
2018-01-20 16:37:52 -06:00
David Yip
24b6e4121f Merge pull request #342 from glitch-soc/merge-upstream
Merge upstream
2018-01-20 16:37:35 -06:00
Iijima Yasushi
72f9eab3d6 Change mailer avatar url (#6309)
* Change mailer avatar url

* Use full_asset_url method
2018-01-20 19:27:13 +01:00
David Yip
3cae362214 Merge new onboarding styles; delete refs to PNGs. 2018-01-20 11:23:06 -06:00
David Yip
38f6d8130d Eliminate stray conflict markers. 2018-01-20 10:46:43 -06:00
David Yip
071c2c9c85 Merge remote-tracking branch 'origin/master' into merge-upstream
Conflicts:
	app/javascript/styles/mastodon/components.scss
2018-01-20 10:45:43 -06:00
Eugen Rochko
0b7a0d15c7 Bump version to 2.2.0rc1 2018-01-20 16:27:19 +01:00
SerCom_KC
80b3ca0f6f Update Simplified Chinese translations (#6306)
* i18n: (zh-CN) Add translations of #6251

* i18n: (zh-CN) Improve translations for #6291

* Fix en.json

* i18n: (zh-CN) Update translations for #6303

* i18n: (zh-CN) Add translations of #6273
with minor adjustment

* Minor fix

* Minor fix
2018-01-21 00:11:21 +09:00
Eugen Rochko
45afdf1781 Fix #6269 - Render LOCAL_DOMAIN as unicode in presentational views (#6305) 2018-01-20 03:49:06 +01:00
Eugen Rochko
79b34a0fa2 Restore onboarding modal (#6303)
* Restore onboarding modal
Revert 5ba8b3a396895ecec083c5258aaf9084d584a7c4

* Change greeting elephant graphic, fix up some design issues

* Fix wrong link color in onboarding modal
2018-01-20 01:32:37 +01:00
Eugen Rochko
872a0d5bd8 Improve HTML e-mails based on Litmus tests (#6301)
* Use PNG images in HTML e-mails

* Make webpack use URLs with host so fonts load inside HTML e-mails

Convert this back to a relative URL in the premailer CSS loader
since local requests are quicker

* Improve responsive design

* Add missing PNG icon
2018-01-20 01:32:21 +01:00
Eugen Rochko
01421999ae Make text e-mails consistent with HTML ones in UserMailer (#6291)
* Make text e-mails consistent with HTML ones in UserMailer

* Fix UserMailer specs
2018-01-20 01:32:05 +01:00
David Yip
2ca965c704 Merge remote-tracking branch 'origin/master' into merge-upstream
Conflicts:
	app/javascript/styles/mastodon/components.scss
	app/javascript/styles/mastodon/modal.scss
2018-01-19 15:22:10 -06:00
David Yip
b8c6656ce9 Merge pull request #339 from ThibG/glitch-soc/features/timeline-tweaks
Do not discard statuses obtained via websocket when API request finishes
2018-01-19 15:17:20 -06:00
Thibaut Girka
1619efd735 Do not discard statuses obtained via websocket when API request finishes
This backports 0b888acfd4
2018-01-19 21:59:12 +01:00
ThibG
0b888acfd4 Do not throw away statuses obtained via websocket when API request finishes (#6302) 2018-01-19 21:48:00 +01:00
Yamagishi Kazutoshi
238de58e65 Change belongs_to_required_by_default to true (#5888) 2018-01-19 20:56:47 +01:00
Yamagishi Kazutoshi
7233ac07d2 Add support Ruby v2.5.0 (#6097) 2018-01-19 20:53:30 +01:00
ThibG
b1e03197fa Process mentions and reblogs even from resolved threads (#6299)
This may lead to out-of-order notifications, but this is better than not having
notifications at all.
2018-01-19 19:11:35 +01:00
Pierre Ozoux
7be53a10b0 Fix "tzinfo-data is not present" docker error (#6300)
when starting the container.
2018-01-19 19:11:19 +01:00
ThibG
a0de3222dd Retry delivering toots over ActivityPub for about 2 days (#6298)
Currently, Mastodon will retry delivering toots for a bit over 1 hour.
This is a very short timespan when considering private and direct toots, which
cannot be seen by the recipient at all after the delivery attempts have failed.

Ideally, private and direct toots should have a different number of retries,
but I do not know how to do that.
2018-01-19 15:49:48 +01:00
Eugen Rochko
540b3f37ae Replace drawer elephant graphic with a vector image (#6286)
* Replace drawer elephant graphic with a vector image

* Replace wave graphic with SVG

* Remove unused elephant graphic
2018-01-19 15:25:27 +01:00
Marcin Mikołajczak
852b48295f i18n: Update Polish translation (#6297) 2018-01-19 21:49:25 +09:00
Thibaut Girka
1287b2782b Display a warning when composing unlisted toots with something looking like a hashtag
This is a backport of b6af88192f to the glitch flavour.
2018-01-19 12:59:33 +01:00
David Yip
2b2de41bc7 Merge remote-tracking branch 'personal/merge/tootsuite/master' into gs-master 2018-01-18 23:40:37 -06:00
David Yip
26ecee79bf Merge pull request #332 from glitch-soc/merge-upstream
Merge in home feed regeneration changes from upstream
2018-01-18 22:00:11 -06:00
David Yip
ae369bceb3 Merge pull request #333 from glitch-soc/win95-update
Update win95 theme from cybrespace/mastodon
2018-01-18 13:35:24 -06:00
Eugen Rochko
9b3b40df66 Fix regeneration marker not expiring (#6290)
* Fix regeneration key not getting expired

* Add rake task to remove old regeneration markers
2018-01-18 20:29:56 +01:00
Eugen Rochko
d799921c75 Replace tutorial modal with welcome e-mail (#6273)
* Remove onboarding modal

* Welcome e-mail

* Send welcome e-mail after confirmation

* Remove obsolete translations
2018-01-18 19:17:25 +01:00
David Yip
766d6aac44 Strip trailing whitespace on win95.scss 2018-01-18 10:54:50 -06:00
David Yip
d3f64812a6 Merge remote-tracking branch 'cybrespace/theme_win95' into win95-update
Conflicts:
	app/javascript/styles/win95.scss
	config/themes.yml
2018-01-18 10:54:12 -06:00
David Yip
3896cd5d79 Use absolute paths for new working/not-found SVGs
This allows these component styles to be used in i.e. the win95 skin.
2018-01-18 10:25:54 -06:00
David Yip
bcd86404da Port 7badad7797 to glitch frontend 2018-01-18 10:25:37 -06:00
David Yip
1964a0f941 Merge remote-tracking branch 'origin/master' into merge-upstream 2018-01-18 09:17:58 -06:00
David Yip
e27eedbd08 Merge branch 'gs-master' into merge-upstream 2018-01-18 09:14:28 -06:00
David Yip
708ec07e27 Style fixes to make eslint happier, hopefully 2018-01-18 09:13:07 -06:00
Renato "Lond" Cerqueira
e56404be41 When must_be_following_dm is on, only notify if recipient dm'ed user (#6283)
* When must_be_following_dm is on, only notify if recipient dm'ed user
Currently, when must_be_following_dm is on, if a user sends a direct
message replying to any status from the recipient, the recipient gets a
notification. This should not be the case, as if the recipient posted
something publicly this can be used to spam their notifications.

* Refactor replied_to_status_is_direct_message?
Following suggestion in PR
2018-01-18 16:12:10 +01:00
David Yip
201e82686f Merge remote-tracking branch 'origin/master' into merge-upstream
Conflicts:
      app/javascript/styles/mastodon/components.scss
2018-01-17 18:37:09 -06:00
Eugen Rochko
7badad7797 Fix home regeneration (#6251)
* Fix regeneration marker not being removed after completion

* Return HTTP 206 from /api/v1/timelines/home if regeneration in progress
Prioritize RegenerationWorker by putting it into default queue

* Display loading indicator and poll home timeline while it regenerates

* Add graphic to regeneration message

* Make "not found" indicator consistent with home regeneration
2018-01-17 23:56:03 +01:00
Jenkins
fe5c1f0803 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-17 22:17:14 +00:00
Renato "Lond" Cerqueira
59797ee233 Weblate translations (#6284)
* Translated using Weblate (French)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/fr/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ca/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (45 of 45 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/pt_BR/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (529 of 529 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (45 of 45 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/nl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (529 of 529 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (45 of 45 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/gl/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (527 of 529 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.8% (528 of 529 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (45 of 45 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/sk/

* Translated using Weblate (Slovak)

Currently translated at 37.2% (197 of 529 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (260 of 260 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sk/

* Translated using Weblate (Russian)

Currently translated at 99.0% (526 of 531 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ru/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (45 of 45 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ca/

* Translated using Weblate (Catalan)

Currently translated at 99.8% (530 of 531 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Japanese)

Currently translated at 92.8% (52 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/

* Translated using Weblate (Japanese)

Currently translated at 75.8% (47 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ja/

* Translated using Weblate (Polish)

Currently translated at 77.4% (48 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/pl/

* Translated using Weblate (Slovak)

Currently translated at 38.3% (204 of 532 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ja/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ca/

* Translated using Weblate (Polish)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pl/

* Translated using Weblate (Russian)

Currently translated at 96.8% (525 of 542 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ru/

* Translated using Weblate (Japanese)

Currently translated at 99.0% (537 of 542 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Polish)

Currently translated at 99.8% (541 of 542 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/

* Translated using Weblate (Japanese)

Currently translated at 99.0% (538 of 543 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Dutch)

Currently translated at 97.4% (529 of 543 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Normalize translations
Ran i18n-tasks normalize && yarn manage:translations
2018-01-18 07:00:23 +09:00
Jenkins
aa2bf07281 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-17 16:17:10 +00:00
りんすき
fbe7756da6 implement web share target (#6278)
* web share target

* fix

* fix
2018-01-17 17:08:10 +01:00
Yamagishi Kazutoshi
0a103c7749 Upgrade ESLint to version 4.x (#6276) 2018-01-17 16:57:15 +01:00
SerCom_KC
fb16c37d2a Update Simplified Chinese translations (#6280)
* i18n: (zh-CN) Update translation for #6252

* e-mail -> email

* i18n: (zh-CN) Update translations for #6256

* i18n: (zh-CN) Minor Improvements

* i18n: (zh-CN) Update translations for #6263

* i18n: (zh-CN) Update translations for #6279
2018-01-17 16:47:25 +01:00
Eugen Rochko
6f244ba82c Use better reblog icon and improve contrast in HTML e-mails (#6272) 2018-01-17 14:12:59 +01:00
Mike Burns
ea75ae2d1f Use be_within instead of eq for a to_f test match (#6275)
Floating point values are notoriously hard to pin down, so use the
`be_within` matcher to verify the approximate value.
2018-01-17 12:45:09 +01:00
Jeong Arm
acb982fc66 Korean translate (#6277)
* Translate Korean

* Translate Korean #6263
2018-01-17 12:42:11 +01:00
mayaeh
eed7484cd6 Change mailer image url (#6279)
* Change image URL in mailer to full path

* Add application_mailer.view_profile localization.
2018-01-17 12:41:24 +01:00
Eugen Rochko
02194838dd HTML e-mails for NotificationMailer (#6263)
* HTML e-mails for NotificationMailer (except digest)

* Add HTML template for digest

* Fix build
2018-01-16 20:20:15 +01:00
SerCom_KC
3323b4173e Change disclaimer in email according to #5817 (#6266) 2018-01-16 06:44:57 +01:00
Yamagishi Kazutoshi
9a28052e92 Change image URL in mailer to full path (#6264) 2018-01-16 06:26:46 +01:00
kibi!
60ff086960 Fixed improper mailer import 2018-01-15 21:23:26 -08:00
kibi!
cda845ae0e Fix mailer to use webpack core 2018-01-15 21:17:03 -08:00
Yamagishi Kazutoshi
e6fd4bea35 Stop duplicate CI with Pull Request (#6265)
see also https://blog.travis-ci.com/2012-08-13-build-workflow-around-pull-requests
2018-01-16 06:15:28 +01:00
David Yip
89a9d629f7 Merge remote-tracking branch 'origin/master' into gs-master
Conflicts:
	Gemfile.lock
2018-01-15 22:17:48 -06:00
Eugen Rochko
5276c0a090 HTML e-mails for UserMailer (#6256)
- premailer gem to turn CSS into inline styles automatically
- rework UserMailer templates
- reword UserMailer templates
2018-01-16 03:29:11 +01:00
Eugen Rochko
7861c5f108 Surround mid-text display names with bdi tags (#6257)
* Fix #1095 - Surround mid-text display names with bdi tags

* Update jest snapshot
2018-01-15 18:55:10 +01:00
Eugen Rochko
3987bd18a4 Fix #6128 - Display unfollow button even if account moved (#6258) 2018-01-15 18:42:15 +01:00
Jenkins
0c7dc6c781 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-15 06:17:15 +00:00
puckipedia
74c1c9ec01 Allow attributedTo in a status to be an embedded object (#6238) 2018-01-15 06:51:46 +01:00
Patrick Figel
537d2939b1 Suppress CSRF token warnings (#6240)
CSRF token checking was enabled for API controllers in #6223,
producing "Can't verify CSRF token authenticity" log spam. This
disables logging of failed CSRF checks.

This also changes the protection strategy for
PushSubscriptionsController to use exceptions, making it consistent
with other controllers that use sessions.
2018-01-15 06:51:23 +01:00
neetshin
2091ae92be Make columns-area unscrollable when modal opened (#6241)
* Add aria-autocomplete='list' in Textaria

ref: https://www.w3.org/TR/wai-aria-1.1/#aria-autocomplete

* Make detect empty string brefore assign upload description

* Change code elements in keyboard-shortcuts component to kbd

* Add validation for onMuteNotifications

* Make columns-area unscrollable when modal opend

* Make columns-area unscrollable when modal opened
2018-01-15 06:51:00 +01:00
Jeong Arm
dcc614f869 Add some browsers (#6246)
Related: #6165
2018-01-15 06:50:29 +01:00
Eugen Rochko
ed867eca9d Move e-mail digest task to sidekiq, reduce workload, improve hint (#6252) 2018-01-15 04:34:28 +01:00
Gô Shoemake
08e4c78e78 Fix column headers accessibility (#6199)
* Fix accessibility of column headers

As a screen reader user new to Mastodon, I encountered the following issues with the column headers as designed:
 * Jumping between them was difficult. FOr instance, passing my home timeline to reach notification settings was difficult to impossible, especially considering infinite scrolling.
 * There doesn't appear to be any means for triggering the control via the keyboard. the `titleClick` handler only responds to mouse clicks.
 * I didn't even realize there was a Settings toggle until I made this change.

Thanks for using ARIA in your designs. It's a huge help. But adding a `button` role doesn't add keyboard handling and other button behavior. Also, because the role was on the heading container, it obscured the controls within the container itself. This fix resolve that. It also exposes the headings as headings rather than buttons, enabling skipping columns by using screen readers' heading navigation commands.

Since I myself am blind, if this fix requires additional visual styling, I'd like help applying that so it can be merged. I'd consider it an essential accessibility fix for my and other blind users' existence on the platform. Thanks!

* Styling fixes

* Fixed overflow issue
2018-01-15 04:33:06 +01:00
kibi!
8ee6ed358f Increase priority of favstar gold 2018-01-14 16:05:44 -08:00
kibi!
1ee0e91fb6 Fixes to notification styling 2018-01-14 15:48:54 -08:00
kibi!
b9f4896830 Fixes to drawer and CWs 2018-01-14 15:31:00 -08:00
beatrix
cd6674606f Merge pull request #329 from KnzkDev/scss-refactor
SCSS Refactor
2018-01-14 17:48:02 -05:00
beatrix
39f231f3da Merge pull request #328 from glitch-soc/feature/glitchy-elephant-friend
Add glitchy elephant friend
2018-01-14 17:40:27 -05:00
kibi!
0fb3bd09e9 Oops this should have been a part of last commit 2018-01-14 14:21:41 -08:00
kibi!
1fa03e026a Fixes onboarding modal (#318) 2018-01-14 14:13:24 -08:00
kibi!
a5931e1f48 Fixed autocollapse detection 2018-01-14 13:29:51 -08:00
David Yip
c2669f93d0 Fix eslint errors. #294. 2018-01-14 05:56:43 -06:00
David Yip
756a9cd139 Run slightly less afoul of accessibility checks. #294.
This makes the clickable element an interactive element, but doesn't add
alt text and makes it not-obvious that you can click the elefriend.  On
one hand, this goes against good practice; on the other hand, it's not
*supposed* to be obvious that you can click the elefriend.
2018-01-14 05:07:08 -06:00
David Yip
e64cc311dd Introduce a bit of luck into the elefriend selection. #294. 2018-01-14 04:46:21 -06:00
David Yip
53cbc9933e Add wave background; blend elefriend in a bit more. #294. 2018-01-14 04:00:21 -06:00
David Yip
cd1377de7f Bind click event to elefriend cycling. #294. 2018-01-14 03:35:25 -06:00
David Yip
1dbb6b5e08 Set glitchy elephant friend variant in initial state. #294.
Also lay some groundwork needed to interactively change the
glitchfriend.

The codebase uses "elefriend" because it's shorter and didn't require me
to realign the actions in actions/compose.js.  Same idea, though.
2018-01-14 03:06:11 -06:00
David Yip
1e0b707018 Add the shrinking glitchy elefriend. #294. 2018-01-13 21:27:39 -06:00
David Yip
835eec8a4c Prototype out glitchy elephant friend. #294.
Glitched elephant friends kindly provided by @ncls7615.
2018-01-13 21:27:14 -06:00
ncls7615
f9b08e2142 Fix collapsed toot style 2018-01-14 11:13:55 +09:00
ncls7615
1ebe66b78c Fix un-fullwidth media style 2018-01-14 11:07:32 +09:00
ncls7615
be2bac05aa Fix media detailed style 2018-01-14 10:38:51 +09:00
ncls7615
b5476f6c75 Fix confirmation modal bug 2018-01-14 09:51:01 +09:00
ncls7615
bc0524834e Fix letterbox bug 2018-01-14 09:25:35 +09:00
ncls7615
6ecf81cacf Improve scss refactor 5 2018-01-14 09:17:38 +09:00
ncls7615
64425dbb77 Improve scss refactor 4 2018-01-14 09:12:10 +09:00
ncls7615
a16d885ac9 Improve scss refactor 3 2018-01-14 08:47:59 +09:00
ncls7615
5f35b39f6f Improve scss refactor 2 2018-01-14 07:48:18 +09:00
ncls7615
bb2ca23839 Improve scss refactor 1 2018-01-14 07:18:50 +09:00
ncls7615
a9be680807 Fix padded-blocks error 2018-01-14 04:12:41 +09:00
ncls7615
6a73c8c8a2 Initial scss refactor 2018-01-14 03:41:20 +09:00
beatrix
9526aababc Merge pull request #324 from glitch-soc/load-public-on-admin
Load public javascripts on admin pages
2018-01-12 23:52:41 -05:00
Gô Shoemake
3a0b47982d load public.js on vanilla#admin 2018-01-12 18:52:53 -08:00
Gô Shoemake
9b9b7fa005 Load packs/public.js for glitch#admin 2018-01-12 18:51:52 -08:00
David Yip
0210e59759 Merge remote-tracking branch 'remotes/origin/fix-column-headers-accessibility' 2018-01-12 20:10:58 -06:00
beatrix
4773481a90 Merge pull request #322 from m4sk1n/glitch-soc-pl
i18n: Update Polish translation
2018-01-12 18:16:31 -05:00
Marcin Mikołajczak
ee7217bc94 i18n: Update Polish translation
Signed-off-by: Marcin Mikołajczak <me@m4sk.in>
2018-01-12 23:08:51 +01:00
David Yip
e2ce628724 Merge pull request #321 from KnzkDev/fix/dropdown-text
Fix dropdown item text
2018-01-12 09:09:10 -06:00
ncls7615
cf5789146b Fix dropdown text 2018-01-12 23:18:25 +09:00
Jenkins
9fa79bc317 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-12 02:17:15 +00:00
Jeong Arm
704053d221 Handle sessions that can't be translated (#6245)
* Handle sessions that can't be translated

This commit fixes #6165.

* Fix typo
2018-01-12 02:44:28 +01:00
David Yip
8c08c852bc Merge pull request #320 from glitch-soc/dockerfile-stack-space
dockerfile: Give more stack space to /sbin/tini
2018-01-11 04:25:26 -06:00
David Yip
f13ebd02c9 dockerfile: Give more stack space to /sbin/tini.
/sbin/tini ends up running the Sass compiler, which seems to need a
larger thread stack size than the musl default.  This patch incorporates
a fix from
https://raw.githubusercontent.com/jubel-han/dockerfiles/master/common/stack-fix.c,
as described in https://github.com/sass/node-sass/issues/2031.
2018-01-11 04:09:16 -06:00
David Yip
26f054253c Merge pull request #319 from glitch-soc/317-attempt-to-switch-flavors-raises-actioncontroller-parametermissing
Fix #317: Attempting to switch flavors raises ActionController::ParameterMissing
2018-01-10 12:49:43 -06:00
David Yip
395e64e858 Thank you, Officer Murphy 2018-01-10 12:10:43 -06:00
David Yip
514db316f7 The flavour parameter is unused, so omit it (#317) 2018-01-10 12:09:42 -06:00
David Yip
6fcb870d96 Allow for user object to be empty. Fixes #317.
If a flavour has only one skin, the skin selector will be omitted.  This
omits the user[setting_skin] field, and because that's the only
user[...] field on the page, the entire user object will not be present
in the request handler's params object.

This commit accounts for that scenario by avoiding params.require(:user)
and instead picking out what we need from the params hash.
2018-01-10 12:09:42 -06:00
David Yip
3ce1385b25 Add examples based on errors seen in #317 2018-01-10 12:09:41 -06:00
David Yip
095a00ef3d Merge pull request #316 from glitch-soc/fix-async
Fix glitch async chunks
2018-01-09 18:30:45 -06:00
kibigo!
35be02f21d Renamed glitch async chunks 2018-01-09 16:26:14 -08:00
David Yip
622c8fdb75 Merge pull request #314 from chriswmartin/merge-vanilla-updates-2
Merge vanilla updates into glitch - round 2
2018-01-09 17:01:23 -06:00
David Yip
991371af5f Merge remote-tracking branch 'origin/master' into merge-upstream
Conflicts:
	db/schema.rb
2018-01-09 14:16:45 -06:00
Eugen Rochko
35b84985a8 Skip ActivityPub Announces of non-public objects (#6230)
* Skip ActivityPub Announces of non-public objects

* Skip OStatus reblogs of non-public statuses
2018-01-09 19:35:10 +01:00
Eugen Rochko
d41f0b66cc Fix #6204: Use content warning for page title when present (#6231) 2018-01-09 19:34:58 +01:00
cwm
aef4b1af66 Refactor /api/web APIs to use the centralized axios instance (tootsuite pr #6223) 2018-01-09 10:51:14 -06:00
cwm
f43c2f12f3 Fix overflowing audit logs (tootsuite pr #6184) 2018-01-09 10:40:14 -06:00
cwm
9bdbe66316 Set background to the navigation of Getting Started column (tootsuite pr #6163) 2018-01-09 10:37:27 -06:00
cwm
b535f24fe5 Use const instead of let for constant (tootsuite pr #6106) 2018-01-09 10:35:36 -06:00
Eugen Rochko
921b781909 Increase rate limit on protected paths (#6229)
Previously each protected path had a separate rate limit. Now they're all in the same bucket, so people are more likely to hit one with register->login. Increasing to 25 per 5 minutes should be fine.
2018-01-09 17:07:54 +01:00
cwm
0d4dcb5fb2 change key to path in timeline settings containers 2018-01-09 09:58:40 -06:00
cwm
6d1c325167 Rename key to path in actions and reducers for settings (tootsuite pr #6105) 2018-01-09 09:48:14 -06:00
cwm
7f4374d97d Fix newlines-to-spaces functionality (tootsuite pr #6158) 2018-01-09 08:52:14 -06:00
cwm
8a0e4bb9a4 Hide moved account's follow button in search result (tootsuite pr #5913) 2018-01-09 08:50:55 -06:00
cwm
5963630c63 Prevent duplicate load of favourites (tootsuite pr #5931) 2018-01-09 08:46:35 -06:00
takayamaki
6f5c0afe93 add index on statuses for /api/v1/accounts/:account_id/statuses (#6202) 2018-01-09 15:00:19 +01:00
Evgeny Petrov
eec6095e02 Russian language update (#6227)
* Russian language update

New strings translated, except "terms" key.
Should this be translated? Can it be changed by end user?

* Removed double quotes in "terms" -> "title" key
2018-01-09 14:59:19 +01:00
Jenkins
e780d5951b Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-09 12:17:16 +00:00
Renato "Lond" Cerqueira
9f04b0d4b1 Weblate translations (#6228)
* Translated using Weblate (Catalan)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (523 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (525 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Arabic)

Currently translated at 80.3% (45 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Arabic)

Currently translated at 83.9% (47 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Persian)

Currently translated at 87.6% (460 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fa/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt/

* Translated using Weblate (Catalan)

Currently translated at 99.2% (521 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pt/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pt/

* Translated using Weblate (Portuguese)

Currently translated at 47.2% (248 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Portuguese)

Currently translated at 48.0% (252 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Persian)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/fa/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (525 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Japanese)

Currently translated at 99.0% (520 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Persian)

Currently translated at 90.4% (475 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fa/

* Translated using Weblate (Polish)

Currently translated at 99.8% (524 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pl/

* Translated using Weblate (Persian)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fa/

* Translated using Weblate (Persian)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/fa/

* Translated using Weblate (Polish)

Currently translated at 99.8% (524 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/

* Translated using Weblate (Persian)

Currently translated at 99.8% (524 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fa/

* Translated using Weblate (Portuguese)

Currently translated at 48.3% (254 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Portuguese)

Currently translated at 56.5% (297 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Portuguese)

Currently translated at 59.4% (312 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/nl/

* Translated using Weblate (Arabic)

Currently translated at 91.0% (51 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Portuguese)

Currently translated at 59.6% (313 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/

* Translated using Weblate (Japanese)

Currently translated at 99.6% (523 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Portuguese)

Currently translated at 67.6% (355 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Arabic)

Currently translated at 98.2% (55 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/

* Translated using Weblate (Galician)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Arabic)

Currently translated at 51.1% (22 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ar/

* Translated using Weblate (Galician)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/gl/

* Added translation using Weblate (Galician)

* Added translation using Weblate (Galician)

* Translated using Weblate (Galician)

Currently translated at 50.0% (1 of 2 strings)

Translation: Mastodon/Activerecord
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/activerecord/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (43 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/gl/

* Added translation using Weblate (Galician)

* Translated using Weblate (Galician)

Currently translated at 24.0% (126 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Added translation using Weblate (Portuguese)

* Translated using Weblate (Arabic)

Currently translated at 55.2% (290 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/

* Translated using Weblate (Galician)

Currently translated at 42.6% (224 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Translated using Weblate (Portuguese)

Currently translated at 80.9% (425 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt/

* Translated using Weblate (Arabic)

Currently translated at 62.7% (27 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ar/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pt/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (2 of 2 strings)

Translation: Mastodon/Activerecord
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/activerecord/pt/

* Translated using Weblate (Portuguese)

Currently translated at 81.3% (427 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Galician)

Currently translated at 100.0% (2 of 2 strings)

Translation: Mastodon/Activerecord
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/activerecord/gl/

* Translated using Weblate (Galician)

Currently translated at 99.8% (524 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Translated using Weblate (Portuguese)

Currently translated at 93.7% (492 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Portuguese)

Currently translated at 95.4% (501 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (525 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Galician)

Currently translated at 99.8% (524 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (525 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Portuguese)

Currently translated at 96.0% (504 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Arabic)

Currently translated at 69.7% (30 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ar/

* Translated using Weblate (Portuguese)

Currently translated at 97.9% (514 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (523 of 525 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Normalize translations
Ran i18n-tasks normalize && yarn manage:translations

* Add back simple_forms removed by weblate
Weblate for some reason removed this files, add back to their previous
versions

* Remove linebreak on doorkeeper.pt.yml
PR review
2018-01-09 20:30:28 +09:00
David Yip
ce7f4aef16 Merge remote-tracking branch 'personal/merge/tootsuite/master' into gs-master 2018-01-08 23:20:39 -06:00
David Yip
ec0bdd6c1a Merge pull request #312 from glitch-soc/tag-sensitive-imgs
Show SENSITIVE tag on sensitive images (#267)
2018-01-08 23:19:14 -06:00
David Yip
df04da098a Merge pull request #311 from glitch-soc/dont-show-solo-skins
Don't show skins selector when there's only one (#256)
2018-01-08 23:18:30 -06:00
Jenkins
7c719c567c Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-09 05:17:13 +00:00
David Yip
488381ae2f Merge pull request #310 from glitch-soc/fix-thread-inheritance
Threads now inherit privacy directly (#309)
2018-01-08 23:15:58 -06:00
kibigo!
60433d03f5 Add missing comma 2018-01-08 18:38:28 -08:00
kibigo!
5d2ef7a616 Show SENSITIVE tag on sensitive images (#267) 2018-01-08 18:25:29 -08:00
kibigo!
44792de49a Don't show skins selector when there's only one (#256) 2018-01-08 16:45:32 -08:00
kibigo!
824a790e63 Threads now inherit privacy directly (#309) 2018-01-08 16:35:01 -08:00
puckipedia
628358aeea Add the author of a status to cc if reblogged (#6226)
This makes slightly more sense, and ensures that the author of a post is always referenced in the audience (which some servers might rely on). And the announce is POSTed to the author's inbox anyways.
2018-01-09 00:47:43 +01:00
Andrew
fe6663ef50 Minor style fixes 2018-01-08 11:49:08 -08:00
nightpool
c235711ffe Refactor /api/web APIs to use the centralized axios instance (#6223)
Also adds the ability to decouple the centralized axios logic from the
state dispatcher
2018-01-08 20:01:33 +01:00
Eugen Rochko
ff6ca8bdc6 Bump version to 2.1.3 2018-01-08 19:15:31 +01:00
beatrix
90e568413b Merge pull request #308 from KnzkDev/fix/list-editor
Fix list editor design
2018-01-08 13:08:11 -05:00
ncls7615
ef0b7d1e76 fix list editor scss 2018-01-09 02:50:24 +09:00
David Yip
65986b6f0b Merge remote-tracking branch 'personal/merge/tootsuite/master' into gs-master 2018-01-08 09:48:42 -06:00
David Yip
2dc4fbbd1a When pulling out max_toot_chars, handle nulls
flavours/glitch/util/initial_state is used in places where we want to
exhibit different behavior based on user preferences.  This means that
it's used in places where no preference is defined, i.e. on an
unauthenticated access.  All values exported from that module must
therefore expect that case; previously, the max chars value didn't.

Addresses #306.
2018-01-08 09:45:59 -06:00
Jenkins
f839ac694c Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-08 10:17:15 +00:00
Eugen Rochko
dbda87c31f Revert #5772 (#6221) 2018-01-08 10:57:52 +01:00
Jenkins
722b3f567f Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-08 04:17:11 +00:00
Eugen Rochko
e4a241abef Fix bad URL schemes being accepted (#6219)
* Fix actors accepting invalid URI schemes or different host between URI and URL

* Fix statuses accepting invalid URI scheme or different host to actor

* Adjust tests to new requirements

* Improve readability of mismatching_origin?/invalid_origin? methods
2018-01-08 05:00:23 +01:00
Eugen Rochko
93555182c3 Do not display elephant friend in single-column layout (#6222) 2018-01-08 03:50:53 +01:00
puckipedia
0eff42d688 Move Article from supported to converted types (#6218) 2018-01-08 00:21:14 +01:00
David Yip
f7c4d4464b Merge remote-tracking branch 'personal/merge/tootsuite/master' into gs-master 2018-01-07 13:30:52 -06:00
David Yip
70c99a9f34 Use error pack when rendering error pages. Fixes #305. 2018-01-07 13:30:17 -06:00
Jenkins
c2e1bfd9ae Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-07 15:17:13 +00:00
Yamagishi Kazutoshi
1d92b90be9 Fix force_ssl conditional (#6201) 2018-01-07 15:19:23 +01:00
Yamagishi Kazutoshi
da809f9eec Fix unintended cache (#6214) 2018-01-07 15:12:59 +01:00
SerCom_KC
c4d36d024c Update Simplified Chinese translations (#6215)
* i18n: (zh-CN) Add translations of #6125

* i18n: (zh-CN) Add translations of #6132

* i18n: (zh-CN) Add translations of #6099

* i18n: (zh-CN) Add translations of #6071

* i18n: (zh-CN) Improve translations
2018-01-07 17:32:50 +09:00
David Yip
5083311d64 Merge remote-tracking branch 'ykzts/fix-unintended-cache' into gs-master 2018-01-07 00:32:24 -06:00
Yamagishi Kazutoshi
2af307bce4 Fix unintended cache 2018-01-07 14:59:12 +09:00
Andrew
56312df73b Z indexes and colors 2018-01-06 18:38:19 -08:00
Andrew
7a93acd803 To 2.1.2 2018-01-06 18:25:19 -08:00
Jenkins
bcbdd4f88d Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-07 02:17:10 +00:00
Jeong Arm
9e97fbf0af Translate Korean (#6212) 2018-01-07 11:13:42 +09:00
kibigo!
b5874c1428 Fixes to search dropdown 2018-01-06 15:34:01 -08:00
beatrix
61ef8d643e fix typo in vanilla names.yml 2018-01-06 16:49:53 -05:00
Ondřej Hruška
9f29fd31ba fixed ctrl enter 2018-01-06 19:58:04 +01:00
Ondřej Hruška
53caab0c0b Fix the always-threaded bug 2018-01-06 19:55:53 +01:00
beatrix-bitrot
b75a1ce326 tighten csp 2018-01-06 18:49:03 +00:00
beatrix
d442cfa65c Merge pull request #303 from KnzkDev/ja-for-thread-mode
Update ja.js for #296
2018-01-06 12:06:17 -05:00
ncls7615
f5a4201ad8 Update ja.js 2018-01-07 01:51:49 +09:00
beatrix
a251c42192 Merge pull request #296 from glitch-soc/thread-mode
Threaded mode~
2018-01-06 11:28:36 -05:00
beatrix
2ec9a75a1d Merge pull request #302 from KnzkDev/fix/search-popout
Fix search popout
2018-01-06 11:25:59 -05:00
beatrix
fa92e88fb2 appease eslint 2018-01-06 10:30:49 -05:00
ncls7615
da98c33161 Fix search popout 2018-01-06 21:50:11 +09:00
David Yip
2eed4ace11 Read max_toot_chars from root object. Fixes #297.
max_toot_chars is present in the root of the initial state object.
(Previously, we were trying to read it from the meta child object.)
2018-01-06 03:01:11 -06:00
kibigo!
c71d848855 my global .gitignore excluded this file ;_; 2018-01-05 21:40:02 -08:00
kibigo!
e4bc013d6f Threaded mode~ 2018-01-05 21:16:43 -08:00
kibigo!
6932b464e6 Fixed improper dropdown func binding for #293 + toot button spacing 2018-01-05 21:02:53 -08:00
kibigo!
ad10a80a99 Styling and autosuggest fixes for #293 2018-01-05 20:43:16 -08:00
kibigo!
8bf9d9362a Fixes composer mounting issue with #293 2018-01-05 18:30:06 -08:00
Andrew
077f2e600c Win95 to 2.1 2018-01-05 16:22:58 -08:00
Andrew
39c0b6ceb3 Updates and fixes to win95 theme 2018-01-05 16:22:58 -08:00
Andrew
ed574fbc09 Theme: Windows 95 2018-01-05 16:22:58 -08:00
David Yip
03aeab857f Merge remote-tracking branch 'personal/merge/tootsuite/master' into gs-master 2018-01-05 17:31:56 -06:00
beatrix
f441770e50 Merge pull request #290 from chriswmartin/web-push-updates
Web push updates
2018-01-05 18:29:57 -05:00
beatrix
b4e667f86b Merge pull request #295 from chriswmartin/getting-started-key-fix
unique ColumnLink keys in getting_started
2018-01-05 18:29:40 -05:00
beatrix
faf20eeaa4 Merge pull request #293 from glitch-soc/compose-refactor
Compose refactor
2018-01-05 18:29:08 -05:00
Jenkins
f6adb409fd Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-05 22:17:12 +00:00
ThibG
10f6793fd0 Fix PuSH workers (#6200) 2018-01-05 23:04:35 +01:00
ThibG
a594139115 When fetching an ActivityPub-enabled status, do not re-request it as text/html (#6196) 2018-01-05 22:42:50 +01:00
TheKinrar
95bd85d9e8 Represent numbers by strings in instance activity API (#6198)
Fixes #6197.
2018-01-05 22:38:33 +01:00
kibigo!
ac686d5a5d Fixed overflow issue 2018-01-05 13:34:21 -08:00
kibigo!
ec620ae486 Styling fixes 2018-01-05 12:41:15 -08:00
Naoki Kosaka
8d51ce4290 Fix enforce HTTPS in production. (#6180) 2018-01-05 20:04:22 +01:00
beatrix
f41b33eb01 Merge pull request #243 from m4sk1n/glitch-pl
i18n: 🇵🇱
2018-01-05 12:36:53 -05:00
cwm
9fc08e4861 add key to lists div 2018-01-05 09:00:48 -06:00
cwm
6236577734 change how list ColumnLink keys are determined 2018-01-05 08:12:34 -06:00
Quenty31
06636c6eca l10n Occitan language: mailer update (#6193)
* Create email_changed.oc.html.erb

* Create email_changed.oc.text.erb

* Update email_changed.oc.html.erb

* Update email_changed.oc.html.erb

* Create reconfirmation_instructions.oc.html.erb

* Create reconfirmation_instructions.oc.text.erb

* Update confirmation_instructions.oc.html.erb

* Update confirmation_instructions.oc.text.erb

* Update confirmation_instructions.oc.html.erb

* Update reconfirmation_instructions.oc.html.erb

* Update reconfirmation_instructions.oc.text.erb

* Update reconfirmation_instructions.oc.html.erb
2018-01-05 18:59:43 +09:00
kibigo!
d7ce339c2e WIP <Compose> Refactor; Fin~ 2018-01-04 21:17:30 -08:00
Eugen Rochko
e9822a4e4e Bump version to 2.1.2 2018-01-05 04:52:06 +01:00
Yamagishi Kazutoshi
9a61b0ef22 Fix RFC 5646 Regular Expression (#6190) 2018-01-05 04:43:50 +01:00
kibigo!
42f50049ff WIP <Compose> Refactor; 1000 tiny edits 2018-01-04 18:33:13 -08:00
kibigo!
b4a3792201 WIP <Compose> Refactor; <ActionsModal>; dropdowns 2018-01-04 18:31:00 -08:00
kibigo!
083170bec7 WIP <Compose> Refactor; SCSS ed. 2018-01-04 18:23:46 -08:00
kibigo!
8713659dff WIP <Compose> Refactor; <OnboardingModal> ed. 2018-01-04 18:21:59 -08:00
kibigo!
3c29f57404 WIP <Compose> Refactor; <Drawer> ed. 2018-01-04 18:21:59 -08:00
kibigo!
924ffe81d4 WIPgit status <Compose> Refactor; <Composer> ed. 2018-01-04 18:21:59 -08:00
Jenkins
c69a23ae46 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-04 23:17:11 +00:00
Branko Kokanovic
d872902997 Small translation fixes for Serbian (and sr@Latn too) (#6188) 2018-01-05 00:16:06 +01:00
Patrick Figel
5ec25ff3e1 Fix email confirmation link not updating email (#6187)
A change introduced in #6125 prevents
`Devise::Models::Confirmable#confirm` from being called for existing
users, which in turn leads to `email` not being set to
`unconfirmed_email`, breaking email updates. This also adds a test
that would've caught this issue.
2018-01-05 00:15:35 +01:00
Lynx Kotoura
49e296e1b0 Fix overflowing audit logs (#6184) 2018-01-04 19:38:46 +01:00
unarist
7347d4f8bb Use disable_ddl_transaction! to prevent warnings on migration (#6183)
Migration is wrapped by transaction, so manual `commit_db_transaction` without transaction restarting causes "there is no transaction in progress" warnings. We should use `disable_ddl_transaction!` instead, if we can omit transaction completely.
2018-01-04 19:38:29 +01:00
Nolan Darilek
3b016342c6 Fix accessibility of column headers
As a screen reader user new to Mastodon, I encountered the following issues with the column headers as designed:
 * Jumping between them was difficult. FOr instance, passing my home timeline to reach notification settings was difficult to impossible, especially considering infinite scrolling.
 * There doesn't appear to be any means for triggering the control via the keyboard. the `titleClick` handler only responds to mouse clicks.
 * I didn't even realize there was a Settings toggle until I made this change.

Thanks for using ARIA in your designs. It's a huge help. But adding a `button` role doesn't add keyboard handling and other button behavior. Also, because the role was on the heading container, it obscured the controls within the container itself. This fix resolve that. It also exposes the headings as headings rather than buttons, enabling skipping columns by using screen readers' heading navigation commands.

Since I myself am blind, if this fix requires additional visual styling, I'd like help applying that so it can be merged. I'd consider it an essential accessibility fix for my and other blind users' existence on the platform. Thanks!
2018-01-04 10:25:26 -06:00
Eugen Rochko
7571c37c99 Bump version to 2.1.1 (#6164) 2018-01-04 16:40:26 +01:00
Yamagishi Kazutoshi
3c18964256 Fallback default thumbnail in instance status API (#6177) 2018-01-04 15:36:55 +01:00
Marcin Mikołajczak
c61dd918a2 i18n: Update Polish translation (#6176)
Signed-off-by: Marcin Mikołajczak <me@m4sk.in>
2018-01-04 23:15:29 +09:00
Marcin Mikołajczak
0f69a90588 i18n: Update Polish translation
Signed-off-by: Marcin Mikołajczak <me@m4sk.in>
2018-01-04 14:42:58 +01:00
Eugen Rochko
02ba03d6db Send one Delete of Actor in ActivityPub when account is suspended (#6172) 2018-01-04 14:40:49 +01:00
ThibG
3bee0996c5 Make sure private toots remain private and do not end up in HTTP caches (#6175) 2018-01-04 14:39:38 +01:00
muan
89daeb43a8 Improve Traditional Chinese translation (#6166)
* Improve Traditional Chinese translations

* Sort alphabetically
2018-01-04 05:00:50 +01:00
Eugen Rochko
7d4f4f9aab Fix FetchAtomService not finding alternatives if there's a Link header (#6170)
without them, such as is the case with GNU social

Fixes the ability to find GNU social accounts via URL in search and
when using remote follow function
2018-01-04 04:56:04 +01:00
Akihiko Odaki
256c2b1de0 Rearrange items in Getting Started navigation (#6126)
Though the subsections are representing features such as navigation and
settings, they are categorized by the ways how they are implemented
(internal navigation or external links.) They are irrelevant and some
arrangements were confusing because of that. (It is nonsense that instance
information is in settings subsection, for example.)

This fixes the issue by rearranging.
2018-01-04 10:56:54 +09:00
Eugen Rochko
02e3e1ec09 Fix nil error in log_target_from_history helper (#6173) 2018-01-04 10:56:23 +09:00
Eugen Rochko
ff924f95bb Fix OpenSSL dependency in ostatus2 (#6174) 2018-01-04 10:56:00 +09:00
Eugen Rochko
c10f4bdb03 Cache JSON of immutable ActivityPub representations (#6171) 2018-01-04 01:21:38 +01:00
Jenkins
fc884d015a Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-03 20:17:13 +00:00
Quenty31
d907d4352e l10n OC language (#6169)
* new strings: hashtag+unlisted, mute, block

* Add confirmation step for email changes

* Add more instance stats APIs
2018-01-03 21:05:54 +01:00
ThibG
a8b51124ba Don't normalize URLs in toots (#6134)
* Don't normalize URLs in toots

URL normalization is ill-defined and may cause certain links to break.

* Change specs since we are not normalizing user-provided URLs
2018-01-03 20:51:33 +01:00
Akihiko Odaki
161c72d66d Allow to dereference Follow object for ActivityPub (#5772)
* Allow to dereference Follow object for ActivityPub

* Accept IRI as object representation for Accept activity
2018-01-03 18:08:57 +01:00
Marcin Mikołajczak
53d99ebf4f i18n: Update Polish translation (#6168)
Signed-off-by: Marcin Mikołajczak <me@m4sk.in>
2018-01-03 22:45:24 +09:00
Yamagishi Kazutoshi
1001922156 Add Japanese translations #5997, #6003, #6004, #6071, #6099, #6125 and #6132 (#6167)
* yarn manage:translations

* Add Japanese translation for #5997

* Add Japanese translation for #6003

* Add Japanese translation for #6004

* Add Japanese translation for #6071

* Add Japanese translation for #6099

* Add Japanese translation for #6125

* Add Japanese translation for #6132
2018-01-03 21:00:39 +09:00
Jenkins
933840bebf Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2018-01-03 04:17:11 +00:00
ThibG
99f962ba73 Allow HTTP caching of json view of public statuses (#6115)
* Allow HTTP caching of json view of public statuses

HTML views are not cached as they can contain private statuses as well

* Disable session cookies for ActivityPub json rendering of public toots
2018-01-03 04:57:57 +01:00
Akihiko Odaki
2471796d75 Set background to the navigation of Getting Started column (#6163)
The background of the navigation matters because its scrollbar is
transparent.
2018-01-03 04:39:14 +01:00
puckipedia
545095b3ce [!] Sanitize incoming classlist properly (#6162)
* Sanitize classlist properly

* Actually properly sanitize every class after the first

* Improve Formatter spec to check for multiple classes and non-space whitespace
2018-01-03 03:54:08 +01:00
Eugen Rochko
d319b3dbe4 Update moved-to property when it's removed too (#6160)
* Fix #6140 - Update moved-to property when it's removed too

* Remove trailing whitespace
2018-01-03 00:38:20 +01:00
Eugen Rochko
d60fd87e01 Don't leave behind husk of remotely-deleted profile (#6159)
There's no reason for an Account record to persist after Delete->Actor is received. SuspendAccountService is necessary to make sure deleted toots get sent over streaming API properly and home feeds get cleaned up. By removing Account record, we can ensure that if in the future the account is restored remotely (or username reused), it can start with a clean slate.
2018-01-03 00:38:02 +01:00
Noiob
94230fe565 Fix newlines-to-spaces functionality (#6158)
yay for regexes, amirite
2018-01-02 19:35:24 +01:00
Patrick Figel
04ecf44c2f Add confirmation step for email changes (#6071)
* Add confirmation step for email changes

This adds a confirmation step for email changes of existing users.
Like the initial account confirmation, a confirmation link is sent
to the new address.

Additionally, a notification is sent to the existing address when
the change is initiated. This message includes instruction to reset
the password immediately or to contact the instance admin if the
change was not initiated by the account owner.

Fixes #3871

* Add review fixes
2018-01-02 16:55:00 +01:00
ThibG
b6af88192f Display a warning when composing unlisted toots with something looking like a hashtag (#6132) 2018-01-02 14:24:52 +01:00
Eugen Rochko
1419f656e2 Fix stats expiring too quickly because of variable mistake (#6155) 2018-01-02 14:02:53 +01:00
Akihiko Odaki
3ba7cde38d Rename key to path in actions and reducers for settings (#6105) 2018-01-02 13:50:54 +01:00
Otakan
ce854ed506 delete X-UA-Compatible (#6068)
* delete X-UA-Compatible

* undo

* restore
2018-01-02 13:38:12 +01:00
Branko Kokanovic
21b9da6418 Adding Serbian latin translations (#6146)
Serbian latin (sr-Latn) is generated automatically from Serbian (sr) translation. Also changed some wording in original (Serbian) translation.
2018-01-02 20:39:12 +09:00
David Yip
fa768abf5c More ../ -> ~. 2018-01-02 00:26:44 -06:00
David Yip
54148b9a4a Merge remote-tracking branch 'origin/master' into merge-upstream
Conflicts:
	app/controllers/authorize_follows_controller.rb
	app/javascript/styles/mastodon/components.scss
2018-01-02 00:11:41 -06:00
Akihiko Odaki
764f876953 Use const instead of let for constant (#6106) 2018-01-02 13:28:49 +09:00
Akihiko Odaki
2c1ed5f872 Show mastodon on modal (#6129) 2018-01-02 05:07:56 +01:00
cwm
72b99f6ee4 bug fix (tootsuite pr #6120) 2017-12-31 08:26:50 -06:00
Jenkins
fa9b7ece2e Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-31 09:17:15 +00:00
Branko Kokanovic
7d376e41be Adding Serbian translation (#6133)
* Adding Serbian translation

* i18n-tasks normalize
2017-12-31 17:28:20 +09:00
David Yip
d817c0a958 Merge pull request #291 from glitch-soc/merge-upstream
Merge with upstream @ f4b80e6511
2017-12-30 18:24:38 -06:00
David Yip
4cca1d1e7e Merge remote-tracking branch 'origin/master' into merge-upstream
Conflicts:
	app/controllers/auth/confirmations_controller.rb
2017-12-30 17:20:07 -06:00
David Yip
e4944d065b Switch to tootsuite's elephant friend for the drawer
This version has a transparent background, which works better with the
wave.
2017-12-30 17:17:37 -06:00
David Yip
513d982f29 Use ~ notation in components for image references
This uses (more or less) absolute references to external assets, which
allows imported stylesheets (like components.scss) to work from other
locations that may not have the expected path structure (e.g. the win95
theme).
2017-12-30 17:15:20 -06:00
cwm
4ce44ba470 remove unused 'saveSettings' from column_settings_container 2017-12-30 16:42:26 -06:00
cwm
0dce26b82b web push updates (tootsuite PRs #5879, #5941, #6047) 2017-12-30 11:45:01 -06:00
Jeong Arm
f4b80e6511 Translate Korean (#6131)
Relates to #6125, #6099
2017-12-30 02:44:19 +01:00
beatrix
a56c4742d3 keep the same filters and page when doing custom emojo stuff (fixes #6112) (#6114) 2017-12-30 02:43:43 +01:00
Eugen Rochko
38fc1b498d Add more instance stats APIs (#6125)
* Add GET /api/v1/instance/peers API to reveal known domains

* Add GET /api/v1/instance/activity API

* Make new APIs disableable, exclude private statuses from activity stats

* Fix code style issue

* Fix week timestamps
2017-12-29 19:52:04 +01:00
David Yip
65c87ca0ae Merge pull request #288 from chriswmartin/fix-gif-avatars
Fix GIF avatars not autoplaying when gif autoplay is enabled
2017-12-28 13:23:41 -06:00
MitarashiDango
511c6f9625 bug fix (WebPush does not work) (#6120) 2017-12-28 16:20:34 +01:00
cwm
832a93e67c Fix GIF avatars not autoplaying (fixes #287, tootsuite pr #6000) 2017-12-28 08:30:51 -06:00
ThibG
868568d1c1 Make host_meta/webfinger replies cacheable (fixes #6100) (#6101)
* Make host_meta/webfinger replies cacheable (fixes #6100)

Drop common code for handling users and sessions as webfinger queries
are very basic, public APIs.

Also explicitly mark results as cacheable with “expires_in”.

* Add “Vary: Accept” header for caching since content-negociation is used
2017-12-27 18:21:12 +01:00
David Yip
7174d1c955 Merge remote-tracking branch 'origin/master' into merge-upstream
Conflicts:
	app/javascript/images/mastodon-drawer.png
	app/javascript/styles/mastodon/components.scss
2017-12-26 22:24:19 -06:00
Akihiko Odaki
65f30f65a2 Move the mastodon on Getting Started column to drawer column (#6109)
Getting Started column obtained many links, and it became much taller.
Because of its height, Getting Started column required long scrolling on
devices with small screen, such as 4 inch phones and 10 inch laptops.

This change moves the mastodon which took large space on the column to
drawer column. The drawer column has only the compose form and has more
space.
2017-12-27 03:31:30 +01:00
Akihiko Odaki
e0ef7f9d79 Fix XML oEmbed support discovery (#6104) 2017-12-27 03:29:49 +01:00
beatrix
7347bc7334 Merge pull request #286 from chriswmartin/merge-vanilla-updates-into-glitch
Merge various small vanilla updates into glitch
2017-12-26 17:03:29 -05:00
beatrix
e6ab869d95 Merge pull request #285 from chriswmartin/detailed-status-actions
Add mute, block, conversation mute actions to detailed status dropdown
2017-12-26 17:03:00 -05:00
cwm
20ad071931 Set direction style to reply indicator (tootsuite pr #6006) 2017-12-26 14:20:41 -06:00
cwm
14243bf80a Reduce motion for boost animation (tootsuite pr #5871) 2017-12-26 14:04:52 -06:00
cwm
337c2e77ee Fix layout for RTL (tootsuite pr #6014, #6018) 2017-12-26 13:49:53 -06:00
cwm
46565ed746 Fix focused background color of favourited direct toot (tootsuite pr #6021) 2017-12-26 13:30:23 -06:00
cwm
815524412b Move dropdown transform origin (tootsuite pr #6091) 2017-12-26 13:28:19 -06:00
cwm
083571915f onMuteNotifications validation (tootsuite pr #6092) 2017-12-26 13:25:43 -06:00
cwm
2bbd22e91c Rename settingKey to settingPath (tootsuite pr #6046 & #6098) 2017-12-26 13:21:20 -06:00
Jenkins
6b85dba8da Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-26 18:17:16 +00:00
beatrix
127bfda521 add ruby-progressbar to gemfile (fixes #6110) (#6111) 2017-12-26 18:43:52 +01:00
takayamaki
1494509468 more faster index on notifications table (#6108) 2017-12-26 17:56:31 +01:00
cwm
cc6a2ffd0a use 'flavours/glitch/' prefix in new imports 2017-12-26 10:41:44 -06:00
cwm
22818d2a16 Add mute, block, conversation mute actions to detailed status dropdown menu 2017-12-26 10:13:38 -06:00
Jenkins
bed13f22e2 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-25 20:17:10 +00:00
Chris
1e5d1fa5c8 Add mute, block, conversation mute actions to detailed status dropdown menu (#6099)
* removed references to hideOnMobile in column_link and getting_started

* add mute, block, conversationMute actions to detailed status dropdown (fixes #1226)

* remove unused withDismiss in detailed status
2017-12-25 20:56:05 +01:00
MitarashiDango
a3b369337f Additional prop name change. (#6098) 2017-12-26 00:14:06 +09:00
Yamagishi Kazutoshi
43c37a4768 Add supported Node.js version to package.json (#6096) 2017-12-25 15:02:07 +01:00
Jenkins
f77c47d01b Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-24 15:17:12 +00:00
Eugen Rochko
cafe27fb29 Add rake task to check and purge accounts that are missing in origin (#6085)
* Add rake task to check and purge accounts that are missing in origin

* Add progress bar and --force options to mastodon:maintenance:purge_removed_accounts
2017-12-24 16:14:33 +01:00
Neetshin
7e6214b869 Add validation for onMuteNotifications (#6092)
* Add aria-autocomplete='list' in Textaria

ref: https://www.w3.org/TR/wai-aria-1.1/#aria-autocomplete

* Make detect empty string brefore assign upload description

* Change code elements in keyboard-shortcuts component to kbd

* Add validation for onMuteNotifications
2017-12-24 17:18:45 +09:00
Nolan Lawson
a8eb0bf44f Reduce motion for boost animation (#5871)
* Reduce motion for boost animation

Fixes #5833

* Fix ternary expression
2017-12-24 04:48:31 +01:00
Akihiko Odaki
35fdf561be Refactor web_push_subscription (#6047)
* Remove onSave method in mapped properties for column_settings

* Make web_push_subscription.register an action
2017-12-24 04:47:35 +01:00
Chris
081956742c removed references to hideOnMobile in column_link and getting_started (#6082)
* removed references to hideOnMobile in column_link and getting_started

* move keyboard shortcuts back below blocked users
2017-12-24 04:47:02 +01:00
cpsdqs
8528fd89d2 Move dropdown transform origin to top edge (#6091) 2017-12-24 00:53:03 +01:00
Jenkins
6d00ca1c71 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-22 04:17:14 +00:00
beatrix
4a7782d5c9 Merge pull request #280 from KnzkDev/add-en-ja
i18n: Add en and ja
2017-12-21 22:49:30 -05:00
ncls7615
fb57ec41df Update and add some translate 2017-12-22 12:22:41 +09:00
ncls7615
4694dcfe16 Add more en and ja 2017-12-22 12:11:28 +09:00
ncls7615
02db8aa3a0 Add en and ja 2017-12-22 12:01:38 +09:00
nightpool
9592b5e31e enforce LOCAL_HTTPS=true in production (#6061)
* enforce https in production

* note changes in production env sample

* typo fix
2017-12-22 02:17:59 +01:00
ThibG
cea98e0c12 Reduce the number of synchronous resolves when posting toots (#6075) 2017-12-22 02:15:08 +01:00
ThibG
6eb60260b1 Display deleted users' role as “Suspended” (#6080)
Deleted users are technically suspended, but the code displaying their status
in the admin interface was broken and displayed a javascript object holding
translations of the possible user roles instead.
2017-12-22 02:14:17 +01:00
beatrix
60d415bc5e Merge pull request #266 from chriswmartin/getting-started-improvements
Getting started column improvements
2017-12-21 18:47:07 -05:00
beatrix
f78d169d71 Merge pull request #278 from KnzkDev/fix/acc-header-avatar
Fix account header avatar
2017-12-21 17:46:53 -05:00
ncls7615
191b625a27 Fix account header avatar 2017-12-22 07:12:49 +09:00
beatrix
a3cb5a6b64 Merge pull request #277 from glitch-soc/273-add-data-status-by-to-detailed-status
Add data-status-by back to DetailedStatus component.  Fixes #273.
2017-12-21 16:10:36 -05:00
David Yip
7bb99ca3cf Add data-status-by back to DetailedStatus component. Fixes #273. 2017-12-21 13:22:33 -06:00
cwm
d736739e8f <kbd> instead of <code> in KeyboardShortcuts component (tootsuite pr #6049) 2017-12-21 13:04:40 -06:00
cwm
7917d45396 remove themes.default from i18n-tasks unused check 2017-12-21 12:45:11 -06:00
beatrix
bc0152974f Merge pull request #276 from glitch-soc/245-click-avatar-to-open-profile-in-new-tab
Wrap <Avatar> in account header in a link to that account's page (fixes #245)
2017-12-21 08:30:22 -05:00
beatrix
4d34b3495d Merge pull request #274 from glitch-soc/fix-packs-on-2fa-pages
Set packs on 2FA-related pages.  Fixes #271.
2017-12-21 08:30:05 -05:00
David Yip
dbd5f8b9a5 Wrap <Avatar> in account header in a link to that account's page. Fixes #245. 2017-12-21 05:25:16 -06:00
David Yip
00d1f8eea1 Remove themes.default from i18n-tasks unused check
This translation key comes in from tootsuite, but the flavours system
does not use it.
2017-12-21 05:24:17 -06:00
David Yip
a1ddc89da6 Remove unused themes.default key in ko locale. 2017-12-20 20:02:50 -06:00
cwm
9042f9a813 add keyboard shortcuts to getting started (fixes #275) 2017-12-20 09:50:29 -06:00
David Yip
bf1eb0912c Set packs on 2FA-related pages. Fixes #271.
Specifically, this commit:

- changes S::TFA::{Confirmations,RecoveryCodes}Controller to derive from
  S::BaseController, because this gives us the necessary actions and
  packs
- prepends set_pack to Auth::SessionsController's action chain so that
  it takes effect in time for render :two_factor
2017-12-20 03:15:54 -06:00
Jenkins
6f11aa8383 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-20 08:17:15 +00:00
Akihiko Odaki
81d29e4126 Rename settingKey in setting_toggle to settingPath (#6046) 2017-12-20 16:19:59 +09:00
Neetshin
c11a52d888 Replace <code> to <kbd> in KeyboardShortcuts component (#6049)
* Add aria-autocomplete='list' in Textaria

ref: https://www.w3.org/TR/wai-aria-1.1/#aria-autocomplete

* Make detect empty string brefore assign upload description

* Change code elements in keyboard-shortcuts component to kbd
2017-12-20 11:46:25 +09:00
Jeong Arm
e52293482e Update Korean translation (#6050)
* Update Korean translation

* Translate Korean for javascript

* Add missing translations on simple_form
2017-12-20 11:45:50 +09:00
beatrix
6cb47f17fc Merge pull request #272 from KnzkDev/add-en-ja
i18n: Update some locale files
2017-12-19 10:35:02 -05:00
ncls7615
d3b2677775 Add en and ja 2017-12-19 20:16:48 +09:00
cwm
14aae9c05c missed a semicolon 2017-12-17 22:21:15 -06:00
cwm
be50e45a74 use makeMapStateToProps 2017-12-17 22:00:25 -06:00
cwm
e4ebbf4f07 use list-ul icon in list header and web/lists 2017-12-17 21:25:18 -06:00
Jenkins
e7fa7fce25 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-17 03:17:14 +00:00
Peter
f38e6a14f2 Add Slovak translation (#6052)
* Add Slovak translation

* Slovak translation: i18n-normalize
2017-12-17 11:26:42 +09:00
Jenkins
05a2a70ca2 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-16 15:17:12 +00:00
beatrix
e202efdf8a Merge pull request #253 from glitch-soc/prevent-local-only-federation
prevent federation of local-only statuses
2017-12-16 09:26:48 -05:00
beatrix
5325c2ab3e Merge pull request #270 from KnzkDev/fix/action_logs
Fix admin/action_logs page
2017-12-16 09:25:57 -05:00
ncls7615
9e8fa3caee fix admin/action_logs page 2017-12-16 20:27:26 +09:00
kibigo!
e677dda07c Fixes #244 to make search results scrollable 2017-12-15 12:49:23 -08:00
Daigo 3 Dango
a434d9c0cc Remove period from the version number (#6039)
2.1.0. -> 2.1.0
2017-12-15 21:38:25 +01:00
David Yip
82b2e224a2 Merge branch 'gs-master' into prevent-local-only-federation
Conflicts:
	db/schema.rb
2017-12-15 12:20:56 -06:00
Jenkins
40afadfb01 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-15 18:17:14 +00:00
Eugen Rochko
a29432f0cd Bump version to 2.1.0 🎆 2017-12-15 19:14:57 +01:00
beatrix
e0a573a2ce Merge pull request #269 from ncls7615/patch-1
Update ja.js
2017-12-15 09:12:54 -05:00
NCLS
e9a5bde9a0 Update ja.js 2017-12-15 19:43:10 +09:00
NCLS
61ed5b972c Update ja.js 2017-12-15 19:38:13 +09:00
Jenkins
37254c4f5d Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-15 01:17:12 +00:00
Eugen Rochko
098c7d27fe Bump version to 2.1.0rc6 2017-12-15 02:00:28 +01:00
Eugen Rochko
3d3b403359 Do not hide statuses from silenced accounts from other silenced accounts (#6030) 2017-12-15 01:54:05 +01:00
Naoki Kosaka
25b0d7538e Fix oEmbed image_modal src. (#6027) 2017-12-14 23:31:14 +01:00
Eugen Rochko
a3b2ea599d Fix #6022 - Prevent nested migrated accounts, or migrations to self (#6026) 2017-12-14 21:35:30 +01:00
SerCom_KC
573414f728 Improve Chinese (Simplified) Translations (#6024)
* i18n: (zh-CN) Change `工作人员` (staff) to `管理人员`
Suggested by @Gargron at https://github.com/tootsuite/mastodon/pull/6005#discussion_r156678109

* i18n: (zh-CN) Change `协管` to `监察员`

* i18n: (zh-CN) Fix all "Are you" questions

* i18n: (zh-CN) Various improvements

* i18n: (zh-CN) Final clean-up

* i18n: (zh-CN) Change translation for 500

* i18n: (zh-CN) Remove spaces between time distances

* i18n: (zh-CN) Improve translations
2017-12-14 19:33:29 +01:00
Jeroen
aa273a2718 Last minute Dutch string updates (#6025)
* Last minute Dutch strinfupdate

* Last minute Dutch strings update

* Fixing Weblate output errors

* Fixing Weblate output errors

* Fixing more Weblate rubish

Weblate is also changing some " to ' - I think that is not a problem

* Fixing more weblate stuff

* Fixing

* Update nl.yml
2017-12-14 18:45:32 +01:00
David Yip
6abb0950c6 Examples for Status.as_public_timeline.
Also adjust the examples for Status.as_tag_timeline to match the
nomenclature used in .as_public_timeline (e.g. "account" -> "viewer").
2017-12-14 02:57:59 -06:00
David Yip
e35a350119 Examples for Status#set_locality and .as_tag_timeline.
This commit also:

- exposes the local-only emoji so that it can be used in examples
- allows local_only to be set explicitly, i.e. for timeline filtering
  specs
2017-12-14 02:27:42 -06:00
Jenkins
e5a9831a56 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-13 23:17:15 +00:00
Lynx Kotoura
0d3ffa691e Fix focused background color of notifications of direct toots (#6021) 2017-12-14 07:36:29 +09:00
Lynx Kotoura
5ad45552b3 Fix overflowing emojis on some devices (#6016)
* Fix overflowing emojis on some devices

* Quit visible and add padding
2017-12-13 22:58:31 +01:00
Olivier Humbert
dc313f27bb 1 fix + 1 translation (#6019) 2017-12-13 22:58:20 +01:00
Jenkins
10f800ab46 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-13 20:17:13 +00:00
Eugen Rochko
7cad926401 Bump version to 2.1.0rc5 2017-12-13 20:53:39 +01:00
Eugen Rochko
3487460f00 Fix regression from #6014 (#6018) 2017-12-13 20:33:04 +01:00
Jenkins
82236a3703 Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master 2017-12-13 18:17:16 +00:00
Quenty31
72314d26ae l10n OC and FR updates (#6017)
* Adjust empty list timeline message (#5997)

* Adjust empty list timeline message (#5997)

* Add filters to admin UI for custom emojis (#6003) + #6004

* Update fr.yml
2017-12-14 03:17:04 +09:00
Eugen Rochko
cc75d47926 Fix layout for RTL (#6014) 2017-12-13 18:28:13 +01:00
Lynx Kotoura
8bf4cc72b6 Excahnge the order of spoiler-input and unlocked warning (#6015)
* Excahnge the order of spoiler-input and unlocked warning

* Fix trailing whitespace
2017-12-13 18:01:56 +01:00
Olivier Humbert
ad941f5a21 Update FR translation (#6012) 2017-12-13 18:00:42 +01:00
Lynx Kotoura
0aeec0390b Redesign tootbox (#5919)
* Redesign tootbox

* Move counter into compose-form__buttons-wrapper

Change font and remove shadow
Refactor sass codes of compose-form
2017-12-13 17:37:23 +01:00
Eugen Rochko
fef6625496 Weblate translations (#6011)
* Translated using Weblate (Dutch)

Currently translated at 100.0% (522 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (French)

Currently translated at 99.8% (521 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/

* Translated using Weblate (Catalan)

Currently translated at 99.4% (519 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Japanese)

Currently translated at 99.4% (519 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Japanese)

Currently translated at 99.6% (520 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/

* Translated using Weblate (Arabic)

Currently translated at 40.0% (209 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/

* Translated using Weblate (Polish)

Currently translated at 99.8% (521 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/

* Added translation using Weblate (Galician)

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.0% (517 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Galician)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/gl/

* Added translation using Weblate (Galician)

* Translated using Weblate (Galician)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/gl/

* Translated using Weblate (Galician)

Currently translated at 22.6% (17 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/gl/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt/

* Translated using Weblate (Portuguese)

Currently translated at 66.0% (37 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Japanese)

Currently translated at 99.6% (520 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (43 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ja/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.6% (520 of 522 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/

* Translated using Weblate (Arabic)

Currently translated at 48.8% (21 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ar/

* Translated using Weblate (Arabic)

Currently translated at 98.2% (55 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Portuguese)

Currently translated at 73.2% (41 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (43 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/pt/

* i18n-tasks normalize && yarn manage:translations

* Restore wrongfully deleted files
2017-12-13 16:37:15 +01:00
SerCom_KC
775c3056b6 Update Chinese (Simplified) translations (#6005)
* i18n: (zh-CN) Remove spaces in time distances

* i18n: (zh-CN) Update translations for #5997

* i18n: (zh-CN) Add translation for #6004
also change translation of `staff` from `管理员` to `工作人员`

* i18n: (zh-CN) Add translations for #6003

* i18n: (zh-CN) Normalization
2017-12-13 15:52:40 +01:00
nullkal
ccf4f170de Make sure call done(); in the listener of public timeline for anonymous connection (#6009) 2017-12-13 14:27:36 +01:00
nullkal
90e7da16a0 Fix the condition in streaming listener (#6008) 2017-12-13 13:42:16 +01:00
Eugen Rochko
ad75ec8b5b Add filters to admin UI for custom emojis (#6003) 2017-12-13 13:28:31 +01:00
Eugen Rochko
57fcc21a86 Bump version to 2.1.0rc4 2017-12-13 12:45:12 +01:00
Yamagishi Kazutoshi
6855baa0c5 Change streaming API URL when remote development (#5942)
* Change streaming API URL when remote development

* Use STREAMING_API_BASE_URL when dev env
2017-12-13 12:43:54 +01:00
Yamagishi Kazutoshi
07b4427865 Set direction style to reply indicator (#6006) 2017-12-13 12:17:37 +01:00
Eugen Rochko
a8deb6648b Fix redundant HTTP request in FetchLinkCardService (#6002) 2017-12-13 12:15:28 +01:00
Eugen Rochko
20a6584d2d Clean up admin UI for accounts (#6004)
* Add staff filter to admin UI for accounts, remove obsolete columns

* Only display OStatus section in admin UI for accounts when OStatus data
2017-12-13 12:15:10 +01:00
Eugen Rochko
155e211dd0 Fix GIF avatars not autoplaying when GIF autoplay is enabled (#6000) 2017-12-13 12:14:03 +01:00
Eugen Rochko
81923f88ba Shorten English title for 2FA to avoid line-break (#6001) 2017-12-13 15:42:22 +09:00
cwm
17e53b931c code style fixes 2017-12-12 23:07:12 -06:00
cwm
9719983f2d move list items into an array, don't unnecessarily concat misc nav item 2017-12-12 22:50:20 -06:00
Eugen Rochko
5706fe18c2 Fix #5952 - NameError (regression from #5762) (#5999)
* Fix #5952 - NameError (regression from #5762)

* Fix
2017-12-13 04:12:38 +01:00
Eugen Rochko
71965cbef2 Adjust empty list timeline message (#5997) 2017-12-13 02:40:32 +01:00
Eugen Rochko
0128b86d30 Use streaming API for standalone timelines on /about and /tag pages (#5998) 2017-12-13 02:12:41 +01:00
Quenty31
0370ba7b0a Update: #5985 and #5817 (#5996) 2017-12-12 20:48:26 +01:00
erin
c986218c3a Improve error handling in streaming/index.js (#5968)
On an unhandled worker exception, we should log the exception
and exit with nonzero status, instead of letting workers
silently fail and restarting them in an endless loop.

Note: we previously tried to handle the `'error'` signal.
That's not a signal Node fires; my patch traps `'uncaughtException'`,
which is what the code was _trying_ to do.
2017-12-12 20:19:33 +01:00
Neetshin
0c8b1eb577 Make detect empty string before assign image description (#5994)
* Add aria-autocomplete='list' in Textaria

ref: https://www.w3.org/TR/wai-aria-1.1/#aria-autocomplete

* Make detect empty string brefore assign upload description
2017-12-12 19:57:22 +01:00
abcang
cfa3f55221 Remove duplicate indexes in lists (#5990) 2017-12-12 17:38:42 +01:00
Akihiko Odaki
f9f6918148 Store preview image for embedded photo in preview cards (#5986)
The preview image would be useful to embed in timeline.
2017-12-12 15:54:38 +01:00
cwm
b6ded7119e travis fixes 2017-12-12 08:50:31 -06:00
beatrix
65d083338d Merge pull request #263 from KnzkDev/optimize-paperclip
Optimizing paperclip
2017-12-12 09:15:52 -05:00
SerCom_KC
2a61b9f000 Update Chinese (Simplified) translations (#5991)
* i18n: (zh-CN) Update translations for #5817

* i18n: (zh-CN) Add translation for #5985

* i18n: (zh-CN) Normalization
2017-12-12 15:13:47 +01:00
nullkal
cfea28216f make it possible to stream public timelines without authorization (#5977)
* make it possible to stream public timelines without authorization

* Fix

* Make eslint allow `value == null`

* Remove redundant line

* Improve style and revert .eslintrc.yml

* Fix streamWsEnd

* Show IP address instead of (anonymous user)

* Add missing semicolon
2017-12-12 15:13:24 +01:00
Renato "Lond" Cerqueira
19257d91bf Return false if object does not respond to url (#5988)
Avoid error when the service returns a mostly valid oembed, but has no
url in it, causing a MethodError: undefined method `url'
for #<OEmbed::Response::Photo:0x000056505def9620>
2017-12-12 15:12:09 +01:00
Renato "Lond" Cerqueira
fe180f18ff Change conditional to avoid nil into string error in sidekiq (#5987)
* Change conditional to avoid nil into string error in sidekiq
When obtaining information about users with mastodon in a different
subdomain, sidekiq was giving out a 'no implicit conversion of nil into String'

* Use presence instead of blank? with ternary.
Following suggestion on PR
2017-12-12 15:11:13 +01:00
Yamagishi Kazutoshi
1486fd64cc Move files for GitHub to .github directory (#5989) 2017-12-12 15:10:12 +01:00
beatrix
504c3d650f Merge pull request #264 from KnzkDev/add-ja
i18n: Add ja translate
2017-12-12 08:49:19 -05:00
David Yip
be33247235 Remove themes.default from nl and pt-BR locales.
This translation has been supplanted by the flavours-related
translations.
2017-12-12 03:28:15 -06:00
David Yip
a057ed5cfe Merge remote-tracking branch 'tootsuite/master' into merge-upstream 2017-12-12 02:54:13 -06:00
cwm
9b7208f4b4 readded connect for onboarding modal 2017-12-12 00:17:07 -06:00
cwm
b358483ef2 remove unneeded imports from getting_started_misc 2017-12-12 00:09:18 -06:00
cwm
37ced4c903 add components to getting started column 2017-12-12 00:01:17 -06:00
ncls7615
b93ad3d0e8 add ja 2017-12-12 13:13:54 +09:00
ncls7615
c0c4526283 add ja for fav conf 2017-12-12 13:05:48 +09:00
Gô Shoemake
04eaa1943f Merge pull request #262 from chriswmartin/fix-oauth-pack
Add missing set pack to authorizations_controller
2017-12-11 19:34:19 -08:00
ncls7615
c95af71da5 optimize paperclip 2017-12-12 12:29:09 +09:00
Akihiko Odaki
14c4a33cd9 Change account_id non-nullable in lists (#5979) 2017-12-12 04:11:17 +01:00
Eugen Rochko
30d2ea03b0 Improve public status page title (#5985) 2017-12-12 03:56:30 +01:00
Eugen Rochko
1356ed72cd Fix #5953 - Add GET /api/v1/accounts/:id/lists (#5983) 2017-12-12 03:55:39 +01:00
Eugen Rochko
481fac7c84 Exclude moved accounts from search results (#5984) 2017-12-12 02:14:33 +01:00
cwm
0a52e37648 change pack to 'auth' 2017-12-11 18:14:41 -06:00
cwm
44992df257 load pack 2017-12-11 17:54:40 -06:00
Erin
2efffe77dc reblog_service.rb: Status#local_only -> local_only? 2017-12-11 17:39:11 -06:00
Erin
c5a4eda694 move outbox filtering to Status#permitted_for (as per @ekiru) 2017-12-11 15:28:04 -06:00
Erin
3ec47e732b post_status_service: stylistic change (local_only -> local_only?) 2017-12-11 15:27:31 -06:00
Quenty31
c588fcf4bc Tiny little change (#5981) 2017-12-11 20:53:29 +01:00
kibigo!
8aa527434c Fixed index in webpack config 2017-12-11 10:45:21 -08:00
beatrix
7d024a6b68 Merge pull request #259 from KnzkDev/flavours-ja
i18n: Add ja translate for flavour/skin ux
2017-12-11 10:56:41 -05:00
ncls7615
d8206d1931 add ja 2017-12-12 00:28:33 +09:00
beatrix
771b950feb Merge pull request #254 from glitch-soc/new-theme-ux
New flavour/skin UX
2017-12-11 09:36:14 -05:00
Eugen Rochko
feed07227b Apply a 25x rate limit by IP even to authenticated requests (#5948) 2017-12-11 15:32:29 +01:00
beatrix
0cd5f2a61f Merge pull request #255 from KnzkDev/remove-picture
Remove getting-started picture
2017-12-11 09:08:13 -05:00
Akihiko Odaki
e56323a4dd Remove preview_card fabricator (#5975)
preview_card fabricator has a removed attribute, status, and is no longer
functional.
2017-12-11 22:22:08 +09:00
David Yip
204688e803 Add missing set_pack def/filter in OAuth::AuthorizedApplicationsController. 2017-12-11 00:17:30 -06:00
ncls7615
9d5ecdbf41 remove picture 2017-12-11 13:52:17 +09:00
David Yip
1138d0c321 Add Rake task to backfill local-only flag (#253) 2017-12-10 22:49:59 -06:00
Erin
288f1293ef set local_only in a before_create callback instead of status service 2017-12-10 21:39:27 -06:00
Erin
0c46058a43 remove vestigial Status#local_only? definition 2017-12-10 21:25:36 -06:00
Erin
c1410af368 post_status_service.rb: save the status after setting local_only 2017-12-10 20:35:57 -06:00
Erin
24f36ca912 Status#not_local_only scope should match nils too 2017-12-10 20:35:42 -06:00
Sylvhem
84d5bfb35e Change the disclaimer under the sign up form (#5817)
* Change the disclaimer below the sign up form

Change the disclaimer below the sign up form on the home page. The current text is linking to the /about/more page under "our terms of service" and to the /terms page under "privacy policy". This change intend to make the message more coherent.

Change l’avertissement en-dessous du formulaire d’inscription sur la page d’accueil. Le texte actuel redirige vers /about/more sous un lien intitulé "nos conditions d’utilisation" et vers /terms via "notre politique de confidentialité". Ce changement vise à rendre le message plus cohérent.

* Second take on the disclaimer

A new version of the disclaimer, based on feedback.

Une nouvelle version de l’avertissement, basé sur les premiers retours.
2017-12-11 02:30:43 +01:00
Erin
f080a9fac7 filter local-only toots from AP outboxes 2017-12-10 19:07:43 -06:00
Erin
6bd18e43ba filter local-only statuses from public pages 2017-12-10 17:23:01 -06:00
Erin
5ef65aab8f replace reblog service check for an 👁️ with #local_only 2017-12-10 17:12:21 -06:00
Erin
cfbb95605b set local_only flag on statuses in post_status_service 2017-12-10 17:04:32 -06:00
Erin
08519cd4f4 status: stub local_only?, add scope, add marked_local_only? 2017-12-10 17:04:28 -06:00
Erin
434c70fd98 add a local_only column to the statuses table 2017-12-10 16:41:25 -06:00
Andrea Scarpino
6a82939adb Fix account and tag searches with leading/trailing spaces (#5965)
* Strip leading & trailing spaces from account query

* Strip leading & trailing spaces from tag search
2017-12-10 19:35:46 +01:00
Lynx Kotoura
98aa96b8d6 Refix extraspace for emojis (#5964)
Fix misalignment between emoji sizes
2017-12-10 17:56:05 +01:00
abcang
3caec1ecc2 Save media outside transaction (#5959) 2017-12-10 16:33:52 +01:00
goofy-bz
2950de86c6 Update devise.fr.yml (#5963)
ludicrously tiny but necessary typofix (wrong accent)
2017-12-11 00:24:29 +09:00
Quenty31
7d4ebeecbd l10n i18n OC: corrections (#5962)
* filling missing strings

* Small changes

Better way of saying
+ removed 2 finals dots

* Corrections

* Corrections

Now with final point or without, just like the EN file

* Update oc.json
2017-12-11 00:07:24 +09:00
Yamagishi Kazutoshi
6e3f176b8e Add Galician language support (#5955) 2017-12-10 04:19:07 +01:00
Olivier Humbert
a4710f9af8 French translation update (#5954)
* Update French translation

* fix
2017-12-10 09:47:59 +09:00
abcang
fcc0795a40 Remove unused function (#5950) 2017-12-09 23:37:31 +01:00
ButterflyOfFire
0f8140d26a Create activerecord.ar.yml (#5951) 2017-12-09 23:37:18 +01:00
Yamagishi Kazutoshi
e7d55df38d Ignore HEAD method if does not support (#5949) 2017-12-09 16:53:40 +01:00
Eugen Rochko
a72d03f43c Weblate translations (#5946)
* Translated using Weblate (German)

Currently translated at 84.2% (439 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* Translated using Weblate (English)

Currently translated at 99.8% (520 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/en/

* Translated using Weblate (German)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/de/

* Translated using Weblate (English)

Currently translated at 99.8% (520 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/en/

* Translated using Weblate (German)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/de/

* Translated using Weblate (French)

Currently translated at 84.6% (441 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (German)

Currently translated at 86.9% (453 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* Translated using Weblate (Korean)

Currently translated at 86.3% (450 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ko/

* Translated using Weblate (French)

Currently translated at 84.8% (442 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (French)

Currently translated at 84.8% (442 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (French)

Currently translated at 84.8% (442 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (Portuguese)

Currently translated at 36.2% (189 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/he/

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/he/

* Translated using Weblate (Hebrew)

Currently translated at 53.1% (277 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/he/

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/he/

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/he/

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (43 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/he/

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/he/

* Translated using Weblate (Spanish)

Currently translated at 75.6% (394 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/es/

* Translated using Weblate (French)

Currently translated at 86.3% (450 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (Portuguese)

Currently translated at 98.2% (55 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/nl/

* Translated using Weblate (Dutch)

Currently translated at 84.6% (441 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (German)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/de/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ca/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/nl/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/ca/

* Translated using Weblate (German)

Currently translated at 88.2% (460 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 90.2% (470 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (French)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 90.2% (470 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (French)

Currently translated at 87.3% (455 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (Hebrew)

Currently translated at 61.8% (322 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/he/

* Translated using Weblate (French)

Currently translated at 87.3% (455 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/fr/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.3% (481 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ca/

* Translated using Weblate (French)

Currently translated at 87.3% (455 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (French)

Currently translated at 87.5% (456 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (French)

Currently translated at 87.7% (457 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/ca/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/

* Translated using Weblate (Portuguese)

Currently translated at 42.4% (221 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Portuguese)

Currently translated at 97.3% (73 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pt/

* Translated using Weblate (Catalan)

Currently translated at 99.8% (520 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (2 of 2 strings)

Translation: Mastodon/Activerecord
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/activerecord/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (43 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt/

* Translated using Weblate (German)

Currently translated at 90.5% (472 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* Translated using Weblate (German)

Currently translated at 90.7% (473 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* Translated using Weblate (German)

Currently translated at 90.9% (474 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* Translated using Weblate (German)

Currently translated at 91.1% (475 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* Translated using Weblate (Dutch)

Currently translated at 90.4% (471 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (German)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/de/

* Translated using Weblate (German)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/de/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.3% (481 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (German)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2 of 2 strings)

Translation: Mastodon/Activerecord
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/activerecord/de/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt_BR/

* Translated using Weblate (Dutch)

Currently translated at 90.5% (472 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Arabic)

Currently translated at 44.1% (19 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ar/

* Translated using Weblate (Norwegian (old code))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/no/

* Translated using Weblate (Arabic)

Currently translated at 85.7% (48 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/

* Translated using Weblate (Arabic)

Currently translated at 92.0% (69 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/ar/

* Translated using Weblate (Portuguese)

Currently translated at 47.7% (249 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt/

* Translated using Weblate (Arabic)

Currently translated at 29.9% (156 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/

* Translated using Weblate (Spanish)

Currently translated at 99.8% (520 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/es/

* Translated using Weblate (Polish)

Currently translated at 99.8% (520 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/

* Translated using Weblate (French)

Currently translated at 99.6% (519 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/es/

* Translated using Weblate (French)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/fr/

* Translated using Weblate (Spanish)

Currently translated at 99.8% (520 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/es/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.2% (517 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Added translation using Weblate (Galician)

* Translated using Weblate (Japanese)

Currently translated at 99.6% (519 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Japanese)

Currently translated at 92.8% (52 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/ja/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.4% (518 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/

* Translated using Weblate (Galician)

Currently translated at 43.6% (107 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (43 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ja/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/ar/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.4% (518 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/

* Translated using Weblate (Arabic)

Currently translated at 96.4% (54 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Arabic)

Currently translated at 31.2% (163 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/

* Translated using Weblate (Dutch)

Currently translated at 91.5% (477 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (French)

Currently translated at 99.8% (520 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/ar/

* Translated using Weblate (Arabic)

Currently translated at 98.2% (55 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (521 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (521 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (French)

Currently translated at 99.8% (520 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/

* Translated using Weblate (Norwegian (old code))

Currently translated at 50.6% (264 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/no/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Galician)

Currently translated at 64.0% (157 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (2 of 2 strings)

Translation: Mastodon/Activerecord
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/activerecord/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (43 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/nl/

* Translated using Weblate (Norwegian (old code))

Currently translated at 100.0% (43 of 43 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/no/

* Translated using Weblate (Norwegian (old code))

Currently translated at 97.3% (73 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/no/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (56 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/nl/

* Translated using Weblate (Norwegian (old code))

Currently translated at 96.4% (54 of 56 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/no/

* Translated using Weblate (Galician)

Currently translated at 100.0% (245 of 245 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/

* Translated using Weblate (German)

Currently translated at 95.2% (496 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* Translated using Weblate (German)

Currently translated at 95.2% (496 of 521 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/de/

* i18n-tasks normalize && yarn manage:translations

* Fix things
2017-12-09 15:35:22 +01:00
Naoki Kosaka
4bce376fdc Missing require 'authorization_decorator'. (#5947) 2017-12-09 15:12:10 +01:00
Eugen Rochko
a865b62efc Rate limit by user instead of IP when API user is authenticated (#5923)
* Fix #668 - Rate limit by user instead of IP when API user is authenticated

* Fix code style issue

* Use request decorator provided by Doorkeeper
2017-12-09 14:20:02 +01:00
SerCom_KC
84cebad49d Update Chinese (Simplified) translations (#5937)
* i18n: (zh-CN) Improve translations for email notifications

* i18n: (zh-CN) Improve translations

* i18n: (zh-CN) Fix subject

* i18n: (zh-CN) Update translations for #5933
2017-12-09 14:19:45 +01:00
Yamagishi Kazutoshi
931e66e572 Back to Web UI from tag page when signed in (#5943) 2017-12-09 14:19:07 +01:00
Yamagishi Kazutoshi
cdae7e4c8b Move push notifications settings (regression from #5879) (#5941)
* Move push notifications settings

* fix typo `setf` -> `set`
2017-12-09 14:18:45 +01:00
Quenty31
3a52c90de1 l10n i18n OC update (#5939)
* update and corrections

* update (invites)

* Update oc.yml

* Update oc.yml
2017-12-09 14:17:34 +01:00
THE BOSS ♨
17e26f8afe Fix typo in paperclip.rb (#5936) 2017-12-09 13:59:59 +09:00
Eugen Rochko
2526ef10c2 Bump version to 2.1.0rc3 2017-12-09 02:42:59 +01:00
abcang
99242b92bc Keep WebPush settings (#5879) 2017-12-09 02:31:37 +01:00
Eugen Rochko
ec3b449baa Fix #5630 - Prevent duplicate load of favourites (#5931) 2017-12-09 02:22:13 +01:00
Eugen Rochko
2f4c5f504f Limit users to 50 lists, remove pagination from lists API (#5933) 2017-12-09 01:32:29 +01:00
Yamagishi Kazutoshi
f08e6e9ab5 Audio.prototype.seek is undefined (#5935) 2017-12-09 01:25:00 +01:00
Eugen Rochko
86b4d5439c Fix #5926 - Do not downgrade to OStatus once ActivityPub is known (#5929) 2017-12-09 01:24:47 +01:00
Eugen Rochko
c36b9cc5a6 Ensure link thumbnails are not stretched to super low quality (#5932) 2017-12-09 00:56:16 +01:00
Eugen Rochko
70ce2a2095 Polish video player CSS, add timer on fullscreen/modal/public pages (#5928) 2017-12-09 00:55:58 +01:00
Yamagishi Kazutoshi
b0db4dad79 Revert fog-aws (ref #5604) (#5934) 2017-12-09 00:47:52 +01:00
783 changed files with 22215 additions and 10382 deletions

View File

@@ -27,6 +27,7 @@ plugins:
enabled: true
eslint:
enabled: true
channel: eslint-4
rubocop:
enabled: true
scss-lint:

View File

@@ -11,10 +11,11 @@ DB_PASS=
DB_PORT=5432
# Federation
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
LOCAL_DOMAIN=example.com
LOCAL_HTTPS=true
# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links)
# 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

View File

@@ -17,11 +17,9 @@ plugins:
parserOptions:
sourceType: module
ecmaFeatures:
arrowFunctions: true
experimentalObjectRestSpread: true
jsx: true
destructuring: true
modules: true
spread: true
ecmaVersion: 2018
settings:
import/extensions:
@@ -114,6 +112,7 @@ rules:
react/self-closing-comp: error
jsx-a11y/accessible-emoji: warn
jsx-a11y/alt-text: warn
jsx-a11y/anchor-has-content: warn
jsx-a11y/aria-activedescendant-has-tabindex: warn
jsx-a11y/aria-props: warn
@@ -124,16 +123,22 @@ rules:
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/interactive-supports-focus: warn
jsx-a11y/label-has-for: off
jsx-a11y/mouse-events-have-key-events: warn
jsx-a11y/no-access-key: warn
jsx-a11y/no-distracting-elements: warn
jsx-a11y/no-noninteractive-element-interactions:
- warn
- handlers:
- onClick
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/no-static-element-interactions:
- warn
- handlers:
- onClick
jsx-a11y/role-has-required-aria-props: warn
jsx-a11y/role-supports-aria-props: off
jsx-a11y/scope: warn

View File

View File

@@ -1 +1 @@
2.4.2
2.5.0

View File

@@ -8,7 +8,7 @@ cache:
- public/packs-test
- tmp/cache/babel-loader
dist: trusty
sudo: required
sudo: false
notifications:
email: false
@@ -37,8 +37,8 @@ addons:
- yarn
rvm:
- 2.3.4
- 2.4.2
- 2.5.0
services:
- redis-server
@@ -49,8 +49,7 @@ install:
- yarn install
before_script:
- bundle exec rake parallel:create parallel:load_schema parallel:prepare
- bundle exec rails assets:precompile
- ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
script:

View File

@@ -1,4 +1,4 @@
FROM ruby:2.4.2-alpine3.6
FROM ruby:2.5.0-alpine3.7
LABEL maintainer="https://github.com/tootsuite/mastodon" \
description="A GNU Social-compatible microblogging server"
@@ -40,6 +40,7 @@ RUN apk -U upgrade \
protobuf \
su-exec \
tini \
tzdata \
&& update-ca-certificates \
&& mkdir -p /tmp/src /opt \
&& wget -O yarn.tar.gz "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
@@ -61,6 +62,9 @@ RUN apk -U upgrade \
&& rm -rf /tmp/* /var/cache/apk/*
COPY Gemfile Gemfile.lock package.json yarn.lock .yarnclean /mastodon/
COPY stack-fix.c /lib
RUN gcc -shared -fPIC /lib/stack-fix.c -o /lib/stack-fix.so
RUN rm /lib/stack-fix.c
RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
&& bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \

13
Gemfile
View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
source 'https://rubygems.org'
ruby '>= 2.3.0', '< 2.5.0'
ruby '>= 2.3.0', '< 2.6.0'
gem 'pkg-config', '~> 1.2'
@@ -14,12 +14,13 @@ gem 'pg', '~> 0.20'
gem 'pghero', '~> 1.7'
gem 'dotenv-rails', '~> 2.2'
gem 'fog-aws', '~> 1.4', require: false
gem 'aws-sdk', '~> 2.10', require: false
gem 'fog-core', '~> 1.45'
gem 'fog-local', '~> 0.4', require: false
gem 'fog-openstack', '~> 0.1', require: false
gem 'paperclip', '~> 5.1'
gem 'paperclip-av-transcoder', '~> 0.6'
gem 'posix-spawn'
gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.5'
@@ -28,15 +29,15 @@ gem 'browser'
gem 'charlock_holmes', '~> 0.7.5'
gem 'iso-639'
gem 'cld3', '~> 3.2.0'
gem 'devise', '~> 4.2'
gem 'devise', '~> 4.4'
gem 'devise-two-factor', '~> 3.0'
gem 'doorkeeper', '~> 4.2'
gem 'fast_blank', '~> 1.0'
gem 'goldfinger', '~> 2.0'
gem 'goldfinger', '~> 2.1'
gem 'hiredis', '~> 0.6'
gem 'redis-namespace', '~> 1.5'
gem 'htmlentities', '~> 4.3'
gem 'http', '~> 2.2'
gem 'http', '~> 3.0'
gem 'http_accept_language', '~> 2.1'
gem 'httplog', '~> 0.99'
gem 'idn-ruby', require: 'idn'
@@ -49,6 +50,7 @@ gem 'oj', '~> 3.3'
gem 'ostatus2', '~> 2.0'
gem 'ox', '~> 2.8'
gem 'pundit', '~> 1.1'
gem 'premailer-rails'
gem 'rack-attack', '~> 5.0'
gem 'rack-cors', '~> 0.4', require: 'rack/cors'
gem 'rack-timeout', '~> 0.4'
@@ -58,6 +60,7 @@ gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis']
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 0.10'
gem 'ruby-oembed', '~> 0.12', require: 'oembed'
gem 'ruby-progressbar', '~> 1.4'
gem 'sanitize', '~> 4.4'
gem 'sidekiq', '~> 5.0'
gem 'sidekiq-scheduler', '~> 2.1'

View File

@@ -57,12 +57,20 @@ GEM
encryptor (~> 3.0.0)
av (0.9.0)
cocaine (~> 0.5.3)
aws-sdk (2.10.100)
aws-sdk-resources (= 2.10.100)
aws-sdk-core (2.10.100)
aws-sigv4 (~> 1.0)
jmespath (~> 1.0)
aws-sdk-resources (2.10.100)
aws-sdk-core (= 2.10.100)
aws-sigv4 (1.0.2)
bcrypt (3.1.11)
better_errors (2.4.0)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
binding_of_caller (0.7.3)
binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
bootsnap (1.1.5)
msgpack (~> 1.0)
@@ -102,7 +110,7 @@ GEM
activesupport
charlock_holmes (0.7.5)
chunky_png (1.3.8)
cld3 (3.2.1)
cld3 (3.2.2)
ffi (>= 1.1.0, < 1.10.0)
climate_control (0.2.0)
cocaine (0.5.8)
@@ -114,8 +122,10 @@ GEM
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.3)
css_parser (1.6.0)
addressable
debug_inspector (0.0.3)
devise (4.3.0)
devise (4.4.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.2)
@@ -152,11 +162,6 @@ GEM
i18n (~> 0.5)
fast_blank (1.0.0)
ffi (1.9.18)
fog-aws (1.4.1)
fog-core (~> 1.38)
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
fog-core (1.45.0)
builder
excon (~> 0.58)
@@ -170,18 +175,15 @@ GEM
fog-core (>= 1.40)
fog-json (>= 1.0)
ipaddress (>= 0.8)
fog-xml (0.1.3)
fog-core
nokogiri (>= 1.5.11, < 2.0.0)
formatador (0.2.5)
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
globalid (0.4.1)
activesupport (>= 4.2.0)
goldfinger (2.0.1)
goldfinger (2.1.0)
addressable (~> 2.5)
http (~> 2.2)
http (~> 3.0)
nokogiri (~> 1.8)
oj (~> 3.0)
hamlit (2.8.5)
@@ -200,14 +202,14 @@ GEM
hiredis (0.6.1)
hkdf (0.3.0)
htmlentities (4.3.4)
http (2.2.2)
http (3.0.0)
addressable (~> 2.3)
http-cookie (~> 1.0)
http-form_data (~> 1.0.1)
http-form_data (>= 2.0.0.pre.pre2, < 3)
http_parser.rb (~> 0.6.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
http-form_data (1.0.3)
http-form_data (2.0.0)
http_accept_language (2.1.1)
http_parser.rb (0.6.0)
httplog (0.99.7)
@@ -228,6 +230,7 @@ GEM
idn-ruby (0.1.0)
ipaddress (0.8.3)
iso-639 (0.2.8)
jmespath (1.3.1)
json (2.1.0)
json-ld (2.1.7)
multi_json (~> 1.12)
@@ -297,14 +300,12 @@ GEM
concurrent-ruby (~> 1.0.0)
sidekiq (>= 3.5.0)
statsd-ruby (~> 1.2.0)
oj (3.3.9)
openssl (2.0.6)
oj (3.3.10)
orm_adapter (0.5.0)
ostatus2 (2.0.1)
addressable (~> 2.4)
http (~> 2.0)
nokogiri (~> 1.6)
openssl (~> 2.0)
ostatus2 (2.0.3)
addressable (~> 2.5)
http (~> 3.0)
nokogiri (~> 1.8)
ox (2.8.2)
paperclip (5.1.0)
activemodel (>= 4.2.0)
@@ -324,7 +325,15 @@ GEM
pghero (1.7.0)
activerecord
pkg-config (1.2.8)
posix-spawn (0.3.13)
powerpack (0.1.1)
premailer (1.11.1)
addressable
css_parser (>= 1.6.0)
htmlentities (>= 4.0.0)
premailer-rails (1.10.1)
actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
@@ -544,6 +553,7 @@ DEPENDENCIES
active_record_query_trace (~> 1.5)
addressable (~> 2.5)
annotate (~> 2.7)
aws-sdk (~> 2.10)
better_errors (~> 2.4)
binding_of_caller (~> 0.7)
bootsnap
@@ -559,23 +569,22 @@ DEPENDENCIES
charlock_holmes (~> 0.7.5)
cld3 (~> 3.2.0)
climate_control (~> 0.2)
devise (~> 4.2)
devise (~> 4.4)
devise-two-factor (~> 3.0)
doorkeeper (~> 4.2)
dotenv-rails (~> 2.2)
fabrication (~> 2.18)
faker (~> 1.7)
fast_blank (~> 1.0)
fog-aws (~> 1.4)
fog-core (~> 1.45)
fog-local (~> 0.4)
fog-openstack (~> 0.1)
fuubar (~> 2.2)
goldfinger (~> 2.0)
goldfinger (~> 2.1)
hamlit-rails (~> 0.2)
hiredis (~> 0.6)
htmlentities (~> 4.3)
http (~> 2.2)
http (~> 3.0)
http_accept_language (~> 2.1)
httplog (~> 0.99)
i18n-tasks (~> 0.9)
@@ -601,6 +610,8 @@ DEPENDENCIES
pg (~> 0.20)
pghero (~> 1.7)
pkg-config (~> 1.2)
posix-spawn
premailer-rails
pry-rails (~> 0.3)
puma (~> 3.10)
pundit (~> 1.1)
@@ -620,6 +631,7 @@ DEPENDENCIES
rspec-sidekiq (~> 3.0)
rubocop
ruby-oembed (~> 0.12)
ruby-progressbar (~> 1.4)
sanitize (~> 4.4)
scss_lint (~> 0.55)
sidekiq (~> 5.0)
@@ -639,7 +651,7 @@ DEPENDENCIES
webpush
RUBY VERSION
ruby 2.4.2p198
ruby 2.5.0p0
BUNDLED WITH
1.16.0
1.16.1

2
Vagrantfile vendored
View File

@@ -79,7 +79,7 @@ VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.box = "ubuntu/xenial64"
config.vm.provider :virtualbox do |vb|
vb.name = "mastodon"

View File

@@ -2,7 +2,8 @@
class AccountsController < ApplicationController
include AccountControllerConcern
include SignatureVerification
before_action :set_cache_headers
def show
respond_to do |format|
@@ -27,10 +28,11 @@ class AccountsController < ApplicationController
end
format.json do
render json: @account,
serializer: ActivityPub::ActorSerializer,
adapter: ActivityPub::Adapter,
content_type: 'application/activity+json'
skip_session!
render_cached_json(['activitypub', 'actor', @account.cache_key], content_type: 'application/activity+json') do
ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
end
end
end
end
@@ -49,7 +51,7 @@ class AccountsController < ApplicationController
end
def default_statuses
@account.statuses.where(visibility: [:public, :unlisted])
@account.statuses.not_local_only.where(visibility: [:public, :unlisted])
end
def only_media_scope

View File

@@ -28,7 +28,7 @@ class ActivityPub::InboxesController < Api::BaseController
def upgrade_account
if signed_request_account.ostatus?
signed_request_account.update(last_webfingered_at: nil)
ResolveRemoteAccountWorker.perform_async(signed_request_account.acct)
ResolveAccountWorker.perform_async(signed_request_account.acct)
end
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed?

View File

@@ -89,7 +89,8 @@ module Admin
:username,
:display_name,
:email,
:ip
:ip,
:staff
)
end
end

View File

@@ -3,6 +3,7 @@
module Admin
class CustomEmojisController < BaseController
before_action :set_custom_emoji, except: [:index, :new, :create]
before_action :set_filter_params
def index
authorize :custom_emoji, :index?
@@ -32,23 +33,26 @@ module Admin
if @custom_emoji.update(resource_params)
log_action :update, @custom_emoji
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.updated_msg')
flash[:notice] = I18n.t('admin.custom_emojis.updated_msg')
else
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.update_failed_msg')
flash[:alert] = I18n.t('admin.custom_emojis.update_failed_msg')
end
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
end
def destroy
authorize @custom_emoji, :destroy?
@custom_emoji.destroy!
log_action :destroy, @custom_emoji
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg')
flash[:notice] = I18n.t('admin.custom_emojis.destroyed_msg')
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
end
def copy
authorize @custom_emoji, :copy?
emoji = CustomEmoji.find_or_initialize_by(domain: nil, shortcode: @custom_emoji.shortcode)
emoji = CustomEmoji.find_or_initialize_by(domain: nil,
shortcode: @custom_emoji.shortcode)
emoji.image = @custom_emoji.image
if emoji.save
@@ -58,21 +62,23 @@ module Admin
flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg')
end
redirect_to admin_custom_emojis_path(page: params[:page])
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
end
def enable
authorize @custom_emoji, :enable?
@custom_emoji.update!(disabled: false)
log_action :enable, @custom_emoji
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.enabled_msg')
flash[:notice] = I18n.t('admin.custom_emojis.enabled_msg')
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
end
def disable
authorize @custom_emoji, :disable?
@custom_emoji.update!(disabled: true)
log_action :disable, @custom_emoji
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.disabled_msg')
flash[:notice] = I18n.t('admin.custom_emojis.disabled_msg')
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
end
private
@@ -81,6 +87,10 @@ module Admin
@custom_emoji = CustomEmoji.find(params[:id])
end
def set_filter_params
@filter_params = filter_params.to_hash.symbolize_keys
end
def resource_params
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
end
@@ -92,7 +102,9 @@ module Admin
def filter_params
params.permit(
:local,
:remote
:remote,
:by_domain,
:shortcode
)
end
end

View File

@@ -17,6 +17,8 @@ module Admin
bootstrap_timeline_accounts
thumbnail
min_invite_role
activity_api_enabled
peers_api_enabled
).freeze
BOOLEAN_SETTINGS = %w(
@@ -24,6 +26,8 @@ module Admin
open_deletion
timeline_preview
show_staff_badge
activity_api_enabled
peers_api_enabled
).freeze
UPLOAD_SETTINGS = %w(

View File

@@ -6,8 +6,8 @@ class Api::BaseController < ApplicationController
include RateLimitHeaders
skip_before_action :verify_authenticity_token
skip_before_action :store_current_location
protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
render json: { error: e.to_s }, status: 422
@@ -72,19 +72,4 @@ class Api::BaseController < ApplicationController
def render_empty
render json: {}, status: 200
end
def set_maps(statuses) # rubocop:disable Style/AccessorMethodName
if current_account.nil?
@reblogs_map = {}
@favourites_map = {}
@mutes_map = {}
return
end
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)
@favourites_map = Status.favourites_map(status_ids, current_account)
@mutes_map = Status.mutes_map(conversation_ids, current_account)
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
class Api::V1::Accounts::ListsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read }
before_action :require_user!
before_action :set_account
respond_to :json
def index
@lists = @account.lists.where(account: current_account)
render json: @lists, each_serializer: REST::ListSerializer
end
private
def set_account
@account = Account.find(params[:account_id])
end
end

View File

@@ -21,12 +21,12 @@ class Api::V1::AccountsController < Api::BaseController
end
def block
BlockService.new.call(current_user.account, @account)
BlockService.new.call(current_user.account, @account, note: params[:note])
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end
def mute
MuteService.new.call(current_user.account, @account, notifications: params[:notifications])
MuteService.new.call(current_user.account, @account, notifications: params[:notifications], note: params[:note])
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end

View File

@@ -0,0 +1,36 @@
# frozen_string_literal: true
class Api::V1::Instances::ActivityController < Api::BaseController
before_action :require_enabled_api!
respond_to :json
def show
render_cached_json('api:v1:instances:activity:show', expires_in: 1.day) { activity }
end
private
def activity
weeks = []
12.times do |i|
day = i.weeks.ago.to_date
week_id = day.cweek
week = Date.commercial(day.cwyear, week_id)
weeks << {
week: week.to_time.to_i.to_s,
statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0',
logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s,
registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0',
}
end
weeks
end
def require_enabled_api!
head 404 unless Setting.activity_api_enabled
end
end

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
class Api::V1::Instances::PeersController < Api::BaseController
before_action :require_enabled_api!
respond_to :json
def index
render_cached_json('api:v1:instances:peers:index', expires_in: 1.day) { Account.remote.domains }
end
private
def require_enabled_api!
head 404 unless Setting.peers_api_enabled
end
end

View File

@@ -1,18 +1,14 @@
# frozen_string_literal: true
class Api::V1::ListsController < Api::BaseController
LISTS_LIMIT = 50
before_action -> { doorkeeper_authorize! :read }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :write }, except: [:index, :show]
before_action :require_user!
before_action :set_list, except: [:index, :create]
after_action :insert_pagination_headers, only: :index
def index
@lists = List.where(account: current_account).paginate_by_max_id(limit_param(LISTS_LIMIT), params[:max_id], params[:since_id])
@lists = List.where(account: current_account).all
render json: @lists, each_serializer: REST::ListSerializer
end
@@ -44,36 +40,4 @@ class Api::V1::ListsController < Api::BaseController
def list_params
params.permit(:title)
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path
if records_continue?
api_v1_lists_url pagination_params(max_id: pagination_max_id)
end
end
def prev_path
unless @lists.empty?
api_v1_lists_url pagination_params(since_id: pagination_since_id)
end
end
def pagination_max_id
@lists.last.id
end
def pagination_since_id
@lists.first.id
end
def records_continue?
@lists.size == limit_param(LISTS_LIMIT)
end
def pagination_params(core_params)
params.permit(:limit).merge(core_params)
end
end

View File

@@ -9,7 +9,11 @@ class Api::V1::Timelines::HomeController < Api::BaseController
def show
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render json: @statuses,
each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
status: regeneration_in_progress? ? 206 : 200
end
private
@@ -57,4 +61,8 @@ class Api::V1::Timelines::HomeController < Api::BaseController
def pagination_since_id
@statuses.first.id
end
def regeneration_in_progress?
Redis.current.exists("account:#{current_account.id}:regeneration")
end
end

View File

@@ -4,6 +4,7 @@ class Api::Web::PushSubscriptionsController < Api::BaseController
respond_to :json
before_action :require_user!
protect_from_forgery with: :exception
def create
params.require(:subscription).require(:endpoint)
@@ -28,6 +29,8 @@ class Api::Web::PushSubscriptionsController < Api::BaseController
},
}
data.deep_merge!(params[:data]) if params[:data]
web_subscription = ::Web::PushSubscription.create!(
endpoint: params[:subscription][:endpoint],
key_p256dh: params[:subscription][:keys][:p256dh],

View File

@@ -31,7 +31,7 @@ class ApplicationController < ActionController::Base
private
def https_enabled?
Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true'
Rails.env.production?
end
def store_current_location
@@ -192,8 +192,31 @@ class ApplicationController < ActionController::Base
format.any { head code }
format.html do
set_locale
use_pack 'error'
render "errors/#{code}", layout: 'error', status: code
end
end
end
def render_cached_json(cache_key, **options)
options[:expires_in] ||= 3.minutes
cache_key = cache_key.join(':') if cache_key.is_a?(Enumerable)
cache_public = options.key?(:public) ? options.delete(:public) : true
content_type = options.delete(:content_type) || 'application/json'
data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do
yield.to_json
end
expires_in options[:expires_in], public: cache_public
render json: data, content_type: content_type
end
def set_cache_headers
response.headers['Vary'] = 'Accept'
end
def skip_session!
request.session_options[:skip] = true
end
end

View File

@@ -2,14 +2,9 @@
class Auth::ConfirmationsController < Devise::ConfirmationsController
layout 'auth'
before_action :set_pack
def show
super do |user|
BootstrapTimelineWorker.perform_async(user.account_id) if user.errors.empty?
end
end
private
def set_pack

View File

@@ -38,6 +38,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController
new_user_session_path
end
def after_update_path_for(_resource)
edit_user_registration_path
end
def check_enabled_registrations
redirect_to root_path if single_user_mode? || !allowed_registrations?
end

View File

@@ -8,8 +8,8 @@ class Auth::SessionsController < Devise::SessionsController
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 :set_pack
before_action :set_instance_presenter, only: [:new]
before_action :set_pack
def create
super do |resource|

View File

@@ -5,6 +5,7 @@ class AuthorizeFollowsController < ApplicationController
before_action :authenticate_user!
before_action :set_pack
before_action :set_body_classes
def show
@account = located_account || render(:error)
@@ -45,7 +46,7 @@ class AuthorizeFollowsController < ApplicationController
end
def account_from_remote_follow
ResolveRemoteAccountService.new.call(acct_without_prefix)
ResolveAccountService.new.call(acct_without_prefix)
end
def acct_param_is_url?
@@ -63,4 +64,8 @@ class AuthorizeFollowsController < ApplicationController
def acct_params
params.fetch(:acct, '')
end
def set_body_classes
@body_classes = 'modal-layout'
end
end

View File

@@ -44,7 +44,8 @@ module RateLimitHeaders
end
def api_throttle_data
request.env['rack.attack.throttle_data']['api']
most_limited_type, = request.env['rack.attack.throttle_data'].min_by { |_, v| v[:limit] }
request.env['rack.attack.throttle_data'][most_limited_type]
end
def request_time

View File

@@ -114,7 +114,7 @@ module SignatureVerification
def account_from_key_id(key_id)
if key_id.start_with?('acct:')
ResolveRemoteAccountService.new.call(key_id.gsub(/\Aacct:/, ''))
ResolveAccountService.new.call(key_id.gsub(/\Aacct:/, ''))
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
account = ActivityPub::TagManager.instance.uri_to_resource(key_id, Account)
account ||= ActivityPub::FetchRemoteKeyService.new.call(key_id, id: false)

View File

@@ -3,7 +3,6 @@
module UserTrackingConcern
extend ActiveSupport::Concern
REGENERATE_FEED_DAYS = 14
UPDATE_SIGN_IN_HOURS = 24
included do
@@ -14,24 +13,10 @@ module UserTrackingConcern
def set_user_activity
return unless user_needs_sign_in_update?
# Mark as signed-in today
current_user.update_tracked_fields!(request)
# Regenerate feed if needed
regenerate_feed! if user_needs_feed_update?
end
def user_needs_sign_in_update?
user_signed_in? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < UPDATE_SIGN_IN_HOURS.hours.ago)
end
def user_needs_feed_update?
current_user.last_sign_in_at < REGENERATE_FEED_DAYS.days.ago
end
def regenerate_feed!
Redis.current.setnx("account:#{current_user.account_id}:regeneration", true) == 1 && Redis.current.expire("account:#{current_user.account_id}:regeneration", 3_600 * 24)
RegenerationWorker.perform_async(current_user.account_id)
end
end

View File

@@ -2,14 +2,16 @@
class EmojisController < ApplicationController
before_action :set_emoji
before_action :set_cache_headers
def show
respond_to do |format|
format.json do
render json: @emoji,
serializer: ActivityPub::EmojiSerializer,
adapter: ActivityPub::Adapter,
content_type: 'application/activity+json'
skip_session!
render_cached_json(['activitypub', 'emoji', @emoji.cache_key], content_type: 'application/activity+json') do
ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter)
end
end
end
end

View File

@@ -5,6 +5,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_action :store_current_location
before_action :authenticate_resource_owner!
before_action :set_pack
include Localized
@@ -13,4 +14,8 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
def store_current_location
store_location_for(:user, request.url)
end
def set_pack
use_pack 'auth'
end
end

View File

@@ -5,6 +5,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
before_action :store_current_location
before_action :authenticate_resource_owner!
before_action :set_pack
include Localized
@@ -13,4 +14,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def store_current_location
store_location_for(:user, request.url)
end
def set_pack
use_pack 'settings'
end
end

View File

@@ -43,4 +43,8 @@ class RemoteFollowController < ApplicationController
def suspended_account?
@account.suspended?
end
def set_body_classes
@body_classes = 'modal-layout'
end
end

View File

@@ -1,13 +1,12 @@
# frozen_string_literal: true
class Settings::FlavoursController < Settings::BaseController
def index
redirect_to action: 'show', flavour: current_flavour
end
def show
unless Themes.instance.flavours.include?(params[:flavour]) or params[:flavour] == current_flavour
unless Themes.instance.flavours.include?(params[:flavour]) || (params[:flavour] == current_flavour)
redirect_to action: 'show', flavour: current_flavour
end
@@ -16,7 +15,7 @@ class Settings::FlavoursController < Settings::BaseController
end
def update
user_settings.update(user_settings_params(params[:flavour]).to_h)
user_settings.update(user_settings_params)
redirect_to action: 'show', flavour: params[:flavour]
end
@@ -26,10 +25,8 @@ class Settings::FlavoursController < Settings::BaseController
UserSettingsDecorator.new(current_user)
end
def user_settings_params(flavour)
params.require(:user).merge({ setting_flavour: flavour }).permit(
:setting_flavour,
:setting_skin
)
def user_settings_params
{ setting_flavour: params.require(:flavour),
setting_skin: params.dig(:user, :setting_skin) }.with_indifferent_access
end
end

View File

@@ -28,6 +28,7 @@ class Settings::MigrationsController < ApplicationController
end
def migration_account_changed?
current_account.moved_to_account_id != @migration.account&.id
current_account.moved_to_account_id != @migration.account&.id &&
current_account.id != @migration.account&.id
end
end

View File

@@ -2,10 +2,8 @@
module Settings
module TwoFactorAuthentication
class ConfirmationsController < ApplicationController
layout 'admin'
before_action :authenticate_user!
class ConfirmationsController < BaseController
before_action :ensure_otp_secret
def new
prepare_two_factor_form
@@ -38,6 +36,10 @@ module Settings
@provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain)
@qrcode = RQRCode::QRCode.new(@provision_url)
end
def ensure_otp_secret
redirect_to settings_two_factor_authentication_path unless current_user.otp_secret
end
end
end
end

View File

@@ -2,11 +2,7 @@
module Settings
module TwoFactorAuthentication
class RecoveryCodesController < ApplicationController
layout 'admin'
before_action :authenticate_user!
class RecoveryCodesController < BaseController
def create
@recovery_codes = current_user.generate_otp_backup_codes!
current_user.save!

View File

@@ -15,13 +15,14 @@ class SharesController < ApplicationController
private
def initial_state_params
text = [params[:title], params[:text], params[:url]].compact.join(' ')
{
settings: Web::Setting.find_by(user: current_user)&.data || {},
push_subscription: current_account.user.web_push_subscription(current_session),
current_account: current_account,
token: current_session.token,
admin: Account.find_local(Setting.site_contact_username),
text: params[:text],
text: text,
}
end
@@ -30,6 +31,6 @@ class SharesController < ApplicationController
end
def set_body_classes
@body_classes = 'compose-standalone'
@body_classes = 'modal-layout compose-standalone'
end
end

View File

@@ -10,6 +10,7 @@ class StatusesController < ApplicationController
before_action :set_link_headers
before_action :check_account_suspension
before_action :redirect_to_original, only: [:show]
before_action :set_cache_headers
def show
respond_to do |format|
@@ -22,19 +23,21 @@ class StatusesController < ApplicationController
end
format.json do
render json: @status,
serializer: ActivityPub::NoteSerializer,
adapter: ActivityPub::Adapter,
content_type: 'application/activity+json'
skip_session! unless @stream_entry.hidden?
render_cached_json(['activitypub', 'note', @status.cache_key], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do
ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter)
end
end
end
end
def activity
render json: @status,
serializer: ActivityPub::ActivitySerializer,
adapter: ActivityPub::Adapter,
content_type: 'application/activity+json'
skip_session!
render_cached_json(['activitypub', 'activity', @status.cache_key], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do
ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter)
end
end
def embed

View File

@@ -1,15 +1,19 @@
# frozen_string_literal: true
module WellKnown
class HostMetaController < ApplicationController
class HostMetaController < ActionController::Base
include RoutingHelper
before_action { response.headers['Vary'] = 'Accept' }
def show
@webfinger_template = "#{webfinger_url}?resource={uri}"
respond_to do |format|
format.xml { render content_type: 'application/xrd+xml' }
end
expires_in(3.days, public: true)
end
end
end

View File

@@ -1,9 +1,11 @@
# frozen_string_literal: true
module WellKnown
class WebfingerController < ApplicationController
class WebfingerController < ActionController::Base
include RoutingHelper
before_action { response.headers['Vary'] = 'Accept' }
def show
@account = Account.find_local!(username_from_resource)
@@ -16,6 +18,8 @@ module WellKnown
render content_type: 'application/xrd+xml'
end
end
expires_in(3.days, public: true)
rescue ActiveRecord::RecordNotFound
head 404
end

View File

@@ -34,7 +34,7 @@ module Admin::ActionLogsHelper
link_to attributes['domain'], "https://#{attributes['domain']}"
when 'Status'
tmp_status = Status.new(attributes)
link_to tmp_status.account.acct, TagManager.instance.url_for(tmp_status)
link_to tmp_status.account&.acct || "##{tmp_status.account_id}", TagManager.instance.url_for(tmp_status)
end
end

View File

@@ -1,11 +1,12 @@
# frozen_string_literal: true
module Admin::FilterHelper
ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent username display_name email ip).freeze
REPORT_FILTERS = %i(resolved account_id target_account_id).freeze
INVITE_FILTER = %i(available expired).freeze
ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent username display_name email ip staff).freeze
REPORT_FILTERS = %i(resolved account_id target_account_id).freeze
INVITE_FILTER = %i(available expired).freeze
CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze
FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER
FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS
def filter_link_to(text, link_to_params, link_class_params = link_to_params)
new_url = filtered_url_for(link_to_params)

View File

@@ -6,6 +6,6 @@ module InstanceHelper
end
def site_hostname
Rails.configuration.x.local_domain
@site_hostname ||= Addressable::URI.parse("//#{Rails.configuration.x.local_domain}").display_uri.host
end
end

View File

@@ -39,6 +39,10 @@ module JsonLdHelper
!json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT)
end
def unsupported_uri_scheme?(uri)
!uri.start_with?('http://', 'https://')
end
def canonicalize(json)
graph = RDF::Graph.new << JSON::LD::API.toRdf(json)
graph.dump(:normalize)

View File

@@ -0,0 +1,4 @@
# frozen_string_literal: true
module MailerHelper
end

View File

@@ -4,6 +4,7 @@ module RoutingHelper
extend ActiveSupport::Concern
include Rails.application.routes.url_helpers
include ActionView::Helpers::AssetTagHelper
include Webpacker::Helper
included do
def default_url_options
@@ -17,6 +18,10 @@ module RoutingHelper
URI.join(root_url, source).to_s
end
def full_pack_url(source, **options)
full_asset_url(asset_pack_path(source, options))
end
private
def use_storage?

View File

@@ -10,11 +10,13 @@ module SettingsHelper
eo: 'Esperanto',
es: 'Español',
fa: 'فارسی',
gl: 'Galego',
fi: 'Suomi',
fr: 'Français',
he: 'עברית',
hr: 'Hrvatski',
hu: 'Magyar',
hy: 'Հայերեն',
id: 'Bahasa Indonesia',
io: 'Ido',
it: 'Italiano',
@@ -27,6 +29,9 @@ module SettingsHelper
pt: 'Português',
'pt-BR': 'Português do Brasil',
ru: 'Русский',
sk: 'Slovensky',
sr: 'Српски',
'sr-Latn': 'Srpski (latinica)',
sv: 'Svenska',
th: 'ภาษาไทย',
tr: 'Türkçe',

View File

@@ -30,13 +30,13 @@ delegate(document, batchCheckboxClassName, 'change', () => {
});
delegate(document, '.media-spoiler-show-button', 'click', () => {
[].forEach.call(document.querySelectorAll('.activity-stream .media-spoiler-wrapper'), (content) => {
content.classList.add('media-spoiler-wrapper__visible');
[].forEach.call(document.querySelectorAll('button.media-spoiler'), (element) => {
element.click();
});
});
delegate(document, '.media-spoiler-hide-button', 'click', () => {
[].forEach.call(document.querySelectorAll('.activity-stream .media-spoiler-wrapper'), (content) => {
content.classList.remove('media-spoiler-wrapper__visible');
[].forEach.call(document.querySelectorAll('.spoiler-button.spoiler-button--visible button'), (element) => {
element.click();
});
});

View File

@@ -0,0 +1 @@
import 'styles/mailer.scss';

View File

@@ -10,6 +10,9 @@ pack:
embed: embed.js
error:
home:
mailer:
filename: mailer.js
stylesheet: true
modal:
public: public.js
settings: settings.js

View File

@@ -12,6 +12,7 @@ import {
} from './timelines';
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
export const COMPOSE_CYCLE_ELEFRIEND = 'COMPOSE_CYCLE_ELEFRIEND';
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
@@ -38,7 +39,6 @@ export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
export const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE';
export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
@@ -55,6 +55,12 @@ export function changeCompose(text) {
};
};
export function cycleElefriendCompose() {
return {
type: COMPOSE_CYCLE_ELEFRIEND,
};
};
export function replyCompose(status, router) {
return (dispatch, getState) => {
dispatch({
@@ -62,7 +68,7 @@ export function replyCompose(status, router) {
status: status,
});
if (!getState().getIn(['compose', 'mounted'])) {
if (router && !getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new');
}
};
@@ -119,6 +125,11 @@ export function submitCompose() {
}).then(function (response) {
dispatch(submitComposeSuccess({ ...response.data }));
// If the response has no data then we can't do anything else.
if (!response.data) {
return;
}
// To make the app more responsive, immediately get the status into the columns
const insertOrRefresh = (timelineId, refreshAction) => {
@@ -316,21 +327,14 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
export function selectComposeSuggestion(position, token, suggestion) {
return (dispatch, getState) => {
let completion, startPosition;
if (typeof suggestion === 'object' && suggestion.id) {
completion = suggestion.native || suggestion.colons;
startPosition = position - 1;
dispatch(useEmoji(suggestion));
} else {
completion = getState().getIn(['accounts', suggestion, 'acct']);
startPosition = position;
}
const completion = typeof suggestion === 'object' && suggestion.id ? (
dispatch(useEmoji(suggestion)),
suggestion.native || suggestion.colons
) : '@' + getState().getIn(['accounts', suggestion, 'acct']);
dispatch({
type: COMPOSE_SUGGESTION_SELECT,
position: startPosition,
position,
token,
completion,
});
@@ -349,10 +353,11 @@ export function unmountCompose() {
};
};
export function toggleComposeAdvancedOption(option) {
export function changeComposeAdvancedOption(option, value) {
return {
option,
type: COMPOSE_ADVANCED_OPTIONS_CHANGE,
option: option,
value,
};
}
@@ -389,10 +394,3 @@ export function insertEmojiCompose(position, emoji) {
emoji,
};
};
export function changeComposing(value) {
return {
type: COMPOSE_COMPOSING_CHANGE,
value,
};
}

View File

@@ -10,6 +10,10 @@ export const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FA
export function fetchFavouritedStatuses() {
return (dispatch, getState) => {
if (getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
return;
}
dispatch(fetchFavouritedStatusesRequest());
api(getState).get('/api/v1/favourites').then(response => {
@@ -46,7 +50,7 @@ export function expandFavouritedStatuses() {
return (dispatch, getState) => {
const url = getState().getIn(['status_lists', 'favourites', 'next'], null);
if (url === null) {
if (url === null || getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
return;
}

View File

@@ -42,7 +42,7 @@ const fetchRelatedRelationships = (dispatch, notifications) => {
const unescapeHTML = (html) => {
const wrapper = document.createElement('div');
html = html.replace(/<br \/>|<br>|\n/, ' ');
html = html.replace(/<br \/>|<br>|\n/g, ' ');
wrapper.innerHTML = html;
return wrapper.textContent;
};

View File

@@ -0,0 +1,23 @@
import {
SET_BROWSER_SUPPORT,
SET_SUBSCRIPTION,
CLEAR_SUBSCRIPTION,
SET_ALERTS,
setAlerts,
} from './setter';
import { register, saveSettings } from './registerer';
export {
SET_BROWSER_SUPPORT,
SET_SUBSCRIPTION,
CLEAR_SUBSCRIPTION,
SET_ALERTS,
register,
};
export function changeAlerts(path, value) {
return dispatch => {
dispatch(setAlerts(path, value));
dispatch(saveSettings());
};
}

View File

@@ -0,0 +1,149 @@
import api from 'flavours/glitch/util/api';
import { pushNotificationsSetting } from 'flavours/glitch/util/settings';
import { setBrowserSupport, setSubscription, clearSubscription } from './setter';
// Taken from https://www.npmjs.com/package/web-push
const urlBase64ToUint8Array = (base64String) => {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content');
const getRegistration = () => navigator.serviceWorker.ready;
const getPushSubscription = (registration) =>
registration.pushManager.getSubscription()
.then(subscription => ({ registration, subscription }));
const subscribe = (registration) =>
registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(getApplicationServerKey()),
});
const unsubscribe = ({ registration, subscription }) =>
subscription ? subscription.unsubscribe().then(() => registration) : registration;
const sendSubscriptionToBackend = (getState, subscription, me) => {
const params = { subscription };
if (me) {
const data = pushNotificationsSetting.get(me);
if (data) {
params.data = data;
}
}
return api(getState).post('/api/web/push_subscriptions', params).then(response => response.data);
};
// Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload
const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype);
export function register () {
return (dispatch, getState) => {
dispatch(setBrowserSupport(supportsPushNotifications));
const me = getState().getIn(['meta', 'me']);
if (me && !pushNotificationsSetting.get(me)) {
const alerts = getState().getIn(['push_notifications', 'alerts']);
if (alerts) {
pushNotificationsSetting.set(me, { alerts: alerts });
}
}
if (supportsPushNotifications) {
if (!getApplicationServerKey()) {
console.error('The VAPID public key is not set. You will not be able to receive Web Push Notifications.');
return;
}
getRegistration()
.then(getPushSubscription)
.then(({ registration, subscription }) => {
if (subscription !== null) {
// We have a subscription, check if it is still valid
const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey)).toString();
const subscriptionServerKey = urlBase64ToUint8Array(getApplicationServerKey()).toString();
const serverEndpoint = getState().getIn(['push_notifications', 'subscription', 'endpoint']);
// If the VAPID public key did not change and the endpoint corresponds
// to the endpoint saved in the backend, the subscription is valid
if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint) {
return subscription;
} else {
// Something went wrong, try to subscribe again
return unsubscribe({ registration, subscription }).then(subscribe).then(
subscription => sendSubscriptionToBackend(getState, subscription, me));
}
}
// No subscription, try to subscribe
return subscribe(registration).then(
subscription => sendSubscriptionToBackend(getState, subscription, me));
})
.then(subscription => {
// If we got a PushSubscription (and not a subscription object from the backend)
// it means that the backend subscription is valid (and was set during hydration)
if (!(subscription instanceof PushSubscription)) {
dispatch(setSubscription(subscription));
if (me) {
pushNotificationsSetting.set(me, { alerts: subscription.alerts });
}
}
})
.catch(error => {
if (error.code === 20 && error.name === 'AbortError') {
console.warn('Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.');
} else if (error.code === 5 && error.name === 'InvalidCharacterError') {
console.error('The VAPID public key seems to be invalid:', getApplicationServerKey());
}
// Clear alerts and hide UI settings
dispatch(clearSubscription());
if (me) {
pushNotificationsSetting.remove(me);
}
try {
getRegistration()
.then(getPushSubscription)
.then(unsubscribe);
} catch (e) {
}
});
} else {
console.warn('Your browser does not support Web Push Notifications.');
}
};
}
export function saveSettings() {
return (_, getState) => {
const state = getState().get('push_notifications');
const subscription = state.get('subscription');
const alerts = state.get('alerts');
const data = { alerts };
api(getState).put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
data,
}).then(() => {
const me = getState().getIn(['meta', 'me']);
if (me) {
pushNotificationsSetting.set(me, data);
}
});
};
}

View File

@@ -1,9 +1,7 @@
import axios from 'axios';
export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT';
export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION';
export const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION';
export const ALERTS_CHANGE = 'PUSH_NOTIFICATIONS_ALERTS_CHANGE';
export const SET_ALERTS = 'PUSH_NOTIFICATIONS_SET_ALERTS';
export function setBrowserSupport (value) {
return {
@@ -25,28 +23,12 @@ export function clearSubscription () {
};
}
export function changeAlerts(key, value) {
export function setAlerts (path, value) {
return dispatch => {
dispatch({
type: ALERTS_CHANGE,
key,
type: SET_ALERTS,
path,
value,
});
dispatch(saveSettings());
};
}
export function saveSettings() {
return (_, getState) => {
const state = getState().get('push_notifications');
const subscription = state.get('subscription');
const alerts = state.get('alerts');
axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
data: {
alerts,
},
});
};
}

View File

@@ -1,14 +1,14 @@
import axios from 'axios';
import api from 'flavours/glitch/util/api';
import { debounce } from 'lodash';
export const SETTING_CHANGE = 'SETTING_CHANGE';
export const SETTING_SAVE = 'SETTING_SAVE';
export function changeSetting(key, value) {
export function changeSetting(path, value) {
return dispatch => {
dispatch({
type: SETTING_CHANGE,
key,
path,
value,
});
@@ -21,9 +21,9 @@ const debouncedSave = debounce((dispatch, getState) => {
return;
}
const data = getState().get('settings').filter((_, key) => key !== 'saved').toJS();
const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS();
axios.put('/api/web/settings', { data }).then(() => dispatch({ type: SETTING_SAVE }));
api(getState).put('/api/web/settings', { data }).then(() => dispatch({ type: SETTING_SAVE }));
}, 5000, { trailing: true });
export function saveSettings() {

View File

@@ -19,13 +19,14 @@ export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
export const TIMELINE_CONTEXT_UPDATE = 'CONTEXT_UPDATE';
export function refreshTimelineSuccess(timeline, statuses, skipLoading, next) {
export function refreshTimelineSuccess(timeline, statuses, skipLoading, next, partial) {
return {
type: TIMELINE_REFRESH_SUCCESS,
timeline,
statuses,
skipLoading,
next,
partial,
};
};
@@ -88,7 +89,7 @@ export function refreshTimeline(timelineId, path, params = {}) {
return function (dispatch, getState) {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
if (timeline.get('isLoading') || timeline.get('online')) {
if (timeline.get('isLoading') || (timeline.get('online') && !timeline.get('isPartial'))) {
return;
}
@@ -104,8 +105,12 @@ export function refreshTimeline(timelineId, path, params = {}) {
dispatch(refreshTimelineRequest(timelineId, skipLoading));
api(getState).get(path, { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(refreshTimelineSuccess(timelineId, response.data, skipLoading, next ? next.uri : null));
if (response.status === 206) {
dispatch(refreshTimelineSuccess(timelineId, [], skipLoading, null, true));
} else {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(refreshTimelineSuccess(timelineId, response.data, skipLoading, next ? next.uri : null, false));
}
}).catch(error => {
dispatch(refreshTimelineFail(timelineId, error, skipLoading));
});

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { Fragment } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import Avatar from './avatar';
@@ -27,8 +27,10 @@ export default class Account extends ImmutablePureComponent {
onFollow: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired,
onMuteNotifications: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
hidden: PropTypes.bool,
small: PropTypes.bool,
};
handleFollow = () => {
@@ -52,7 +54,12 @@ export default class Account extends ImmutablePureComponent {
}
render () {
const { account, intl, hidden } = this.props;
const {
account,
hidden,
intl,
small,
} = this.props;
if (!account) {
return <div />;
@@ -69,7 +76,7 @@ export default class Account extends ImmutablePureComponent {
let buttons;
if (account.get('id') !== me && account.get('relationship', null) !== null) {
if (account.get('id') !== me && !small && account.get('relationship', null) !== null) {
const following = account.getIn(['relationship', 'following']);
const requested = account.getIn(['relationship', 'requested']);
const blocking = account.getIn(['relationship', 'blocking']);
@@ -87,27 +94,45 @@ export default class Account extends ImmutablePureComponent {
hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username') })} onClick={this.handleMuteNotifications} />;
}
buttons = (
<div>
<Fragment>
<IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />
{hidingNotificationsButton}
</div>
</Fragment>
);
} else {
} else if (!account.get('moved')) {
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
}
}
return (
return small ? (
<Permalink
className='account small'
href={account.get('url')}
to={`/accounts/${account.get('id')}`}
>
<div className='account__avatar-wrapper'>
<Avatar
account={account}
size={24}
/>
</div>
<DisplayName
account={account}
inline
/>
</Permalink>
) : (
<div className='account'>
<div className='account__wrapper'>
<Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
<DisplayName account={account} />
</Permalink>
<div className='account__relationship'>
{buttons}
</div>
{buttons ?
<div className='account__relationship'>
{buttons}
</div>
: null}
</div>
</div>
);

View File

@@ -21,9 +21,9 @@ export default class AttachmentList extends ImmutablePureComponent {
<ul className='attachment-list__list'>
{media.map(attachment =>
<li key={attachment.get('id')}>
(<li key={attachment.get('id')}>
<a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a>
</li>
</li>)
)}
</ul>
</div>

View File

@@ -1,42 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light';
const assetHost = process.env.CDN_HOST || '';
export default class AutosuggestEmoji extends React.PureComponent {
static propTypes = {
emoji: PropTypes.object.isRequired,
};
render () {
const { emoji } = this.props;
let url;
if (emoji.custom) {
url = emoji.imageUrl;
} else {
const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')];
if (!mapping) {
return null;
}
url = `${assetHost}/emoji/${mapping.filename}.svg`;
}
return (
<div className='autosuggest-emoji'>
<img
className='emojione'
src={url}
alt={emoji.native || emoji.colons}
/>
{emoji.colons}
</div>
);
}
}

View File

@@ -1,223 +0,0 @@
import React from 'react';
import AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
import AutosuggestEmoji from './autosuggest_emoji';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { isRtl } from 'flavours/glitch/util/rtl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Textarea from 'react-textarea-autosize';
import classNames from 'classnames';
const textAtCursorMatchesToken = (str, caretPosition) => {
let word;
let left = str.slice(0, caretPosition).search(/[^\s\u200B]+$/);
let right = str.slice(caretPosition).search(/[\s\u200B]/);
if (right < 0) {
word = str.slice(left);
} else {
word = str.slice(left, right + caretPosition);
}
if (!word || word.trim().length < 3 || ['@', ':'].indexOf(word[0]) === -1) {
return [null, null];
}
word = word.trim().toLowerCase();
if (word.length > 0) {
return [left + 1, word];
} else {
return [null, null];
}
};
export default class AutosuggestTextarea extends ImmutablePureComponent {
static propTypes = {
value: PropTypes.string,
suggestions: ImmutablePropTypes.list,
disabled: PropTypes.bool,
placeholder: PropTypes.string,
onSuggestionSelected: PropTypes.func.isRequired,
onSuggestionsClearRequested: PropTypes.func.isRequired,
onSuggestionsFetchRequested: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onKeyUp: PropTypes.func,
onKeyDown: PropTypes.func,
onPaste: PropTypes.func.isRequired,
autoFocus: PropTypes.bool,
};
static defaultProps = {
autoFocus: true,
};
state = {
suggestionsHidden: false,
selectedSuggestion: 0,
lastToken: null,
tokenStart: 0,
};
onChange = (e) => {
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
if (token !== null && this.state.lastToken !== token) {
this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
this.props.onSuggestionsFetchRequested(token);
} else if (token === null) {
this.setState({ lastToken: null });
this.props.onSuggestionsClearRequested();
}
this.props.onChange(e);
}
onKeyDown = (e) => {
const { suggestions, disabled } = this.props;
const { selectedSuggestion, suggestionsHidden } = this.state;
if (disabled) {
e.preventDefault();
return;
}
switch(e.key) {
case 'Escape':
if (!suggestionsHidden) {
e.preventDefault();
this.setState({ suggestionsHidden: true });
}
break;
case 'ArrowDown':
if (suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
}
break;
case 'ArrowUp':
if (suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
}
break;
case 'Enter':
case 'Tab':
// Select suggestion
if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
e.stopPropagation();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
}
break;
}
if (e.defaultPrevented || !this.props.onKeyDown) {
return;
}
this.props.onKeyDown(e);
}
onKeyUp = e => {
if (e.key === 'Escape' && this.state.suggestionsHidden) {
document.querySelector('.ui').parentElement.focus();
}
if (this.props.onKeyUp) {
this.props.onKeyUp(e);
}
}
onBlur = () => {
this.setState({ suggestionsHidden: true });
}
onSuggestionClick = (e) => {
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
e.preventDefault();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
this.textarea.focus();
}
componentWillReceiveProps (nextProps) {
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) {
this.setState({ suggestionsHidden: false });
}
}
setTextarea = (c) => {
this.textarea = c;
}
onPaste = (e) => {
if (e.clipboardData && e.clipboardData.files.length === 1) {
this.props.onPaste(e.clipboardData.files);
e.preventDefault();
}
}
renderSuggestion = (suggestion, i) => {
const { selectedSuggestion } = this.state;
let inner, key;
if (typeof suggestion === 'object') {
inner = <AutosuggestEmoji emoji={suggestion} />;
key = suggestion.id;
} else {
inner = <AutosuggestAccountContainer id={suggestion} />;
key = suggestion;
}
return (
<div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
{inner}
</div>
);
}
render () {
const { value, suggestions, disabled, placeholder, autoFocus } = this.props;
const { suggestionsHidden } = this.state;
const style = { direction: 'ltr' };
if (isRtl(value)) {
style.direction = 'rtl';
}
return (
<div className='autosuggest-textarea'>
<label>
<span style={{ display: 'none' }}>{placeholder}</span>
<Textarea
inputRef={this.setTextarea}
className='autosuggest-textarea__textarea'
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp}
onBlur={this.onBlur}
onPaste={this.onPaste}
style={style}
aria-autocomplete='list'
/>
</label>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(this.renderSuggestion)}
</div>
</div>
);
}
}

View File

@@ -1,19 +1,22 @@
import classNames from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { autoPlayGif } from 'flavours/glitch/util/initial_state';
export default class Avatar extends React.PureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
className: PropTypes.string,
size: PropTypes.number.isRequired,
style: PropTypes.object,
animate: PropTypes.bool,
inline: PropTypes.bool,
animate: PropTypes.bool,
};
static defaultProps = {
animate: false,
animate: autoPlayGif,
size: 20,
inline: false,
};
@@ -33,17 +36,19 @@ export default class Avatar extends React.PureComponent {
}
render () {
const { account, size, animate, inline } = this.props;
const {
account,
animate,
className,
inline,
size,
} = this.props;
const { hovering } = this.state;
const src = account.get('avatar');
const staticSrc = account.get('avatar_static');
let className = 'account__avatar';
if (inline) {
className = className + ' account__avatar-inline';
}
const computedClass = classNames('account__avatar', { 'account__avatar-inline': inline }, className);
const style = {
...this.props.style,
@@ -60,7 +65,7 @@ export default class Avatar extends React.PureComponent {
return (
<div
className={className}
className={computedClass}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
style={style}

View File

@@ -1,22 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { autoPlayGif } from 'flavours/glitch/util/initial_state';
export default class AvatarOverlay extends React.PureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
friend: ImmutablePropTypes.map.isRequired,
animate: PropTypes.bool,
};
static defaultProps = {
animate: autoPlayGif,
};
render() {
const { account, friend } = this.props;
const { account, friend, animate } = this.props;
const baseStyle = {
backgroundImage: `url(${account.get('avatar_static')})`,
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
};
const overlayStyle = {
backgroundImage: `url(${friend.get('avatar_static')})`,
backgroundImage: `url(${friend.get(animate ? 'avatar' : 'avatar_static')})`,
};
return (

View File

@@ -6,9 +6,9 @@ import PropTypes from 'prop-types';
const Collapsable = ({ fullHeight, isVisible, children }) => (
<Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}>
{({ opacity, height }) =>
<div style={{ height: `${height}px`, overflow: 'hidden', opacity: opacity / 100, display: Math.floor(opacity) === 0 ? 'none' : 'block' }}>
(<div style={{ height: `${height}px`, overflow: 'hidden', opacity: opacity / 100, display: Math.floor(opacity) === 0 ? 'none' : 'block' }}>
{children}
</div>
</div>)
}
</Motion>
);

View File

@@ -1,20 +1,30 @@
// Package imports.
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
export default class DisplayName extends React.PureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
};
render () {
const displayNameHtml = { __html: this.props.account.get('display_name_html') };
return (
<span className='display-name'>
<strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /> <span className='display-name__account'>@{this.props.account.get('acct')}</span>
</span>
);
}
// The component.
export default function DisplayName ({
account,
className,
inline,
}) {
const computedClass = classNames('display-name', { inline }, className);
// The result.
return account ? (
<span className={computedClass}>
<strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} />
{inline ? ' ' : null}
<span className='display-name__account'>@{account.get('acct')}</span>
</span>
) : null;
}
// Props.
DisplayName.propTypes = {
account: ImmutablePropTypes.map,
className: PropTypes.string,
inline: PropTypes.bool,
};

View File

@@ -133,8 +133,13 @@ export default class Dropdown extends React.PureComponent {
this.props.onModalOpen({
status,
actions: items,
onClick: this.handleItemClick,
actions: items.map(
(item, i) => item ? {
...item,
name: `${item.text}-${i}`,
onClick: this.handleItemClick.bind(this, i),
} : null
),
});
return;
@@ -162,8 +167,7 @@ export default class Dropdown extends React.PureComponent {
}
}
handleItemClick = e => {
const i = Number(e.currentTarget.getAttribute('data-index'));
handleItemClick = (i, e) => {
const { action, to } = this.props.items[i];
this.handleClose();

View File

@@ -0,0 +1,26 @@
// Package imports.
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
// This just renders a FontAwesome icon.
export default function Icon ({
className,
fullwidth,
icon,
}) {
const computedClass = classNames('icon', 'fa', { 'fa-fw': fullwidth }, `fa-${icon}`, className);
return icon ? (
<span
aria-hidden='true'
className={computedClass}
/>
) : null;
}
// Props.
Icon.propTypes = {
className: PropTypes.string,
fullwidth: PropTypes.bool,
icon: PropTypes.string,
};

View File

@@ -116,7 +116,7 @@ export default class IconButton extends React.PureComponent {
return (
<Motion defaultStyle={motionDefaultStyle} style={motionStyle}>
{({ rotate }) =>
<button
(<button
aria-label={title}
aria-pressed={pressed}
aria-expanded={expanded}
@@ -128,7 +128,7 @@ export default class IconButton extends React.PureComponent {
>
<i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${icon}`} aria-hidden='true' />
{this.props.label}
</button>
</button>)
}
</Motion>
);

View File

@@ -0,0 +1,97 @@
// Inspired by <CommonLink> from Mastodon GO!
// ~ 😘 kibi!
// Package imports.
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
// Utils.
import { assignHandlers } from 'flavours/glitch/util/react_helpers';
// Handlers.
const handlers = {
// We don't handle clicks that are made with modifiers, since these
// often have special browser meanings (eg, "open in new tab").
click (e) {
const { onClick } = this.props;
if (!onClick || e.button || e.ctrlKey || e.shiftKey || e.altKey || e.metaKey) {
return;
}
onClick(e);
e.preventDefault(); // Prevents following of the link
},
};
// The component.
export default class Link extends React.PureComponent {
// Constructor.
constructor (props) {
super(props);
assignHandlers(this, handlers);
}
// Rendering.
render () {
const { click } = this.handlers;
const {
children,
className,
href,
onClick,
role,
title,
...rest
} = this.props;
const computedClass = classNames('link', className, `role-${role}`);
// We assume that our `onClick` is a routing function and give it
// the qualities of a link even if no `href` is provided. However,
// if we have neither an `onClick` or an `href`, our link is
// purely presentational.
const conditionalProps = {};
if (href) {
conditionalProps.href = href;
conditionalProps.onClick = click;
} else if (onClick) {
conditionalProps.onClick = click;
conditionalProps.role = 'link';
conditionalProps.tabIndex = 0;
} else {
conditionalProps.role = 'presentation';
}
// If we were provided a `role` it overwrites any that we may have
// set above. This can be used for "links" which are actually
// buttons.
if (role) {
conditionalProps.role = role;
}
// Rendering. We set `rel='noopener'` for user privacy, and our
// `target` as `'_blank'`.
return (
<a
className={computedClass}
{...conditionalProps}
rel='noopener'
target='_blank'
title={title}
{...rest}
>{children}</a>
);
}
}
// Props.
Link.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
href: PropTypes.string, // The link destination
onClick: PropTypes.func, // A function to call instead of opening the link
role: PropTypes.string, // An ARIA role for the link
title: PropTypes.string, // A title for the link
};

View File

@@ -9,7 +9,26 @@ import classNames from 'classnames';
import { autoPlayGif } from 'flavours/glitch/util/initial_state';
const messages = defineMessages({
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
hidden: {
defaultMessage: 'Media hidden',
id: 'status.media_hidden',
},
sensitive: {
defaultMessage: 'Sensitive',
id: 'media_gallery.sensitive',
},
toggle: {
defaultMessage: 'Click to view',
id: 'status.sensitive_toggle',
},
toggle_visible: {
defaultMessage: 'Toggle visibility',
id: 'media_gallery.toggle_visible',
},
warning: {
defaultMessage: 'Sensitive content',
id: 'status.sensitive_warning',
},
});
class Item extends React.PureComponent {
@@ -206,48 +225,79 @@ export default class MediaGallery extends React.PureComponent {
this.props.onOpenMedia(this.props.media, index);
}
isStandaloneEligible() {
const { media, standalone } = this.props;
return standalone && media.size === 1 && media.getIn([0, 'meta', 'small', 'aspect']);
}
render () {
const { media, intl, sensitive, letterbox, fullwidth } = this.props;
const {
handleClick,
handleOpen,
} = this;
const {
fullwidth,
intl,
letterbox,
media,
sensitive,
standalone,
} = this.props;
const { visible } = this.state;
const size = media.take(4).size;
let children;
if (!visible) {
let warning;
if (sensitive) {
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
} else {
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
}
children = (
<button className='media-spoiler' onClick={this.handleOpen}>
<span className='media-spoiler__warning'>{warning}</span>
<span className='media-spoiler__trigger'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
</button>
);
} else {
if (this.isStandaloneEligible()) {
children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} />;
} else {
children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} />);
}
}
const computedClass = classNames('media-gallery', `size-${size}`, { 'full-width': fullwidth });
return (
<div className={`media-gallery size-${size} ${fullwidth ? 'full-width' : ''}`}>
<div className={classNames('spoiler-button', { 'spoiler-button--visible': visible })}>
<IconButton title={intl.formatMessage(messages.toggle_visible)} icon={visible ? 'eye' : 'eye-slash'} overlay onClick={this.handleOpen} />
</div>
{children}
<div className={computedClass}>
{visible ? (
<div className='sensitive-info'>
<IconButton
icon='eye'
onClick={handleOpen}
overlay
title={intl.formatMessage(messages.toggle_visible)}
/>
{sensitive ? (
<span className='sensitive-marker'>
<FormattedMessage {...messages.sensitive} />
</span>
) : null}
</div>
) : null}
{function () {
switch (true) {
case !visible:
return (
<button
className='media-spoiler'
onClick={handleOpen}
>
<span className='media-spoiler__warning'>
<FormattedMessage {...(sensitive ? messages.warning : messages.hidden)} />
</span>
<span className='media-spoiler__trigger'>
<FormattedMessage {...messages.toggle} />
</span>
</button>
);
case standalone && media.size === 1 && !!media.getIn([0, 'meta', 'small', 'aspect']):
return (
<Item
attachment={media.get(0)}
onClick={handleClick}
standalone
/>
);
default:
return media.take(4).map(
(attachment, i) => (
<Item
attachment={attachment}
index={i}
key={attachment.get('id')}
letterbox={letterbox}
onClick={handleClick}
size={size}
/>
)
);
}
}()}
</div>
);
}

View File

@@ -2,9 +2,14 @@ import React from 'react';
import { FormattedMessage } from 'react-intl';
const MissingIndicator = () => (
<div className='missing-indicator'>
<div className='regeneration-indicator missing-indicator'>
<div>
<FormattedMessage id='missing_indicator.label' defaultMessage='Not found' />
<div className='regeneration-indicator__figure' />
<div className='regeneration-indicator__label'>
<FormattedMessage id='missing_indicator.label' tagName='strong' defaultMessage='Not found' />
<FormattedMessage id='missing_indicator.sublabel' defaultMessage='This resource could not be found' />
</div>
</div>
</div>
);

View File

@@ -22,7 +22,13 @@ export default class Permalink extends React.PureComponent {
}
render () {
const { href, children, className, ...other } = this.props;
const {
children,
className,
href,
to,
...other
} = this.props;
return (
<a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>

View File

@@ -6,24 +6,24 @@ export default class SettingText extends React.PureComponent {
static propTypes = {
settings: ImmutablePropTypes.map.isRequired,
settingKey: PropTypes.array.isRequired,
settingPath: PropTypes.array.isRequired,
label: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
};
handleChange = (e) => {
this.props.onChange(this.props.settingKey, e.target.value);
this.props.onChange(this.props.settingPath, e.target.value);
}
render () {
const { settings, settingKey, label } = this.props;
const { settings, settingPath, label } = this.props;
return (
<label>
<span style={{ display: 'none' }}>{label}</span>
<input
className='setting-text'
value={settings.getIn(settingKey)}
value={settings.getIn(settingPath)}
onChange={this.handleChange}
placeholder={label}
/>

View File

@@ -47,7 +47,6 @@ export default class Status extends ImmutablePureComponent {
state = {
isExpanded: null,
markedForDelete: false,
}
// Avoid checking props that are functions (and whose equality will always
@@ -67,7 +66,6 @@ export default class Status extends ImmutablePureComponent {
updateOnStates = [
'isExpanded',
'markedForDelete',
]
// If our settings have changed to disable collapsed statuses, then we
@@ -121,15 +119,15 @@ export default class Status extends ImmutablePureComponent {
if (function () {
switch (true) {
case collapse:
case autoCollapseSettings.get('all'):
case autoCollapseSettings.get('notifications') && muted:
case !!collapse:
case !!autoCollapseSettings.get('all'):
case autoCollapseSettings.get('notifications') && !!muted:
case autoCollapseSettings.get('lengthy') && node.clientHeight > (
status.get('media_attachments').size && !muted ? 650 : 400
):
case autoCollapseSettings.get('reblogs') && prepend === 'reblogged_by':
case autoCollapseSettings.get('replies') && status.get('in_reply_to_id', null) !== null:
case autoCollapseSettings.get('media') && !(status.get('spoiler_text').length) && status.get('media_attachments').size:
case autoCollapseSettings.get('media') && !(status.get('spoiler_text').length) && !!status.get('media_attachments').size:
return true;
default:
return false;
@@ -318,14 +316,14 @@ export default class Status extends ImmutablePureComponent {
media = (
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
{Component => <Component
{Component => (<Component
preview={video.get('preview_url')}
src={video.get('url')}
sensitive={status.get('sensitive')}
letterbox={settings.getIn(['media', 'letterbox'])}
fullwidth={settings.getIn(['media', 'fullwidth'])}
onOpenVideo={this.handleOpenVideo}
/>}
/>)}
</Bundle>
);
mediaIcon = 'video-camera';
@@ -382,7 +380,6 @@ export default class Status extends ImmutablePureComponent {
const computedClass = classNames('status', `status-${status.get('visibility')}`, {
collapsed: isExpanded === false,
'has-background': isExpanded === false && background,
'marked-for-delete': this.state.markedForDelete,
muted,
}, 'focusable');

View File

@@ -104,8 +104,8 @@ export default class StatusHeader extends React.PureComponent {
active={collapsed}
title={
collapsed ?
intl.formatMessage(messages.uncollapse) :
intl.formatMessage(messages.collapse)
intl.formatMessage(messages.uncollapse) :
intl.formatMessage(messages.collapse)
}
icon='angle-double-up'
onClick={this.handleCollapsedClick}

View File

@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import StatusContainer from 'flavours/glitch/containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ScrollableList from './scrollable_list';
import { FormattedMessage } from 'react-intl';
export default class StatusList extends ImmutablePureComponent {
@@ -16,6 +17,7 @@ export default class StatusList extends ImmutablePureComponent {
trackScroll: PropTypes.bool,
shouldUpdateScroll: PropTypes.func,
isLoading: PropTypes.bool,
isPartial: PropTypes.bool,
hasMore: PropTypes.bool,
prepend: PropTypes.node,
emptyMessage: PropTypes.node,
@@ -48,8 +50,23 @@ export default class StatusList extends ImmutablePureComponent {
}
render () {
const { statusIds, ...other } = this.props;
const { isLoading } = other;
const { statusIds, ...other } = this.props;
const { isLoading, isPartial } = other;
if (isPartial) {
return (
<div className='regeneration-indicator'>
<div>
<div className='regeneration-indicator__figure' />
<div className='regeneration-indicator__label'>
<FormattedMessage id='regeneration_indicator.label' tagName='strong' defaultMessage='Loading&hellip;' />
<FormattedMessage id='regeneration_indicator.sublabel' defaultMessage='Your home feed is being prepared!' />
</div>
</div>
</div>
);
}
const scrollableContent = (isLoading || statusIds.size > 0) ? (
statusIds.map((statusId) => (

View File

@@ -63,7 +63,15 @@ export default class Header extends ImmutablePureComponent {
<div className='account__header__wrapper'>
<div className='account__header' style={{ backgroundImage: `url(${account.get('header')})` }}>
<div>
<Avatar account={account} size={90} />
<a
href={account.get('url')}
className='account__header__avatar'
role='presentation'
target='_blank'
rel='noopener'
>
<Avatar account={account} size={90} />
</a>
<span className='account__header__display-name' dangerouslySetInnerHTML={{ __html: displayName }} />
<span className='account__header__username'>@{account.get('acct')} {account.get('locked') ? <i className='fa fa-lock' /> : null}</span>

View File

@@ -95,10 +95,10 @@ export default class AccountGallery extends ImmutablePureComponent {
<div className='account-gallery__container'>
{medias.map(media =>
<MediaItem
(<MediaItem
key={media.get('id')}
media={media}
/>
/>)
)}
{loadMore}
</div>

View File

@@ -26,7 +26,7 @@ export default class ColumnSettings extends React.PureComponent {
<span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
<div className='column-settings__row'>
<SettingText settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
<SettingText settings={settings} settingPath={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
</div>
</div>
);

View File

@@ -8,8 +8,8 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({
onChange (key, checked) {
dispatch(changeSetting(['community', ...key], checked));
onChange (path, checked) {
dispatch(changeSetting(['community', ...path], checked));
},
});

View File

@@ -1,62 +0,0 @@
// Package imports.
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { injectIntl, defineMessages } from 'react-intl';
// Our imports.
import ComposeAdvancedOptionsToggle from './advanced_options_toggle';
import ComposeDropdown from './dropdown';
const messages = defineMessages({
local_only_short :
{ id: 'advanced-options.local-only.short', defaultMessage: 'Local-only' },
local_only_long :
{ id: 'advanced-options.local-only.long', defaultMessage: 'Do not post to other instances' },
advanced_options_icon_title :
{ id: 'advanced_options.icon_title', defaultMessage: 'Advanced options' },
});
@injectIntl
export default class ComposeAdvancedOptions extends React.PureComponent {
static propTypes = {
values : ImmutablePropTypes.contains({
do_not_federate : PropTypes.bool.isRequired,
}).isRequired,
onChange : PropTypes.func.isRequired,
intl : PropTypes.object.isRequired,
};
render () {
const { intl, values } = this.props;
const options = [
{ icon: 'wifi', shortText: messages.local_only_short, longText: messages.local_only_long, name: 'do_not_federate' },
];
const anyEnabled = values.some((enabled) => enabled);
const optionElems = options.map((option) => {
return (
<ComposeAdvancedOptionsToggle
onChange={this.props.onChange}
active={values.get(option.name)}
key={option.name}
name={option.name}
shortText={intl.formatMessage(option.shortText)}
longText={intl.formatMessage(option.longText)}
/>
);
});
return (
<ComposeDropdown
title={intl.formatMessage(messages.advanced_options_icon_title)}
icon='home'
highlight={anyEnabled}
>
{optionElems}
</ComposeDropdown>
);
}
}

View File

@@ -1,35 +0,0 @@
// Package imports.
import React from 'react';
import PropTypes from 'prop-types';
import Toggle from 'react-toggle';
export default class ComposeAdvancedOptionsToggle extends React.PureComponent {
static propTypes = {
onChange: PropTypes.func.isRequired,
active: PropTypes.bool.isRequired,
name: PropTypes.string.isRequired,
shortText: PropTypes.string.isRequired,
longText: PropTypes.string.isRequired,
}
onToggle = () => {
this.props.onChange(this.props.name);
}
render() {
const { active, shortText, longText } = this.props;
return (
<div role='button' tabIndex='0' className='advanced-options-dropdown__option' onClick={this.onToggle}>
<div className='advanced-options-dropdown__option__toggle'>
<Toggle checked={active} onChange={this.onToggle} />
</div>
<div className='advanced-options-dropdown__option__content'>
<strong>{shortText}</strong>
{longText}
</div>
</div>
);
}
}

View File

@@ -1,131 +0,0 @@
// Package imports //
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, defineMessages } from 'react-intl';
// Our imports //
import ComposeDropdown from './dropdown';
import { uploadCompose } from 'flavours/glitch/actions/compose';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { openModal } from 'flavours/glitch/actions/modal';
const messages = defineMessages({
upload :
{ id: 'compose.attach.upload', defaultMessage: 'Upload a file' },
doodle :
{ id: 'compose.attach.doodle', defaultMessage: 'Draw something' },
attach :
{ id: 'compose.attach', defaultMessage: 'Attach...' },
});
const mapStateToProps = state => ({
// This horrible expression is copied from vanilla upload_button_container
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')),
resetFileKey: state.getIn(['compose', 'resetFileKey']),
acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']),
});
const mapDispatchToProps = dispatch => ({
onSelectFile (files) {
dispatch(uploadCompose(files));
},
onOpenDoodle () {
dispatch(openModal('DOODLE', { noEsc: true }));
},
});
@injectIntl
@connect(mapStateToProps, mapDispatchToProps)
export default class ComposeAttachOptions extends ImmutablePureComponent {
static propTypes = {
intl : PropTypes.object.isRequired,
resetFileKey: PropTypes.number,
acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
disabled: PropTypes.bool,
onSelectFile: PropTypes.func.isRequired,
onOpenDoodle: PropTypes.func.isRequired,
};
handleItemClick = bt => {
if (bt === 'upload') {
this.fileElement.click();
}
if (bt === 'doodle') {
this.props.onOpenDoodle();
}
this.dropdown.setState({ open: false });
};
handleFileChange = (e) => {
if (e.target.files.length > 0) {
this.props.onSelectFile(e.target.files);
}
}
setFileRef = (c) => {
this.fileElement = c;
}
setDropdownRef = (c) => {
this.dropdown = c;
}
render () {
const { intl, resetFileKey, disabled, acceptContentTypes } = this.props;
const options = [
{ icon: 'cloud-upload', text: messages.upload, name: 'upload' },
{ icon: 'paint-brush', text: messages.doodle, name: 'doodle' },
];
const optionElems = options.map((item) => {
const hdl = () => this.handleItemClick(item.name);
return (
<div
role='button'
tabIndex='0'
key={item.name}
onClick={hdl}
className='privacy-dropdown__option'
>
<div className='privacy-dropdown__option__icon'>
<i className={`fa fa-fw fa-${item.icon}`} />
</div>
<div className='privacy-dropdown__option__content'>
<strong>{intl.formatMessage(item.text)}</strong>
</div>
</div>
);
});
return (
<div>
<ComposeDropdown
title={intl.formatMessage(messages.attach)}
icon='paperclip'
disabled={disabled}
ref={this.setDropdownRef}
>
{optionElems}
</ComposeDropdown>
<input
key={resetFileKey}
ref={this.setFileRef}
type='file'
multiple={false}
accept={acceptContentTypes.toArray().join(',')}
onChange={this.handleFileChange}
disabled={disabled}
style={{ display: 'none' }}
/>
</div>
);
}
}

View File

@@ -1,24 +0,0 @@
import React from 'react';
import Avatar from 'flavours/glitch/components/avatar';
import DisplayName from 'flavours/glitch/components/display_name';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
export default class AutosuggestAccount extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
};
render () {
const { account } = this.props;
return (
<div className='autosuggest-account'>
<div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
<DisplayName account={account} />
</div>
);
}
}

View File

@@ -1,25 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { length } from 'stringz';
export default class CharacterCounter extends React.PureComponent {
static propTypes = {
text: PropTypes.string.isRequired,
max: PropTypes.number.isRequired,
};
checkRemainingText (diff) {
if (diff < 0) {
return <span className='character-counter character-counter--over'>{diff}</span>;
}
return <span className='character-counter'>{diff}</span>;
}
render () {
const diff = this.props.max - length(this.props.text);
return this.checkRemainingText(diff);
}
}

View File

@@ -1,286 +0,0 @@
import React from 'react';
import CharacterCounter from './character_counter';
import Button from 'flavours/glitch/components/button';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import ReplyIndicatorContainer from '../containers/reply_indicator_container';
import AutosuggestTextarea from 'flavours/glitch/components/autosuggest_textarea';
import { defineMessages, injectIntl } from 'react-intl';
import Collapsable from 'flavours/glitch/components/collapsable';
import SpoilerButtonContainer from '../containers/spoiler_button_container';
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
import ComposeAdvancedOptionsContainer from '../containers/advanced_options_container';
import SensitiveButtonContainer from '../containers/sensitive_button_container';
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
import UploadFormContainer from '../containers/upload_form_container';
import WarningContainer from '../containers/warning_container';
import { isMobile } from 'flavours/glitch/util/is_mobile';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { length } from 'stringz';
import { countableText } from 'flavours/glitch/util/counter';
import ComposeAttachOptions from './attach_options';
import initialState from 'flavours/glitch/util/initial_state';
const maxChars = initialState.max_toot_chars;
const messages = defineMessages({
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
});
@injectIntl
export default class ComposeForm extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
text: PropTypes.string.isRequired,
suggestion_token: PropTypes.string,
suggestions: ImmutablePropTypes.list,
spoiler: PropTypes.bool,
privacy: PropTypes.string,
advanced_options: ImmutablePropTypes.contains({
do_not_federate: PropTypes.bool,
}),
spoiler_text: PropTypes.string,
focusDate: PropTypes.instanceOf(Date),
preselectDate: PropTypes.instanceOf(Date),
is_submitting: PropTypes.bool,
is_uploading: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
onClearSuggestions: PropTypes.func.isRequired,
onFetchSuggestions: PropTypes.func.isRequired,
onPrivacyChange: PropTypes.func.isRequired,
onSuggestionSelected: PropTypes.func.isRequired,
onChangeSpoilerText: PropTypes.func.isRequired,
onPaste: PropTypes.func.isRequired,
onPickEmoji: PropTypes.func.isRequired,
showSearch: PropTypes.bool,
settings : ImmutablePropTypes.map.isRequired,
};
static defaultProps = {
showSearch: false,
};
handleChange = (e) => {
this.props.onChange(e.target.value);
}
handleKeyDown = (e) => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
this.handleSubmit();
}
}
handleSubmit2 = () => {
this.props.onPrivacyChange(this.props.settings.get('side_arm'));
this.handleSubmit();
}
handleSubmit = () => {
if (this.props.text !== this.autosuggestTextarea.textarea.value) {
// Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
// Update the state to match the current text
this.props.onChange(this.autosuggestTextarea.textarea.value);
}
this.props.onSubmit();
}
onSuggestionsClearRequested = () => {
this.props.onClearSuggestions();
}
onSuggestionsFetchRequested = (token) => {
this.props.onFetchSuggestions(token);
}
onSuggestionSelected = (tokenStart, token, value) => {
this._restoreCaret = null;
this.props.onSuggestionSelected(tokenStart, token, value);
}
handleChangeSpoilerText = (e) => {
this.props.onChangeSpoilerText(e.target.value);
}
componentWillReceiveProps (nextProps) {
// If this is the update where we've finished uploading,
// save the last caret position so we can restore it below!
if (!nextProps.is_uploading && this.props.is_uploading) {
this._restoreCaret = this.autosuggestTextarea.textarea.selectionStart;
}
}
componentDidUpdate (prevProps) {
// This statement does several things:
// - If we're beginning a reply, and,
// - Replying to zero or one users, places the cursor at the end of the textbox.
// - Replying to more than one user, selects any usernames past the first;
// this provides a convenient shortcut to drop everyone else from the conversation.
// - If we've just finished uploading an image, and have a saved caret position,
// restores the cursor to that position after the text changes!
if (this.props.focusDate !== prevProps.focusDate || (prevProps.is_uploading && !this.props.is_uploading && typeof this._restoreCaret === 'number')) {
let selectionEnd, selectionStart;
if (this.props.preselectDate !== prevProps.preselectDate) {
selectionEnd = this.props.text.length;
selectionStart = this.props.text.search(/\s/) + 1;
} else if (typeof this._restoreCaret === 'number') {
selectionStart = this._restoreCaret;
selectionEnd = this._restoreCaret;
} else {
selectionEnd = this.props.text.length;
selectionStart = selectionEnd;
}
this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd);
this.autosuggestTextarea.textarea.focus();
} else if(prevProps.is_submitting && !this.props.is_submitting) {
this.autosuggestTextarea.textarea.focus();
}
}
setAutosuggestTextarea = (c) => {
this.autosuggestTextarea = c;
}
handleEmojiPick = (data) => {
const position = this.autosuggestTextarea.textarea.selectionStart;
const emojiChar = data.native;
this._restoreCaret = position + emojiChar.length + 1;
this.props.onPickEmoji(position, data);
}
render () {
const { intl, onPaste, showSearch } = this.props;
const disabled = this.props.is_submitting;
const maybeEye = (this.props.advanced_options && this.props.advanced_options.do_not_federate) ? ' 👁️' : '';
const text = [this.props.spoiler_text, countableText(this.props.text), maybeEye].join('');
const secondaryVisibility = this.props.settings.get('side_arm');
let showSideArm = secondaryVisibility !== 'none';
let publishText = '';
let publishText2 = '';
let title = '';
let title2 = '';
const privacyIcons = {
none: '',
public: 'globe',
unlisted: 'unlock-alt',
private: 'lock',
direct: 'envelope',
};
title = `${intl.formatMessage(messages.publish)}: ${intl.formatMessage({ id: `privacy.${this.props.privacy}.short` })}`;
if (showSideArm) {
// Enhanced behavior with dual toot buttons
publishText = (
<span>
{
<i
className={`fa fa-${privacyIcons[this.props.privacy]}`}
style={{ paddingRight: '5px' }}
/>
}{intl.formatMessage(messages.publish)}
</span>
);
title2 = `${intl.formatMessage(messages.publish)}: ${intl.formatMessage({ id: `privacy.${secondaryVisibility}.short` })}`;
publishText2 = (
<i
className={`fa fa-${privacyIcons[secondaryVisibility]}`}
aria-label={title2}
/>
);
} else {
// Original vanilla behavior - no icon if public or unlisted
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
publishText = <span className='compose-form__publish-private'><i className='fa fa-lock' /> {intl.formatMessage(messages.publish)}</span>;
} else {
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
}
}
const submitDisabled = disabled || this.props.is_uploading || length(text) > maxChars || (text.length !== 0 && text.trim().length === 0);
return (
<div className='compose-form'>
<Collapsable isVisible={this.props.spoiler} fullHeight={50}>
<div className='spoiler-input'>
<label>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.spoiler_placeholder)}</span>
<input placeholder={intl.formatMessage(messages.spoiler_placeholder)} value={this.props.spoiler_text} onChange={this.handleChangeSpoilerText} onKeyDown={this.handleKeyDown} type='text' className='spoiler-input__input' id='cw-spoiler-input' />
</label>
</div>
</Collapsable>
<WarningContainer />
<ReplyIndicatorContainer />
<div className='compose-form__autosuggest-wrapper'>
<AutosuggestTextarea
ref={this.setAutosuggestTextarea}
placeholder={intl.formatMessage(messages.placeholder)}
disabled={disabled}
value={this.props.text}
onChange={this.handleChange}
suggestions={this.props.suggestions}
onKeyDown={this.handleKeyDown}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste}
autoFocus={!showSearch && !isMobile(window.innerWidth)}
/>
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
</div>
<div className='compose-form__modifiers'>
<UploadFormContainer />
</div>
<div className='compose-form__buttons'>
<ComposeAttachOptions />
<SensitiveButtonContainer />
<div className='compose-form__buttons-separator' />
<PrivacyDropdownContainer />
<SpoilerButtonContainer />
<ComposeAdvancedOptionsContainer />
</div>
<div className='compose-form__publish'>
<div className='character-counter__wrapper'><CharacterCounter max={maxChars} text={text} /></div>
<div className='compose-form__publish-button-wrapper'>
{
showSideArm ?
<Button
className='compose-form__publish__side-arm'
text={publishText2}
title={title2}
onClick={this.handleSubmit2}
disabled={submitDisabled}
/> : ''
}
<Button
className='compose-form__publish__primary'
text={publishText}
title={title}
onClick={this.handleSubmit}
disabled={submitDisabled}
/>
</div>
</div>
</div>
);
}
}

View File

@@ -1,77 +0,0 @@
// Package imports.
import React from 'react';
import PropTypes from 'prop-types';
// Our imports.
import IconButton from 'flavours/glitch/components/icon_button';
const iconStyle = {
height : null,
lineHeight : '27px',
};
export default class ComposeDropdown extends React.PureComponent {
static propTypes = {
title: PropTypes.string.isRequired,
icon: PropTypes.string,
highlight: PropTypes.bool,
disabled: PropTypes.bool,
children: PropTypes.arrayOf(PropTypes.node).isRequired,
};
state = {
open: false,
};
onGlobalClick = (e) => {
if (e.target !== this.node && !this.node.contains(e.target) && this.state.open) {
this.setState({ open: false });
}
};
componentDidMount () {
window.addEventListener('click', this.onGlobalClick);
window.addEventListener('touchstart', this.onGlobalClick);
}
componentWillUnmount () {
window.removeEventListener('click', this.onGlobalClick);
window.removeEventListener('touchstart', this.onGlobalClick);
}
onToggleDropdown = () => {
if (this.props.disabled) return;
this.setState({ open: !this.state.open });
};
setRef = (c) => {
this.node = c;
};
render () {
const { open } = this.state;
let { highlight, title, icon, disabled } = this.props;
if (!icon) icon = 'ellipsis-h';
return (
<div ref={this.setRef} className={`advanced-options-dropdown ${open ? 'open' : ''} ${highlight ? 'active' : ''} `}>
<div className='advanced-options-dropdown__value'>
<IconButton
className={'inverted'}
title={title}
icon={icon} active={open || highlight}
size={18}
style={iconStyle}
disabled={disabled}
onClick={this.onToggleDropdown}
/>
</div>
<div className='advanced-options-dropdown__dropdown'>
{this.props.children}
</div>
</div>
);
}
}

View File

@@ -1,38 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Avatar from 'flavours/glitch/components/avatar';
import IconButton from 'flavours/glitch/components/icon_button';
import Permalink from 'flavours/glitch/components/permalink';
import { FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
export default class NavigationBar extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
onClose: PropTypes.func.isRequired,
};
render () {
return (
<div className='navigation-bar'>
<Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
<Avatar account={this.props.account} size={40} />
</Permalink>
<div className='navigation-bar__profile'>
<Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
<strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong>
</Permalink>
<a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
</div>
<IconButton title='' icon='close' onClick={this.props.onClose} />
</div>
);
}
}

View File

@@ -1,200 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, defineMessages } from 'react-intl';
import IconButton from 'flavours/glitch/components/icon_button';
import Overlay from 'react-overlays/lib/Overlay';
import Motion from 'flavours/glitch/util/optional_motion';
import spring from 'react-motion/lib/spring';
import detectPassiveEvents from 'detect-passive-events';
import classNames from 'classnames';
const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
public_long: { id: 'privacy.public.long', defaultMessage: 'Post to public timelines' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Do not show in public timelines' },
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
private_long: { id: 'privacy.private.long', defaultMessage: 'Post to followers only' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
direct_long: { id: 'privacy.direct.long', defaultMessage: 'Post to mentioned users only' },
change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
});
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
class PrivacyDropdownMenu extends React.PureComponent {
static propTypes = {
style: PropTypes.object,
items: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
};
handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) {
this.props.onClose();
}
}
handleClick = e => {
if (e.key === 'Escape') {
this.props.onClose();
} else if (!e.key || e.key === 'Enter') {
const value = e.currentTarget.getAttribute('data-index');
e.preventDefault();
this.props.onClose();
this.props.onChange(value);
}
}
componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, false);
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
}
componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, false);
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
}
setRef = c => {
this.node = c;
}
render () {
const { style, items, value } = this.props;
return (
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
{({ opacity, scaleX, scaleY }) => (
<div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}>
{items.map(item =>
<div role='button' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleClick} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })}>
<div className='privacy-dropdown__option__icon'>
<i className={`fa fa-fw fa-${item.icon}`} />
</div>
<div className='privacy-dropdown__option__content'>
<strong>{item.text}</strong>
{item.meta}
</div>
</div>
)}
</div>
)}
</Motion>
);
}
}
@injectIntl
export default class PrivacyDropdown extends React.PureComponent {
static propTypes = {
isUserTouching: PropTypes.func,
isModalOpen: PropTypes.bool.isRequired,
onModalOpen: PropTypes.func,
onModalClose: PropTypes.func,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
state = {
open: false,
};
handleToggle = () => {
if (this.props.isUserTouching()) {
if (this.state.open) {
this.props.onModalClose();
} else {
this.props.onModalOpen({
actions: this.options.map(option => ({ ...option, active: option.value === this.props.value })),
onClick: this.handleModalActionClick,
});
}
} else {
this.setState({ open: !this.state.open });
}
}
handleModalActionClick = (e) => {
e.preventDefault();
const { value } = this.options[e.currentTarget.getAttribute('data-index')];
this.props.onModalClose();
this.props.onChange(value);
}
handleKeyDown = e => {
switch(e.key) {
case 'Enter':
this.handleToggle();
break;
case 'Escape':
this.handleClose();
break;
}
}
handleClose = () => {
this.setState({ open: false });
}
handleChange = value => {
this.props.onChange(value);
}
componentWillMount () {
const { intl: { formatMessage } } = this.props;
this.options = [
{ icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
{ icon: 'unlock-alt', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
{ icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
{ icon: 'envelope', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
];
}
render () {
const { value, intl } = this.props;
const { open } = this.state;
const valueOption = this.options.find(item => item.value === value);
return (
<div className={classNames('privacy-dropdown', { active: open })} onKeyDown={this.handleKeyDown}>
<div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === 0 })}>
<IconButton
className='privacy-dropdown__value-icon'
icon={valueOption.icon}
title={intl.formatMessage(messages.change_privacy)}
size={18}
expanded={open}
active={open}
inverted
onClick={this.handleToggle}
style={{ height: null, lineHeight: '27px' }}
/>
</div>
<Overlay show={open} placement='bottom' target={this}>
<PrivacyDropdownMenu
items={this.options}
value={value}
onClose={this.handleClose}
onChange={this.handleChange}
/>
</Overlay>
</div>
);
}
}

View File

@@ -1,63 +0,0 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import Avatar from 'flavours/glitch/components/avatar';
import IconButton from 'flavours/glitch/components/icon_button';
import DisplayName from 'flavours/glitch/components/display_name';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
const messages = defineMessages({
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
});
@injectIntl
export default class ReplyIndicator extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = {
status: ImmutablePropTypes.map,
onCancel: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onCancel();
}
handleAccountClick = (e) => {
if (e.button === 0) {
e.preventDefault();
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
}
}
render () {
const { status, intl } = this.props;
if (!status) {
return null;
}
const content = { __html: status.get('contentHtml') };
return (
<div className='reply-indicator'>
<div className='reply-indicator__header'>
<div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} /></div>
<a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name'>
<div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
<DisplayName account={status.get('account')} />
</a>
</div>
<div className='reply-indicator__content' dangerouslySetInnerHTML={content} />
</div>
);
}
}

View File

@@ -1,129 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Overlay from 'react-overlays/lib/Overlay';
import Motion from 'flavours/glitch/util/optional_motion';
import spring from 'react-motion/lib/spring';
const messages = defineMessages({
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
});
class SearchPopout extends React.PureComponent {
static propTypes = {
style: PropTypes.object,
};
render () {
const { style } = this.props;
return (
<div style={{ ...style, position: 'absolute', width: 285 }}>
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
{({ opacity, scaleX, scaleY }) => (
<div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
<h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
<ul>
<li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
<li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
<li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
<li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
</ul>
<FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />
</div>
)}
</Motion>
</div>
);
}
}
@injectIntl
export default class Search extends React.PureComponent {
static propTypes = {
value: PropTypes.string.isRequired,
submitted: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
onClear: PropTypes.func.isRequired,
onShow: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
state = {
expanded: false,
};
handleChange = (e) => {
this.props.onChange(e.target.value);
}
handleClear = (e) => {
e.preventDefault();
if (this.props.value.length > 0 || this.props.submitted) {
this.props.onClear();
}
}
handleKeyDown = (e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.props.onSubmit();
} else if (e.key === 'Escape') {
document.querySelector('.ui').parentElement.focus();
}
}
noop () {
}
handleFocus = () => {
this.setState({ expanded: true });
this.props.onShow();
}
handleBlur = () => {
this.setState({ expanded: false });
}
render () {
const { intl, value, submitted } = this.props;
const { expanded } = this.state;
const hasValue = value.length > 0 || submitted;
return (
<div className='search'>
<label>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span>
<input
className='search__input'
type='text'
placeholder={intl.formatMessage(messages.placeholder)}
value={value}
onChange={this.handleChange}
onKeyUp={this.handleKeyDown}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
</label>
<div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
<i className={`fa fa-search ${hasValue ? '' : 'active'}`} />
<i aria-label={intl.formatMessage(messages.placeholder)} className={`fa fa-times-circle ${hasValue ? 'active' : ''}`} />
</div>
<Overlay show={expanded && !hasValue} placement='bottom' target={this}>
<SearchPopout />
</Overlay>
</div>
);
}
}

View File

@@ -1,65 +0,0 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { FormattedMessage } from 'react-intl';
import AccountContainer from 'flavours/glitch/containers/account_container';
import StatusContainer from 'flavours/glitch/containers/status_container';
import { Link } from 'react-router-dom';
import ImmutablePureComponent from 'react-immutable-pure-component';
export default class SearchResults extends ImmutablePureComponent {
static propTypes = {
results: ImmutablePropTypes.map.isRequired,
};
render () {
const { results } = this.props;
let accounts, statuses, hashtags;
let count = 0;
if (results.get('accounts') && results.get('accounts').size > 0) {
count += results.get('accounts').size;
accounts = (
<div className='search-results__section'>
{results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
</div>
);
}
if (results.get('statuses') && results.get('statuses').size > 0) {
count += results.get('statuses').size;
statuses = (
<div className='search-results__section'>
{results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}
</div>
);
}
if (results.get('hashtags') && results.get('hashtags').size > 0) {
count += results.get('hashtags').size;
hashtags = (
<div className='search-results__section'>
{results.get('hashtags').map(hashtag =>
<Link key={hashtag} className='search-results__hashtag' to={`/timelines/tag/${hashtag}`}>
#{hashtag}
</Link>
)}
</div>
);
}
return (
<div className='search-results'>
<div className='search-results__header'>
<FormattedMessage id='search_results.total' defaultMessage='{count, number} {count, plural, one {result} other {results}}' values={{ count }} />
</div>
{accounts}
{statuses}
{hashtags}
</div>
);
}
}

View File

@@ -1,96 +0,0 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import IconButton from 'flavours/glitch/components/icon_button';
import Motion from 'flavours/glitch/util/optional_motion';
import spring from 'react-motion/lib/spring';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl } from 'react-intl';
import classNames from 'classnames';
const messages = defineMessages({
undo: { id: 'upload_form.undo', defaultMessage: 'Undo' },
description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' },
});
@injectIntl
export default class Upload extends ImmutablePureComponent {
static propTypes = {
media: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
onUndo: PropTypes.func.isRequired,
onDescriptionChange: PropTypes.func.isRequired,
};
state = {
hovered: false,
focused: false,
dirtyDescription: null,
};
handleUndoClick = () => {
this.props.onUndo(this.props.media.get('id'));
}
handleInputChange = e => {
this.setState({ dirtyDescription: e.target.value });
}
handleMouseEnter = () => {
this.setState({ hovered: true });
}
handleMouseLeave = () => {
this.setState({ hovered: false });
}
handleInputFocus = () => {
this.setState({ focused: true });
}
handleInputBlur = () => {
const { dirtyDescription } = this.state;
this.setState({ focused: false, dirtyDescription: null });
if (dirtyDescription !== null) {
this.props.onDescriptionChange(this.props.media.get('id'), dirtyDescription);
}
}
render () {
const { intl, media } = this.props;
const active = this.state.hovered || this.state.focused;
const description = this.state.dirtyDescription || media.get('description') || '';
return (
<div className='compose-form__upload' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
{({ scale }) => (
<div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})` }}>
<IconButton icon='times' title={intl.formatMessage(messages.undo)} size={36} onClick={this.handleUndoClick} />
<div className={classNames('compose-form__upload-description', { active })}>
<label>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.description)}</span>
<input
placeholder={intl.formatMessage(messages.description)}
type='text'
value={description}
maxLength={420}
onFocus={this.handleInputFocus}
onChange={this.handleInputChange}
onBlur={this.handleInputBlur}
/>
</label>
</div>
</div>
)}
</Motion>
</div>
);
}
}

View File

@@ -1,77 +0,0 @@
import React from 'react';
import IconButton from 'flavours/glitch/components/icon_button';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
const messages = defineMessages({
upload: { id: 'upload_button.label', defaultMessage: 'Add media' },
});
const makeMapStateToProps = () => {
const mapStateToProps = state => ({
acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']),
});
return mapStateToProps;
};
const iconStyle = {
height: null,
lineHeight: '27px',
};
@connect(makeMapStateToProps)
@injectIntl
export default class UploadButton extends ImmutablePureComponent {
static propTypes = {
disabled: PropTypes.bool,
onSelectFile: PropTypes.func.isRequired,
style: PropTypes.object,
resetFileKey: PropTypes.number,
acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
intl: PropTypes.object.isRequired,
};
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, acceptContentTypes } = 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} />
<label>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.upload)}</span>
<input
key={resetFileKey}
ref={this.setRef}
type='file'
multiple={false}
accept={acceptContentTypes.toArray().join(',')}
onChange={this.handleChange}
disabled={disabled}
style={{ display: 'none' }}
/>
</label>
</div>
);
}
}

Some files were not shown because too many files have changed in this diff Show More