Compare commits

...

69 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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
りんすき
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
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
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
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
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
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
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
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
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
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
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
Eugen Rochko
dbda87c31f Revert #5772 (#6221) 2018-01-08 10:57:52 +01: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
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
Jeong Arm
9e97fbf0af Translate Korean (#6212) 2018-01-07 11:13:42 +09: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
Naoki Kosaka
8d51ce4290 Fix enforce HTTPS in production. (#6180) 2018-01-05 20:04:22 +01: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
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
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
416 changed files with 4996 additions and 2408 deletions

View File

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

View File

@@ -17,11 +17,9 @@ plugins:
parserOptions: parserOptions:
sourceType: module sourceType: module
ecmaFeatures: ecmaFeatures:
arrowFunctions: true experimentalObjectRestSpread: true
jsx: true jsx: true
destructuring: true ecmaVersion: 2018
modules: true
spread: true
settings: settings:
import/extensions: import/extensions:
@@ -109,6 +107,7 @@ rules:
react/self-closing-comp: error react/self-closing-comp: error
jsx-a11y/accessible-emoji: warn jsx-a11y/accessible-emoji: warn
jsx-a11y/alt-text: warn
jsx-a11y/anchor-has-content: warn jsx-a11y/anchor-has-content: warn
jsx-a11y/aria-activedescendant-has-tabindex: warn jsx-a11y/aria-activedescendant-has-tabindex: warn
jsx-a11y/aria-props: warn jsx-a11y/aria-props: warn
@@ -119,16 +118,22 @@ rules:
jsx-a11y/href-no-hash: warn jsx-a11y/href-no-hash: warn
jsx-a11y/html-has-lang: warn jsx-a11y/html-has-lang: warn
jsx-a11y/iframe-has-title: warn jsx-a11y/iframe-has-title: warn
jsx-a11y/img-has-alt: warn
jsx-a11y/img-redundant-alt: warn jsx-a11y/img-redundant-alt: warn
jsx-a11y/interactive-supports-focus: warn
jsx-a11y/label-has-for: off jsx-a11y/label-has-for: off
jsx-a11y/mouse-events-have-key-events: warn jsx-a11y/mouse-events-have-key-events: warn
jsx-a11y/no-access-key: warn jsx-a11y/no-access-key: warn
jsx-a11y/no-distracting-elements: warn jsx-a11y/no-distracting-elements: warn
jsx-a11y/no-noninteractive-element-interactions:
- warn
- handlers:
- onClick
jsx-a11y/no-onchange: warn jsx-a11y/no-onchange: warn
jsx-a11y/no-redundant-roles: warn jsx-a11y/no-redundant-roles: warn
jsx-a11y/onclick-has-focus: warn jsx-a11y/no-static-element-interactions:
jsx-a11y/onclick-has-role: warn - warn
- handlers:
- onClick
jsx-a11y/role-has-required-aria-props: warn jsx-a11y/role-has-required-aria-props: warn
jsx-a11y/role-supports-aria-props: off jsx-a11y/role-supports-aria-props: off
jsx-a11y/scope: warn jsx-a11y/scope: warn

View File

@@ -1 +1 @@
2.4.2 2.5.0

View File

@@ -9,6 +9,9 @@ cache:
- tmp/cache/babel-loader - tmp/cache/babel-loader
dist: trusty dist: trusty
sudo: required sudo: required
branches:
only:
- master
notifications: notifications:
email: false email: false
@@ -37,20 +40,20 @@ addons:
- yarn - yarn
rvm: rvm:
- 2.3.4
- 2.4.2 - 2.4.2
- 2.5.0
services: services:
- redis-server - redis-server
install: install:
- gem update --system
- nvm install - nvm install
- bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16 - bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16
- yarn install - yarn install
before_script: before_script:
- bundle exec rake parallel:create parallel:load_schema parallel:prepare - ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile
- bundle exec rails assets:precompile
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++" - ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
script: 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" \ LABEL maintainer="https://github.com/tootsuite/mastodon" \
description="A GNU Social-compatible microblogging server" description="A GNU Social-compatible microblogging server"
@@ -40,6 +40,7 @@ RUN apk -U upgrade \
protobuf \ protobuf \
su-exec \ su-exec \
tini \ tini \
tzdata \
&& update-ca-certificates \ && update-ca-certificates \
&& mkdir -p /tmp/src /opt \ && 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" \ && wget -O yarn.tar.gz "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'https://rubygems.org' source 'https://rubygems.org'
ruby '>= 2.3.0', '< 2.5.0' ruby '>= 2.3.0', '< 2.6.0'
gem 'pkg-config', '~> 1.2' gem 'pkg-config', '~> 1.2'
@@ -28,7 +28,7 @@ gem 'browser'
gem 'charlock_holmes', '~> 0.7.5' gem 'charlock_holmes', '~> 0.7.5'
gem 'iso-639' gem 'iso-639'
gem 'cld3', '~> 3.2.0' gem 'cld3', '~> 3.2.0'
gem 'devise', '~> 4.3' gem 'devise', '~> 4.4'
gem 'devise-two-factor', '~> 3.0' gem 'devise-two-factor', '~> 3.0'
gem 'doorkeeper', '~> 4.2' gem 'doorkeeper', '~> 4.2'
gem 'fast_blank', '~> 1.0' gem 'fast_blank', '~> 1.0'
@@ -49,6 +49,7 @@ gem 'oj', '~> 3.3'
gem 'ostatus2', '~> 2.0' gem 'ostatus2', '~> 2.0'
gem 'ox', '~> 2.8' gem 'ox', '~> 2.8'
gem 'pundit', '~> 1.1' gem 'pundit', '~> 1.1'
gem 'premailer-rails'
gem 'rack-attack', '~> 5.0' gem 'rack-attack', '~> 5.0'
gem 'rack-cors', '~> 0.4', require: 'rack/cors' gem 'rack-cors', '~> 0.4', require: 'rack/cors'
gem 'rack-timeout', '~> 0.4' gem 'rack-timeout', '~> 0.4'

View File

@@ -110,7 +110,7 @@ GEM
activesupport activesupport
charlock_holmes (0.7.5) charlock_holmes (0.7.5)
chunky_png (1.3.8) chunky_png (1.3.8)
cld3 (3.2.1) cld3 (3.2.2)
ffi (>= 1.1.0, < 1.10.0) ffi (>= 1.1.0, < 1.10.0)
climate_control (0.2.0) climate_control (0.2.0)
cocaine (0.5.8) cocaine (0.5.8)
@@ -122,8 +122,10 @@ GEM
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
crass (1.0.3) crass (1.0.3)
css_parser (1.6.0)
addressable
debug_inspector (0.0.3) debug_inspector (0.0.3)
devise (4.3.0) devise (4.4.0)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.2) railties (>= 4.1.0, < 5.2)
@@ -324,6 +326,13 @@ GEM
activerecord activerecord
pkg-config (1.2.8) pkg-config (1.2.8)
powerpack (0.1.1) 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) pry (0.11.3)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.9.0) method_source (~> 0.9.0)
@@ -559,7 +568,7 @@ DEPENDENCIES
charlock_holmes (~> 0.7.5) charlock_holmes (~> 0.7.5)
cld3 (~> 3.2.0) cld3 (~> 3.2.0)
climate_control (~> 0.2) climate_control (~> 0.2)
devise (~> 4.3) devise (~> 4.4)
devise-two-factor (~> 3.0) devise-two-factor (~> 3.0)
doorkeeper (~> 4.2) doorkeeper (~> 4.2)
dotenv-rails (~> 2.2) dotenv-rails (~> 2.2)
@@ -600,6 +609,7 @@ DEPENDENCIES
pg (~> 0.20) pg (~> 0.20)
pghero (~> 1.7) pghero (~> 1.7)
pkg-config (~> 1.2) pkg-config (~> 1.2)
premailer-rails
pry-rails (~> 0.3) pry-rails (~> 0.3)
puma (~> 3.10) puma (~> 3.10)
pundit (~> 1.1) pundit (~> 1.1)
@@ -639,7 +649,7 @@ DEPENDENCIES
webpush webpush
RUBY VERSION RUBY VERSION
ruby 2.4.2p198 ruby 2.5.0p0
BUNDLED WITH BUNDLED WITH
1.16.1 1.16.1

View File

@@ -1,22 +0,0 @@
# frozen_string_literal: true
class ActivityPub::FollowsController < Api::BaseController
include SignatureVerification
def show
render json: follow_request,
serializer: ActivityPub::FollowSerializer,
adapter: ActivityPub::Adapter,
content_type: 'application/activity+json'
end
private
def follow_request
FollowRequest.includes(:account).references(:account).find_by!(
id: params.require(:id),
accounts: { domain: nil, username: params.require(:account_username) },
target_account: signed_request_account
)
end
end

View File

@@ -6,8 +6,8 @@ class Api::BaseController < ApplicationController
include RateLimitHeaders include RateLimitHeaders
skip_before_action :verify_authenticity_token
skip_before_action :store_current_location skip_before_action :store_current_location
protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
render json: { error: e.to_s }, status: 422 render json: { error: e.to_s }, status: 422

View File

@@ -21,9 +21,9 @@ class Api::V1::Instances::ActivityController < Api::BaseController
weeks << { weeks << {
week: week.to_time.to_i.to_s, week: week.to_time.to_i.to_s,
statuses: Redis.current.get("activity:statuses:local:#{week_id}") || 0, statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0',
logins: Redis.current.pfcount("activity:logins:#{week_id}"), logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s,
registrations: Redis.current.get("activity:accounts:local:#{week_id}") || 0, registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0',
} }
end end

View File

@@ -9,7 +9,11 @@ class Api::V1::Timelines::HomeController < Api::BaseController
def show def show
@statuses = load_statuses @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 end
private private
@@ -57,4 +61,8 @@ class Api::V1::Timelines::HomeController < Api::BaseController
def pagination_since_id def pagination_since_id
@statuses.first.id @statuses.first.id
end end
def regeneration_in_progress?
Redis.current.exists("account:#{current_account.id}:regeneration")
end
end end

View File

@@ -4,6 +4,7 @@ class Api::Web::PushSubscriptionsController < Api::BaseController
respond_to :json respond_to :json
before_action :require_user! before_action :require_user!
protect_from_forgery with: :exception
def create def create
params.require(:subscription).require(:endpoint) params.require(:subscription).require(:endpoint)

View File

@@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base
private private
def https_enabled? def https_enabled?
Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true' Rails.env.production?
end end
def store_current_location def store_current_location
@@ -124,15 +124,15 @@ class ApplicationController < ActionController::Base
def render_cached_json(cache_key, **options) def render_cached_json(cache_key, **options)
options[:expires_in] ||= 3.minutes options[:expires_in] ||= 3.minutes
options[:public] ||= true
cache_key = cache_key.join(':') if cache_key.is_a?(Enumerable) 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' content_type = options.delete(:content_type) || 'application/json'
data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do
yield.to_json yield.to_json
end end
expires_in options[:expires_in], public: options[:public] expires_in options[:expires_in], public: cache_public
render json: data, content_type: content_type render json: data, content_type: content_type
end end

View File

@@ -32,7 +32,7 @@ module UserTrackingConcern
end end
def regenerate_feed! 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) Redis.current.setnx("account:#{current_user.account_id}:regeneration", true) && Redis.current.expire("account:#{current_user.account_id}:regeneration", 1.day.seconds)
RegenerationWorker.perform_async(current_user.account_id) RegenerationWorker.perform_async(current_user.account_id)
end end
end end

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="2048" height="1792" viewBox="0 0 2048 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1344 1504q0 13-9.5 22.5t-22.5 9.5h-960q-8 0-13.5-2t-9-7-5.5-8-3-11.5-1-11.5v-600h-192q-26 0-45-19t-19-45q0-24 15-41l320-384q19-22 49-22t49 22l320 384q15 17 15 41 0 26-19 45t-45 19h-192v384h576q16 0 25 11l160 192q7 10 7 21zm640-416q0 24-15 41l-320 384q-20 23-49 23t-49-23l-320-384q-15-17-15-41 0-26 19-45t45-19h192v-384h-576q-16 0-25-12l-160-192q-7-9-7-20 0-13 9.5-22.5t22.5-9.5h960q8 0 13.5 2t9 7 5.5 8 3 11.5 1 11.5v600h192q26 0 45 19t19 45z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 604 B

View File

@@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 214 B

View File

@@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 273 B

View File

@@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 252 B

View File

@@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"/>
</svg>

After

Width:  |  Height:  |  Size: 395 B

View File

@@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</svg>

After

Width:  |  Height:  |  Size: 305 B

View File

@@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 220 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.4144 232.00976"><path d="M107.86523 0C78.203984.2425 49.672422 3.4535937 33.044922 11.089844c0 0-32.97656262 14.752031-32.97656262 65.082031 0 11.525-.224375 25.306175.140625 39.919925 1.19750002 49.22 9.02375002 97.72843 54.53124962 109.77343 20.9825 5.55375 38.99711 6.71547 53.505856 5.91797 26.31125-1.45875 41.08203-9.38867 41.08203-9.38867l-.86914-19.08984s-18.80171 5.92758-39.91796 5.20508c-20.921254-.7175-43.006879-2.25516-46.390629-27.94141-.3125-2.25625-.46875-4.66938-.46875-7.20313 0 0 20.536953 5.0204 46.564449 6.21289 15.915.73001 30.8393-.93343 45.99805-2.74218 29.07-3.47125 54.38125-21.3818 57.5625-37.74805 5.0125-25.78125 4.59961-62.916015 4.59961-62.916015 0-50.33-32.97461-65.082031-32.97461-65.082031C166.80539 3.4535938 138.255.2425 108.59375 0h-.72852zM74.296875 39.326172c12.355 0 21.710234 4.749297 27.896485 14.248047l6.01367 10.080078 6.01563-10.080078c6.185-9.49875 15.54023-14.248047 27.89648-14.248047 10.6775 0 19.28156 3.753672 25.85156 11.076172 6.36875 7.3225 9.53907 17.218828 9.53907 29.673828v60.941408h-24.14454V81.869141c0-12.46875-5.24453-18.798829-15.73828-18.798829-11.6025 0-17.41797 7.508516-17.41797 22.353516v32.375002H96.207031V85.423828c0-14.845-5.815468-22.353515-17.417969-22.353516-10.49375 0-15.740234 6.330079-15.740234 18.798829v59.148439H38.904297V80.076172c0-12.455 3.171016-22.351328 9.541015-29.673828 6.568751-7.3225 15.172813-11.076172 25.851563-11.076172z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -1,4 +1,4 @@
import axios from 'axios'; import api from '../../api';
import { pushNotificationsSetting } from '../../settings'; import { pushNotificationsSetting } from '../../settings';
import { setBrowserSupport, setSubscription, clearSubscription } from './setter'; import { setBrowserSupport, setSubscription, clearSubscription } from './setter';
@@ -35,7 +35,7 @@ const subscribe = (registration) =>
const unsubscribe = ({ registration, subscription }) => const unsubscribe = ({ registration, subscription }) =>
subscription ? subscription.unsubscribe().then(() => registration) : registration; subscription ? subscription.unsubscribe().then(() => registration) : registration;
const sendSubscriptionToBackend = (subscription, me) => { const sendSubscriptionToBackend = (getState, subscription, me) => {
const params = { subscription }; const params = { subscription };
if (me) { if (me) {
@@ -45,7 +45,7 @@ const sendSubscriptionToBackend = (subscription, me) => {
} }
} }
return axios.post('/api/web/push_subscriptions', params).then(response => response.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 // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload
@@ -85,13 +85,13 @@ export function register () {
} else { } else {
// Something went wrong, try to subscribe again // Something went wrong, try to subscribe again
return unsubscribe({ registration, subscription }).then(subscribe).then( return unsubscribe({ registration, subscription }).then(subscribe).then(
subscription => sendSubscriptionToBackend(subscription, me)); subscription => sendSubscriptionToBackend(getState, subscription, me));
} }
} }
// No subscription, try to subscribe // No subscription, try to subscribe
return subscribe(registration).then( return subscribe(registration).then(
subscription => sendSubscriptionToBackend(subscription, me)); subscription => sendSubscriptionToBackend(getState, subscription, me));
}) })
.then(subscription => { .then(subscription => {
// If we got a PushSubscription (and not a subscription object from the backend) // If we got a PushSubscription (and not a subscription object from the backend)
@@ -137,7 +137,7 @@ export function saveSettings() {
const alerts = state.get('alerts'); const alerts = state.get('alerts');
const data = { alerts }; const data = { alerts };
axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, { api(getState).put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
data, data,
}).then(() => { }).then(() => {
const me = getState().getIn(['meta', 'me']); const me = getState().getIn(['meta', 'me']);

View File

@@ -1,4 +1,4 @@
import axios from 'axios'; import api from '../api';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
export const SETTING_CHANGE = 'SETTING_CHANGE'; export const SETTING_CHANGE = 'SETTING_CHANGE';
@@ -23,7 +23,7 @@ const debouncedSave = debounce((dispatch, getState) => {
const data = getState().get('settings').filter((_, path) => path !== '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 }); }, 5000, { trailing: true });
export function saveSettings() { export function saveSettings() {

View File

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

View File

@@ -1,4 +1,5 @@
import axios from 'axios'; import axios from 'axios';
import ready from './ready';
import LinkHeader from './link_header'; import LinkHeader from './link_header';
export const getLinks = response => { export const getLinks = response => {
@@ -11,10 +12,17 @@ export const getLinks = response => {
return LinkHeader.parse(value); return LinkHeader.parse(value);
}; };
let csrfHeader = {};
function setCSRFHeader() {
const csrfToken = document.querySelector('meta[name=csrf-token]').content;
csrfHeader['X-CSRF-Token'] = csrfToken;
}
ready(setCSRFHeader);
export default getState => axios.create({ export default getState => axios.create({
headers: { headers: Object.assign(csrfHeader, getState ? {
'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`, 'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`,
}, } : {}),
transformResponse: [function (data) { transformResponse: [function (data) {
try { try {

View File

@@ -4,14 +4,16 @@ exports[`<DisplayName /> renders display name + account name 1`] = `
<span <span
className="display-name" className="display-name"
> >
<strong <bdi>
className="display-name__html" <strong
dangerouslySetInnerHTML={ className="display-name__html"
Object { dangerouslySetInnerHTML={
"__html": "<p>Foo</p>", Object {
"__html": "<p>Foo</p>",
}
} }
} />
/> </bdi>
<span <span
className="display-name__account" className="display-name__account"

View File

@@ -93,7 +93,7 @@ export default class Account extends ImmutablePureComponent {
{hidingNotificationsButton} {hidingNotificationsButton}
</Fragment> </Fragment>
); );
} else if (!account.get('moved')) { } else if (!account.get('moved') || following) {
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />; buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
} }
} }

View File

@@ -20,11 +20,11 @@ export default class AttachmentList extends ImmutablePureComponent {
</div> </div>
<ul className='attachment-list__list'> <ul className='attachment-list__list'>
{media.map(attachment => {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> <a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a>
</li> </li>
)} ))}
</ul> </ul>
</div> </div>
); );

View File

@@ -5,11 +5,11 @@ import PropTypes from 'prop-types';
const Collapsable = ({ fullHeight, isVisible, children }) => ( 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) }}> <Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}>
{({ opacity, height }) => {({ 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} {children}
</div> </div>
} )}
</Motion> </Motion>
); );

View File

@@ -23,7 +23,6 @@ export default class ColumnHeader extends React.PureComponent {
icon: PropTypes.string.isRequired, icon: PropTypes.string.isRequired,
active: PropTypes.bool, active: PropTypes.bool,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
focusable: PropTypes.bool,
showBackButton: PropTypes.bool, showBackButton: PropTypes.bool,
children: PropTypes.node, children: PropTypes.node,
pinned: PropTypes.bool, pinned: PropTypes.bool,
@@ -32,10 +31,6 @@ export default class ColumnHeader extends React.PureComponent {
onClick: PropTypes.func, onClick: PropTypes.func,
}; };
static defaultProps = {
focusable: true,
}
state = { state = {
collapsed: true, collapsed: true,
animating: false, animating: false,
@@ -68,7 +63,7 @@ export default class ColumnHeader extends React.PureComponent {
} }
render () { render () {
const { title, icon, active, children, pinned, onPin, multiColumn, focusable, showBackButton, intl: { formatMessage } } = this.props; const { title, icon, active, children, pinned, onPin, multiColumn, showBackButton, intl: { formatMessage } } = this.props;
const { collapsed, animating } = this.state; const { collapsed, animating } = this.state;
const wrapperClassName = classNames('column-header__wrapper', { const wrapperClassName = classNames('column-header__wrapper', {
@@ -135,11 +130,13 @@ export default class ColumnHeader extends React.PureComponent {
return ( return (
<div className={wrapperClassName}> <div className={wrapperClassName}>
<h1 tabIndex={focusable ? 0 : null} role='button' className={buttonClassName} aria-label={title} onClick={this.handleTitleClick}> <h1 className={buttonClassName}>
<i className={`fa fa-fw fa-${icon} column-header__icon`} /> <button onClick={this.handleTitleClick}>
<span className='column-header__title'> <i className={`fa fa-fw fa-${icon} column-header__icon`} />
{title} <span className='column-header__title'>
</span> {title}
</span>
</button>
<div className='column-header__buttons'> <div className='column-header__buttons'>
{backButton} {backButton}

View File

@@ -12,7 +12,7 @@ export default class DisplayName extends React.PureComponent {
return ( return (
<span className='display-name'> <span className='display-name'>
<strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /> <span className='display-name__account'>@{this.props.account.get('acct')}</span> <bdi><strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /></bdi> <span className='display-name__account'>@{this.props.account.get('acct')}</span>
</span> </span>
); );
} }

View File

@@ -93,7 +93,7 @@ export default class IconButton extends React.PureComponent {
return ( return (
<Motion defaultStyle={{ rotate: active ? -360 : 0 }} style={{ rotate: animate ? spring(active ? -360 : 0, { stiffness: 120, damping: 7 }) : 0 }}> <Motion defaultStyle={{ rotate: active ? -360 : 0 }} style={{ rotate: animate ? spring(active ? -360 : 0, { stiffness: 120, damping: 7 }) : 0 }}>
{({ rotate }) => {({ rotate }) => (
<button <button
aria-label={title} aria-label={title}
aria-pressed={pressed} aria-pressed={pressed}
@@ -106,7 +106,7 @@ export default class IconButton extends React.PureComponent {
> >
<i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${icon}`} aria-hidden='true' /> <i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${icon}`} aria-hidden='true' />
</button> </button>
} )}
</Motion> </Motion>
); );
} }

View File

@@ -2,9 +2,14 @@ import React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
const MissingIndicator = () => ( const MissingIndicator = () => (
<div className='missing-indicator'> <div className='regeneration-indicator missing-indicator'>
<div> <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>
</div> </div>
); );

View File

@@ -162,7 +162,7 @@ export default class Status extends ImmutablePureComponent {
prepend = ( prepend = (
<div className='status__prepend'> <div className='status__prepend'>
<div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-retweet status__prepend-icon' /></div> <div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-retweet status__prepend-icon' /></div>
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name muted'><strong dangerouslySetInnerHTML={display_name_html} /></a> }} /> <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} />
</div> </div>
); );
@@ -178,14 +178,16 @@ export default class Status extends ImmutablePureComponent {
media = ( media = (
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
{Component => <Component {Component => (
preview={video.get('preview_url')} <Component
src={video.get('url')} preview={video.get('preview_url')}
width={239} src={video.get('url')}
height={110} width={239}
sensitive={status.get('sensitive')} height={110}
onOpenVideo={this.handleOpenVideo} sensitive={status.get('sensitive')}
/>} onOpenVideo={this.handleOpenVideo}
/>
)}
</Bundle> </Bundle>
); );
} else { } else {

View File

@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import StatusContainer from '../containers/status_container'; import StatusContainer from '../containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import ScrollableList from './scrollable_list'; import ScrollableList from './scrollable_list';
import { FormattedMessage } from 'react-intl';
export default class StatusList extends ImmutablePureComponent { export default class StatusList extends ImmutablePureComponent {
@@ -16,6 +17,7 @@ export default class StatusList extends ImmutablePureComponent {
trackScroll: PropTypes.bool, trackScroll: PropTypes.bool,
shouldUpdateScroll: PropTypes.func, shouldUpdateScroll: PropTypes.func,
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
isPartial: PropTypes.bool,
hasMore: PropTypes.bool, hasMore: PropTypes.bool,
prepend: PropTypes.node, prepend: PropTypes.node,
emptyMessage: PropTypes.node, emptyMessage: PropTypes.node,
@@ -48,8 +50,23 @@ export default class StatusList extends ImmutablePureComponent {
} }
render () { render () {
const { statusIds, ...other } = this.props; const { statusIds, ...other } = this.props;
const { isLoading } = other; 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) ? ( const scrollableContent = (isLoading || statusIds.size > 0) ? (
statusIds.map((statusId) => ( statusIds.map((statusId) => (

View File

@@ -41,7 +41,7 @@ class Avatar extends ImmutablePureComponent {
return ( return (
<Motion defaultStyle={{ radius: 90 }} style={{ radius: spring(isHovered ? 30 : 90, { stiffness: 180, damping: 12 }) }}> <Motion defaultStyle={{ radius: 90 }} style={{ radius: spring(isHovered ? 30 : 90, { stiffness: 180, damping: 12 }) }}>
{({ radius }) => {({ radius }) => (
<a <a
href={account.get('url')} href={account.get('url')}
className='account__header__avatar' className='account__header__avatar'
@@ -56,7 +56,7 @@ class Avatar extends ImmutablePureComponent {
> >
<span style={{ display: 'none' }}>{account.get('acct')}</span> <span style={{ display: 'none' }}>{account.get('acct')}</span>
</a> </a>
} )}
</Motion> </Motion>
); );
} }
@@ -103,7 +103,7 @@ export default class Header extends ImmutablePureComponent {
} }
} }
if (account.get('moved')) { if (account.get('moved') && !account.getIn(['relationship', 'following'])) {
actionBtn = ''; actionBtn = '';
} }

View File

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

View File

@@ -34,7 +34,7 @@ export default class MovedNote extends ImmutablePureComponent {
<div className='account__moved-note'> <div className='account__moved-note'>
<div className='account__moved-note__message'> <div className='account__moved-note__message'>
<div className='account__moved-note__icon-wrapper'><i className='fa fa-fw fa-suitcase account__moved-note__icon' /></div> <div className='account__moved-note__icon-wrapper'><i className='fa fa-fw fa-suitcase account__moved-note__icon' /></div>
<FormattedMessage id='account.moved_to' defaultMessage='{name} has moved to:' values={{ name: <strong dangerouslySetInnerHTML={displayNameHtml} /> }} /> <FormattedMessage id='account.moved_to' defaultMessage='{name} has moved to:' values={{ name: <bdi><strong dangerouslySetInnerHTML={displayNameHtml} /></bdi> }} />
</div> </div>
<a href={to.get('url')} onClick={this.handleAccountClick} className='detailed-status__display-name'> <a href={to.get('url')} onClick={this.handleAccountClick} className='detailed-status__display-name'>

View File

@@ -72,7 +72,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
<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 }) }}> <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 }) => ( {({ opacity, scaleX, scaleY }) => (
<div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}> <div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}>
{items.map(item => {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 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'> <div className='privacy-dropdown__option__icon'>
<i className={`fa fa-fw fa-${item.icon}`} /> <i className={`fa fa-fw fa-${item.icon}`} />
@@ -83,7 +83,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
{item.meta} {item.meta}
</div> </div>
</div> </div>
)} ))}
</div> </div>
)} )}
</Motion> </Motion>

View File

@@ -40,11 +40,11 @@ export default class SearchResults extends ImmutablePureComponent {
count += results.get('hashtags').size; count += results.get('hashtags').size;
hashtags = ( hashtags = (
<div className='search-results__section'> <div className='search-results__section'>
{results.get('hashtags').map(hashtag => {results.get('hashtags').map(hashtag => (
<Link key={hashtag} className='search-results__hashtag' to={`/timelines/tag/${hashtag}`}> <Link key={hashtag} className='search-results__hashtag' to={`/timelines/tag/${hashtag}`}>
#{hashtag} #{hashtag}
</Link> </Link>
)} ))}
</div> </div>
); );
} }

View File

@@ -94,15 +94,15 @@ export default class Compose extends React.PureComponent {
<div className='drawer__inner' onFocus={this.onFocus}> <div className='drawer__inner' onFocus={this.onFocus}>
<NavigationContainer onClose={this.onBlur} /> <NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer /> <ComposeFormContainer />
<div className='mastodon' /> {multiColumn && <div className='mastodon' />}
</div> </div>
<Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}> <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
{({ x }) => {({ x }) => (
<div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}> <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
<SearchResultsContainer /> <SearchResultsContainer />
</div> </div>
} )}
</Motion> </Motion>
</div> </div>
</div> </div>

View File

@@ -40,98 +40,98 @@ export const urlRegex = (function() {
regexen.validSubdomain = regexSupplant(/(?:(?:#{validDomainChars}(?:[_-]|#{validDomainChars})*)?#{validDomainChars}\.)/); regexen.validSubdomain = regexSupplant(/(?:(?:#{validDomainChars}(?:[_-]|#{validDomainChars})*)?#{validDomainChars}\.)/);
regexen.validDomainName = regexSupplant(/(?:(?:#{validDomainChars}(?:-|#{validDomainChars})*)?#{validDomainChars}\.)/); regexen.validDomainName = regexSupplant(/(?:(?:#{validDomainChars}(?:-|#{validDomainChars})*)?#{validDomainChars}\.)/);
regexen.validGTLD = regexSupplant(RegExp( regexen.validGTLD = regexSupplant(RegExp(
'(?:(?:' + '(?:(?:' +
'삼성|닷컴|닷넷|香格里拉|餐厅|食品|飞利浦|電訊盈科|集团|通販|购物|谷歌|诺基亚|联通|网络|网站|网店|网址|组织机构|移动|珠宝|点看|游戏|淡马锡|机构|書籍|时尚|新闻|政府|' + '삼성|닷컴|닷넷|香格里拉|餐厅|食品|飞利浦|電訊盈科|集团|通販|购物|谷歌|诺基亚|联通|网络|网站|网店|网址|组织机构|移动|珠宝|点看|游戏|淡马锡|机构|書籍|时尚|新闻|政府|' +
'政务|手表|手机|我爱你|慈善|微博|广东|工行|家電|娱乐|天主教|大拿|大众汽车|在线|嘉里大酒店|嘉里|商标|商店|商城|公益|公司|八卦|健康|信息|佛山|企业|中文网|中信|世界|' + '政务|手表|手机|我爱你|慈善|微博|广东|工行|家電|娱乐|天主教|大拿|大众汽车|在线|嘉里大酒店|嘉里|商标|商店|商城|公益|公司|八卦|健康|信息|佛山|企业|中文网|中信|世界|' +
'ポイント|ファッション|セール|ストア|コム|グーグル|クラウド|みんな|คอม|संगठन|नेट|कॉम|همراه|موقع|موبايلي|كوم|كاثوليك|عرب|شبكة|' + 'ポイント|ファッション|セール|ストア|コム|グーグル|クラウド|みんな|คอม|संगठन|नेट|कॉम|همراه|موقع|موبايلي|كوم|كاثوليك|عرب|شبكة|' +
'بيتك|بازار|العليان|ارامكو|اتصالات|ابوظبي|קום|сайт|рус|орг|онлайн|москва|ком|католик|дети|' + 'بيتك|بازار|العليان|ارامكو|اتصالات|ابوظبي|קום|сайт|рус|орг|онлайн|москва|ком|католик|дети|' +
'zuerich|zone|zippo|zip|zero|zara|zappos|yun|youtube|you|yokohama|yoga|yodobashi|yandex|yamaxun|' + 'zuerich|zone|zippo|zip|zero|zara|zappos|yun|youtube|you|yokohama|yoga|yodobashi|yandex|yamaxun|' +
'yahoo|yachts|xyz|xxx|xperia|xin|xihuan|xfinity|xerox|xbox|wtf|wtc|wow|world|works|work|woodside|' + 'yahoo|yachts|xyz|xxx|xperia|xin|xihuan|xfinity|xerox|xbox|wtf|wtc|wow|world|works|work|woodside|' +
'wolterskluwer|wme|winners|wine|windows|win|williamhill|wiki|wien|whoswho|weir|weibo|wedding|wed|' + 'wolterskluwer|wme|winners|wine|windows|win|williamhill|wiki|wien|whoswho|weir|weibo|wedding|wed|' +
'website|weber|webcam|weatherchannel|weather|watches|watch|warman|wanggou|wang|walter|walmart|' + 'website|weber|webcam|weatherchannel|weather|watches|watch|warman|wanggou|wang|walter|walmart|' +
'wales|vuelos|voyage|voto|voting|vote|volvo|volkswagen|vodka|vlaanderen|vivo|viva|vistaprint|' + 'wales|vuelos|voyage|voto|voting|vote|volvo|volkswagen|vodka|vlaanderen|vivo|viva|vistaprint|' +
'vista|vision|visa|virgin|vip|vin|villas|viking|vig|video|viajes|vet|versicherung|' + 'vista|vision|visa|virgin|vip|vin|villas|viking|vig|video|viajes|vet|versicherung|' +
'vermögensberatung|vermögensberater|verisign|ventures|vegas|vanguard|vana|vacations|ups|uol|uno|' + 'vermögensberatung|vermögensberater|verisign|ventures|vegas|vanguard|vana|vacations|ups|uol|uno|' +
'university|unicom|uconnect|ubs|ubank|tvs|tushu|tunes|tui|tube|trv|trust|travelersinsurance|' + 'university|unicom|uconnect|ubs|ubank|tvs|tushu|tunes|tui|tube|trv|trust|travelersinsurance|' +
'travelers|travelchannel|travel|training|trading|trade|toys|toyota|town|tours|total|toshiba|' + 'travelers|travelchannel|travel|training|trading|trade|toys|toyota|town|tours|total|toshiba|' +
'toray|top|tools|tokyo|today|tmall|tkmaxx|tjx|tjmaxx|tirol|tires|tips|tiffany|tienda|tickets|' + 'toray|top|tools|tokyo|today|tmall|tkmaxx|tjx|tjmaxx|tirol|tires|tips|tiffany|tienda|tickets|' +
'tiaa|theatre|theater|thd|teva|tennis|temasek|telefonica|telecity|tel|technology|tech|team|tdk|' + 'tiaa|theatre|theater|thd|teva|tennis|temasek|telefonica|telecity|tel|technology|tech|team|tdk|' +
'tci|taxi|tax|tattoo|tatar|tatamotors|target|taobao|talk|taipei|tab|systems|symantec|sydney|' + 'tci|taxi|tax|tattoo|tatar|tatamotors|target|taobao|talk|taipei|tab|systems|symantec|sydney|' +
'swiss|swiftcover|swatch|suzuki|surgery|surf|support|supply|supplies|sucks|style|study|studio|' + 'swiss|swiftcover|swatch|suzuki|surgery|surf|support|supply|supplies|sucks|style|study|studio|' +
'stream|store|storage|stockholm|stcgroup|stc|statoil|statefarm|statebank|starhub|star|staples|' + 'stream|store|storage|stockholm|stcgroup|stc|statoil|statefarm|statebank|starhub|star|staples|' +
'stada|srt|srl|spreadbetting|spot|spiegel|space|soy|sony|song|solutions|solar|sohu|software|' + 'stada|srt|srl|spreadbetting|spot|spiegel|space|soy|sony|song|solutions|solar|sohu|software|' +
'softbank|social|soccer|sncf|smile|smart|sling|skype|sky|skin|ski|site|singles|sina|silk|shriram|' + 'softbank|social|soccer|sncf|smile|smart|sling|skype|sky|skin|ski|site|singles|sina|silk|shriram|' +
'showtime|show|shouji|shopping|shop|shoes|shiksha|shia|shell|shaw|sharp|shangrila|sfr|sexy|sex|' + 'showtime|show|shouji|shopping|shop|shoes|shiksha|shia|shell|shaw|sharp|shangrila|sfr|sexy|sex|' +
'sew|seven|ses|services|sener|select|seek|security|secure|seat|search|scot|scor|scjohnson|' + 'sew|seven|ses|services|sener|select|seek|security|secure|seat|search|scot|scor|scjohnson|' +
'science|schwarz|schule|school|scholarships|schmidt|schaeffler|scb|sca|sbs|sbi|saxo|save|sas|' + 'science|schwarz|schule|school|scholarships|schmidt|schaeffler|scb|sca|sbs|sbi|saxo|save|sas|' +
'sarl|sapo|sap|sanofi|sandvikcoromant|sandvik|samsung|samsclub|salon|sale|sakura|safety|safe|' + 'sarl|sapo|sap|sanofi|sandvikcoromant|sandvik|samsung|samsclub|salon|sale|sakura|safety|safe|' +
'saarland|ryukyu|rwe|run|ruhr|rugby|rsvp|room|rogers|rodeo|rocks|rocher|rmit|rip|rio|ril|' + 'saarland|ryukyu|rwe|run|ruhr|rugby|rsvp|room|rogers|rodeo|rocks|rocher|rmit|rip|rio|ril|' +
'rightathome|ricoh|richardli|rich|rexroth|reviews|review|restaurant|rest|republican|report|' + 'rightathome|ricoh|richardli|rich|rexroth|reviews|review|restaurant|rest|republican|report|' +
'repair|rentals|rent|ren|reliance|reit|reisen|reise|rehab|redumbrella|redstone|red|recipes|' + 'repair|rentals|rent|ren|reliance|reit|reisen|reise|rehab|redumbrella|redstone|red|recipes|' +
'realty|realtor|realestate|read|raid|radio|racing|qvc|quest|quebec|qpon|pwc|pub|prudential|pru|' + 'realty|realtor|realestate|read|raid|radio|racing|qvc|quest|quebec|qpon|pwc|pub|prudential|pru|' +
'protection|property|properties|promo|progressive|prof|productions|prod|pro|prime|press|praxi|' + 'protection|property|properties|promo|progressive|prof|productions|prod|pro|prime|press|praxi|' +
'pramerica|post|porn|politie|poker|pohl|pnc|plus|plumbing|playstation|play|place|pizza|pioneer|' + 'pramerica|post|porn|politie|poker|pohl|pnc|plus|plumbing|playstation|play|place|pizza|pioneer|' +
'pink|ping|pin|pid|pictures|pictet|pics|piaget|physio|photos|photography|photo|phone|philips|phd|' + 'pink|ping|pin|pid|pictures|pictet|pics|piaget|physio|photos|photography|photo|phone|philips|phd|' +
'pharmacy|pfizer|pet|pccw|pay|passagens|party|parts|partners|pars|paris|panerai|panasonic|' + 'pharmacy|pfizer|pet|pccw|pay|passagens|party|parts|partners|pars|paris|panerai|panasonic|' +
'pamperedchef|page|ovh|ott|otsuka|osaka|origins|orientexpress|organic|org|orange|oracle|open|ooo|' + 'pamperedchef|page|ovh|ott|otsuka|osaka|origins|orientexpress|organic|org|orange|oracle|open|ooo|' +
'onyourside|online|onl|ong|one|omega|ollo|oldnavy|olayangroup|olayan|okinawa|office|off|observer|' + 'onyourside|online|onl|ong|one|omega|ollo|oldnavy|olayangroup|olayan|okinawa|office|off|observer|' +
'obi|nyc|ntt|nrw|nra|nowtv|nowruz|now|norton|northwesternmutual|nokia|nissay|nissan|ninja|nikon|' + 'obi|nyc|ntt|nrw|nra|nowtv|nowruz|now|norton|northwesternmutual|nokia|nissay|nissan|ninja|nikon|' +
'nike|nico|nhk|ngo|nfl|nexus|nextdirect|next|news|newholland|new|neustar|network|netflix|netbank|' + 'nike|nico|nhk|ngo|nfl|nexus|nextdirect|next|news|newholland|new|neustar|network|netflix|netbank|' +
'net|nec|nba|navy|natura|nationwide|name|nagoya|nadex|nab|mutuelle|mutual|museum|mtr|mtpc|mtn|' + 'net|nec|nba|navy|natura|nationwide|name|nagoya|nadex|nab|mutuelle|mutual|museum|mtr|mtpc|mtn|' +
'msd|movistar|movie|mov|motorcycles|moto|moscow|mortgage|mormon|mopar|montblanc|monster|money|' + 'msd|movistar|movie|mov|motorcycles|moto|moscow|mortgage|mormon|mopar|montblanc|monster|money|' +
'monash|mom|moi|moe|moda|mobily|mobile|mobi|mma|mls|mlb|mitsubishi|mit|mint|mini|mil|microsoft|' + 'monash|mom|moi|moe|moda|mobily|mobile|mobi|mma|mls|mlb|mitsubishi|mit|mint|mini|mil|microsoft|' +
'miami|metlife|merckmsd|meo|menu|men|memorial|meme|melbourne|meet|media|med|mckinsey|mcdonalds|' + 'miami|metlife|merckmsd|meo|menu|men|memorial|meme|melbourne|meet|media|med|mckinsey|mcdonalds|' +
'mcd|mba|mattel|maserati|marshalls|marriott|markets|marketing|market|map|mango|management|man|' + 'mcd|mba|mattel|maserati|marshalls|marriott|markets|marketing|market|map|mango|management|man|' +
'makeup|maison|maif|madrid|macys|luxury|luxe|lupin|lundbeck|ltda|ltd|lplfinancial|lpl|love|lotto|' + 'makeup|maison|maif|madrid|macys|luxury|luxe|lupin|lundbeck|ltda|ltd|lplfinancial|lpl|love|lotto|' +
'lotte|london|lol|loft|locus|locker|loans|loan|lixil|living|live|lipsy|link|linde|lincoln|limo|' + 'lotte|london|lol|loft|locus|locker|loans|loan|lixil|living|live|lipsy|link|linde|lincoln|limo|' +
'limited|lilly|like|lighting|lifestyle|lifeinsurance|life|lidl|liaison|lgbt|lexus|lego|legal|' + 'limited|lilly|like|lighting|lifestyle|lifeinsurance|life|lidl|liaison|lgbt|lexus|lego|legal|' +
'lefrak|leclerc|lease|lds|lawyer|law|latrobe|latino|lat|lasalle|lanxess|landrover|land|lancome|' + 'lefrak|leclerc|lease|lds|lawyer|law|latrobe|latino|lat|lasalle|lanxess|landrover|land|lancome|' +
'lancia|lancaster|lamer|lamborghini|ladbrokes|lacaixa|kyoto|kuokgroup|kred|krd|kpn|kpmg|kosher|' + 'lancia|lancaster|lamer|lamborghini|ladbrokes|lacaixa|kyoto|kuokgroup|kred|krd|kpn|kpmg|kosher|' +
'komatsu|koeln|kiwi|kitchen|kindle|kinder|kim|kia|kfh|kerryproperties|kerrylogistics|kerryhotels|' + 'komatsu|koeln|kiwi|kitchen|kindle|kinder|kim|kia|kfh|kerryproperties|kerrylogistics|kerryhotels|' +
'kddi|kaufen|juniper|juegos|jprs|jpmorgan|joy|jot|joburg|jobs|jnj|jmp|jll|jlc|jio|jewelry|jetzt|' + 'kddi|kaufen|juniper|juegos|jprs|jpmorgan|joy|jot|joburg|jobs|jnj|jmp|jll|jlc|jio|jewelry|jetzt|' +
'jeep|jcp|jcb|java|jaguar|iwc|iveco|itv|itau|istanbul|ist|ismaili|iselect|irish|ipiranga|' + 'jeep|jcp|jcb|java|jaguar|iwc|iveco|itv|itau|istanbul|ist|ismaili|iselect|irish|ipiranga|' +
'investments|intuit|international|intel|int|insure|insurance|institute|ink|ing|info|infiniti|' + 'investments|intuit|international|intel|int|insure|insurance|institute|ink|ing|info|infiniti|' +
'industries|immobilien|immo|imdb|imamat|ikano|iinet|ifm|ieee|icu|ice|icbc|ibm|hyundai|hyatt|' + 'industries|immobilien|immo|imdb|imamat|ikano|iinet|ifm|ieee|icu|ice|icbc|ibm|hyundai|hyatt|' +
'hughes|htc|hsbc|how|house|hotmail|hotels|hoteles|hot|hosting|host|hospital|horse|honeywell|' + 'hughes|htc|hsbc|how|house|hotmail|hotels|hoteles|hot|hosting|host|hospital|horse|honeywell|' +
'honda|homesense|homes|homegoods|homedepot|holiday|holdings|hockey|hkt|hiv|hitachi|hisamitsu|' + 'honda|homesense|homes|homegoods|homedepot|holiday|holdings|hockey|hkt|hiv|hitachi|hisamitsu|' +
'hiphop|hgtv|hermes|here|helsinki|help|healthcare|health|hdfcbank|hdfc|hbo|haus|hangout|hamburg|' + 'hiphop|hgtv|hermes|here|helsinki|help|healthcare|health|hdfcbank|hdfc|hbo|haus|hangout|hamburg|' +
'hair|guru|guitars|guide|guge|gucci|guardian|group|grocery|gripe|green|gratis|graphics|grainger|' + 'hair|guru|guitars|guide|guge|gucci|guardian|group|grocery|gripe|green|gratis|graphics|grainger|' +
'gov|got|gop|google|goog|goodyear|goodhands|goo|golf|goldpoint|gold|godaddy|gmx|gmo|gmbh|gmail|' + 'gov|got|gop|google|goog|goodyear|goodhands|goo|golf|goldpoint|gold|godaddy|gmx|gmo|gmbh|gmail|' +
'globo|global|gle|glass|glade|giving|gives|gifts|gift|ggee|george|genting|gent|gea|gdn|gbiz|' + 'globo|global|gle|glass|glade|giving|gives|gifts|gift|ggee|george|genting|gent|gea|gdn|gbiz|' +
'garden|gap|games|game|gallup|gallo|gallery|gal|fyi|futbol|furniture|fund|fun|fujixerox|fujitsu|' + 'garden|gap|games|game|gallup|gallo|gallery|gal|fyi|futbol|furniture|fund|fun|fujixerox|fujitsu|' +
'ftr|frontier|frontdoor|frogans|frl|fresenius|free|fox|foundation|forum|forsale|forex|ford|' + 'ftr|frontier|frontdoor|frogans|frl|fresenius|free|fox|foundation|forum|forsale|forex|ford|' +
'football|foodnetwork|food|foo|fly|flsmidth|flowers|florist|flir|flights|flickr|fitness|fit|' + 'football|foodnetwork|food|foo|fly|flsmidth|flowers|florist|flir|flights|flickr|fitness|fit|' +
'fishing|fish|firmdale|firestone|fire|financial|finance|final|film|fido|fidelity|fiat|ferrero|' + 'fishing|fish|firmdale|firestone|fire|financial|finance|final|film|fido|fidelity|fiat|ferrero|' +
'ferrari|feedback|fedex|fast|fashion|farmers|farm|fans|fan|family|faith|fairwinds|fail|fage|' + 'ferrari|feedback|fedex|fast|fashion|farmers|farm|fans|fan|family|faith|fairwinds|fail|fage|' +
'extraspace|express|exposed|expert|exchange|everbank|events|eus|eurovision|etisalat|esurance|' + 'extraspace|express|exposed|expert|exchange|everbank|events|eus|eurovision|etisalat|esurance|' +
'estate|esq|erni|ericsson|equipment|epson|epost|enterprises|engineering|engineer|energy|emerck|' + 'estate|esq|erni|ericsson|equipment|epson|epost|enterprises|engineering|engineer|energy|emerck|' +
'email|education|edu|edeka|eco|eat|earth|dvr|dvag|durban|dupont|duns|dunlop|duck|dubai|dtv|drive|' + 'email|education|edu|edeka|eco|eat|earth|dvr|dvag|durban|dupont|duns|dunlop|duck|dubai|dtv|drive|' +
'download|dot|doosan|domains|doha|dog|dodge|doctor|docs|dnp|diy|dish|discover|discount|directory|' + 'download|dot|doosan|domains|doha|dog|dodge|doctor|docs|dnp|diy|dish|discover|discount|directory|' +
'direct|digital|diet|diamonds|dhl|dev|design|desi|dentist|dental|democrat|delta|deloitte|dell|' + 'direct|digital|diet|diamonds|dhl|dev|design|desi|dentist|dental|democrat|delta|deloitte|dell|' +
'delivery|degree|deals|dealer|deal|dds|dclk|day|datsun|dating|date|data|dance|dad|dabur|cyou|' + 'delivery|degree|deals|dealer|deal|dds|dclk|day|datsun|dating|date|data|dance|dad|dabur|cyou|' +
'cymru|cuisinella|csc|cruises|cruise|crs|crown|cricket|creditunion|creditcard|credit|courses|' + 'cymru|cuisinella|csc|cruises|cruise|crs|crown|cricket|creditunion|creditcard|credit|courses|' +
'coupons|coupon|country|corsica|coop|cool|cookingchannel|cooking|contractors|contact|consulting|' + 'coupons|coupon|country|corsica|coop|cool|cookingchannel|cooking|contractors|contact|consulting|' +
'construction|condos|comsec|computer|compare|company|community|commbank|comcast|com|cologne|' + 'construction|condos|comsec|computer|compare|company|community|commbank|comcast|com|cologne|' +
'college|coffee|codes|coach|clubmed|club|cloud|clothing|clinique|clinic|click|cleaning|claims|' + 'college|coffee|codes|coach|clubmed|club|cloud|clothing|clinique|clinic|click|cleaning|claims|' +
'cityeats|city|citic|citi|citadel|cisco|circle|cipriani|church|chrysler|chrome|christmas|chloe|' + 'cityeats|city|citic|citi|citadel|cisco|circle|cipriani|church|chrysler|chrome|christmas|chloe|' +
'chintai|cheap|chat|chase|channel|chanel|cfd|cfa|cern|ceo|center|ceb|cbs|cbre|cbn|cba|catholic|' + 'chintai|cheap|chat|chase|channel|chanel|cfd|cfa|cern|ceo|center|ceb|cbs|cbre|cbn|cba|catholic|' +
'catering|cat|casino|cash|caseih|case|casa|cartier|cars|careers|career|care|cards|caravan|car|' + 'catering|cat|casino|cash|caseih|case|casa|cartier|cars|careers|career|care|cards|caravan|car|' +
'capitalone|capital|capetown|canon|cancerresearch|camp|camera|cam|calvinklein|call|cal|cafe|cab|' + 'capitalone|capital|capetown|canon|cancerresearch|camp|camera|cam|calvinklein|call|cal|cafe|cab|' +
'bzh|buzz|buy|business|builders|build|bugatti|budapest|brussels|brother|broker|broadway|' + 'bzh|buzz|buy|business|builders|build|bugatti|budapest|brussels|brother|broker|broadway|' +
'bridgestone|bradesco|box|boutique|bot|boston|bostik|bosch|boots|booking|book|boo|bond|bom|bofa|' + 'bridgestone|bradesco|box|boutique|bot|boston|bostik|bosch|boots|booking|book|boo|bond|bom|bofa|' +
'boehringer|boats|bnpparibas|bnl|bmw|bms|blue|bloomberg|blog|blockbuster|blanco|blackfriday|' + 'boehringer|boats|bnpparibas|bnl|bmw|bms|blue|bloomberg|blog|blockbuster|blanco|blackfriday|' +
'black|biz|bio|bingo|bing|bike|bid|bible|bharti|bet|bestbuy|best|berlin|bentley|beer|beauty|' + 'black|biz|bio|bingo|bing|bike|bid|bible|bharti|bet|bestbuy|best|berlin|bentley|beer|beauty|' +
'beats|bcn|bcg|bbva|bbt|bbc|bayern|bauhaus|basketball|baseball|bargains|barefoot|barclays|' + 'beats|bcn|bcg|bbva|bbt|bbc|bayern|bauhaus|basketball|baseball|bargains|barefoot|barclays|' +
'barclaycard|barcelona|bar|bank|band|bananarepublic|banamex|baidu|baby|azure|axa|aws|avianca|' + 'barclaycard|barcelona|bar|bank|band|bananarepublic|banamex|baidu|baby|azure|axa|aws|avianca|' +
'autos|auto|author|auspost|audio|audible|audi|auction|attorney|athleta|associates|asia|asda|arte|' + 'autos|auto|author|auspost|audio|audible|audi|auction|attorney|athleta|associates|asia|asda|arte|' +
'art|arpa|army|archi|aramco|arab|aquarelle|apple|app|apartments|aol|anz|anquan|android|analytics|' + 'art|arpa|army|archi|aramco|arab|aquarelle|apple|app|apartments|aol|anz|anquan|android|analytics|' +
'amsterdam|amica|amfam|amex|americanfamily|americanexpress|alstom|alsace|ally|allstate|allfinanz|' + 'amsterdam|amica|amfam|amex|americanfamily|americanexpress|alstom|alsace|ally|allstate|allfinanz|' +
'alipay|alibaba|alfaromeo|akdn|airtel|airforce|airbus|aigo|aig|agency|agakhan|africa|afl|' + 'alipay|alibaba|alfaromeo|akdn|airtel|airforce|airbus|aigo|aig|agency|agakhan|africa|afl|' +
'afamilycompany|aetna|aero|aeg|adult|ads|adac|actor|active|aco|accountants|accountant|accenture|' + 'afamilycompany|aetna|aero|aeg|adult|ads|adac|actor|active|aco|accountants|accountant|accenture|' +
'academy|abudhabi|abogado|able|abc|abbvie|abbott|abb|abarth|aarp|aaa|onion' + 'academy|abudhabi|abogado|able|abc|abbvie|abbott|abb|abarth|aarp|aaa|onion' +
')(?=[^0-9a-zA-Z@]|$))')); ')(?=[^0-9a-zA-Z@]|$))'));
regexen.validCCTLD = regexSupplant(RegExp( regexen.validCCTLD = regexSupplant(RegExp(
'(?:(?:' + '(?:(?:' +
'한국|香港|澳門|新加坡|台灣|台湾|中國|中国|გე|ไทย|ලංකා|ഭാരതം|ಭಾರತ|భారత్|சிங்கப்பூர்|இலங்கை|இந்தியா|ଭାରତ|ભારત|ਭਾਰਤ|' + '한국|香港|澳門|新加坡|台灣|台湾|中國|中国|გე|ไทย|ලංකා|ഭാരതം|ಭಾರತ|భారత్|சிங்கப்பூர்|இலங்கை|இந்தியா|ଭାରତ|ભારત|ਭਾਰਤ|' +
'ভাৰত|ভারত|বাংলা|भारोत|भारतम्|भारत|ڀارت|پاکستان|مليسيا|مصر|قطر|فلسطين|عمان|عراق|سورية|سودان|تونس|' + 'ভাৰত|ভারত|বাংলা|भारोत|भारतम्|भारत|ڀارت|پاکستان|مليسيا|مصر|قطر|فلسطين|عمان|عراق|سورية|سودان|تونس|' +
'بھارت|بارت|ایران|امارات|المغرب|السعودية|الجزائر|الاردن|հայ|қаз|укр|срб|рф|мон|мкд|ею|бел|бг|ελ|' + 'بھارت|بارت|ایران|امارات|المغرب|السعودية|الجزائر|الاردن|հայ|қаз|укр|срб|рф|мон|мкд|ею|бел|бг|ελ|' +
@@ -143,7 +143,7 @@ export const urlRegex = (function() {
'gu|gt|gs|gr|gq|gp|gn|gm|gl|gi|gh|gg|gf|ge|gd|gb|ga|fr|fo|fm|fk|fj|fi|eu|et|es|er|eh|eg|ee|ec|dz|' + 'gu|gt|gs|gr|gq|gp|gn|gm|gl|gi|gh|gg|gf|ge|gd|gb|ga|fr|fo|fm|fk|fj|fi|eu|et|es|er|eh|eg|ee|ec|dz|' +
'do|dm|dk|dj|de|cz|cy|cx|cw|cv|cu|cr|co|cn|cm|cl|ck|ci|ch|cg|cf|cd|cc|ca|bz|by|bw|bv|bt|bs|br|bq|' + 'do|dm|dk|dj|de|cz|cy|cx|cw|cv|cu|cr|co|cn|cm|cl|ck|ci|ch|cg|cf|cd|cc|ca|bz|by|bw|bv|bt|bs|br|bq|' +
'bo|bn|bm|bl|bj|bi|bh|bg|bf|be|bd|bb|ba|az|ax|aw|au|at|as|ar|aq|ao|an|am|al|ai|ag|af|ae|ad|ac' + 'bo|bn|bm|bl|bj|bi|bh|bg|bf|be|bd|bb|ba|az|ax|aw|au|at|as|ar|aq|ao|an|am|al|ai|ag|af|ae|ad|ac' +
')(?=[^0-9a-zA-Z@]|$))')); ')(?=[^0-9a-zA-Z@]|$))'));
regexen.validPunycode = /(?:xn--[0-9a-z]+)/; regexen.validPunycode = /(?:xn--[0-9a-z]+)/;
regexen.validSpecialCCTLD = /(?:(?:co|tv)(?=[^0-9a-zA-Z@]|$))/; regexen.validSpecialCCTLD = /(?:(?:co|tv)(?=[^0-9a-zA-Z@]|$))/;
regexen.validDomain = regexSupplant(/(?:#{validSubdomain}*#{validDomainName}(?:#{validGTLD}|#{validCCTLD}|#{validPunycode}))/); regexen.validDomain = regexSupplant(/(?:#{validSubdomain}*#{validDomainName}(?:#{validGTLD}|#{validCCTLD}|#{validPunycode}))/);
@@ -168,8 +168,8 @@ export const urlRegex = (function() {
'#{validGeneralUrlPathChars}*' + '#{validGeneralUrlPathChars}*' +
')' + ')' +
')' + ')' +
'\\)' '\\)',
, 'i'); 'i');
// Valid end-of-path chracters (so /foo. does not gobble the period). // Valid end-of-path chracters (so /foo. does not gobble the period).
// 1. Allow =&# for empty URL parameters and other URL-join artifacts // 1. Allow =&# for empty URL parameters and other URL-join artifacts
regexen.validUrlPathEndingChars = regexSupplant(/[^#{spaces_group}\(\)\?!\*';:=\,\.\$%\[\]#{pd}~&\|@]|(?:#{validUrlBalancedParens})/i); regexen.validUrlPathEndingChars = regexSupplant(/[^#{spaces_group}\(\)\?!\*';:=\,\.\$%\[\]#{pd}~&\|@]|(?:#{validUrlBalancedParens})/i);
@@ -190,7 +190,7 @@ export const urlRegex = (function() {
'(?::(#{validPortNumber}))?' + // $4 Port number (optional) '(?::(#{validPortNumber}))?' + // $4 Port number (optional)
'(\\/#{validUrlPath}*)?' + // $5 URL Path '(\\/#{validUrlPath}*)?' + // $5 URL Path
'(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?' + // $6 Query String '(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?' + // $6 Query String
')' ')',
, 'gi'); 'gi');
return regexen.validUrl; return regexen.validUrl;
}()); }());

View File

@@ -24,10 +24,10 @@ describe('emoji', () => {
expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual( expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual(
'<img draggable="false" class="emojione" alt="👩‍👩‍👦‍👦" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />'); '<img draggable="false" class="emojione" alt="👩‍👩‍👦‍👦" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />');
expect(emojify('👨‍👩‍👧‍👧')).toEqual( expect(emojify('👨‍👩‍👧‍👧')).toEqual(
'<img draggable="false" class="emojione" alt="👨‍👩‍👧‍👧" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />'); '<img draggable="false" class="emojione" alt="👨‍👩‍👧‍👧" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />');
expect(emojify('👩‍👩‍👦')).toEqual('<img draggable="false" class="emojione" alt="👩‍👩‍👦" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />'); expect(emojify('👩‍👩‍👦')).toEqual('<img draggable="false" class="emojione" alt="👩‍👩‍👦" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />');
expect(emojify('\u2757')).toEqual( expect(emojify('\u2757')).toEqual(
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />'); '<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
}); });
it('does multiple unicode', () => { it('does multiple unicode', () => {

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { expandHomeTimeline } from '../../actions/timelines'; import { expandHomeTimeline, refreshHomeTimeline } from '../../actions/timelines';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column'; import Column from '../../components/column';
@@ -16,6 +16,7 @@ const messages = defineMessages({
const mapStateToProps = state => ({ const mapStateToProps = state => ({
hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0, hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0,
isPartial: state.getIn(['timelines', 'home', 'isPartial'], false),
}); });
@connect(mapStateToProps) @connect(mapStateToProps)
@@ -26,6 +27,7 @@ export default class HomeTimeline extends React.PureComponent {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
isPartial: PropTypes.bool,
columnId: PropTypes.string, columnId: PropTypes.string,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
}; };
@@ -57,6 +59,39 @@ export default class HomeTimeline extends React.PureComponent {
this.props.dispatch(expandHomeTimeline()); this.props.dispatch(expandHomeTimeline());
} }
componentDidMount () {
this._checkIfReloadNeeded(false, this.props.isPartial);
}
componentDidUpdate (prevProps) {
this._checkIfReloadNeeded(prevProps.isPartial, this.props.isPartial);
}
componentWillUnmount () {
this._stopPolling();
}
_checkIfReloadNeeded (wasPartial, isPartial) {
const { dispatch } = this.props;
if (wasPartial === isPartial) {
return;
} else if (!wasPartial && isPartial) {
this.polling = setInterval(() => {
dispatch(refreshHomeTimeline());
}, 3000);
} else if (wasPartial && !isPartial) {
this._stopPolling();
}
}
_stopPolling () {
if (this.polling) {
clearInterval(this.polling);
this.polling = null;
}
}
render () { render () {
const { intl, hasUnread, columnId, multiColumn } = this.props; const { intl, hasUnread, columnId, multiColumn } = this.props;
const pinned = !!columnId; const pinned = !!columnId;

View File

@@ -66,11 +66,11 @@ export default class ListEditor extends ImmutablePureComponent {
{showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />} {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />}
<Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}> <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
{({ x }) => {({ x }) => (
<div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}> <div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)} {searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
</div> </div>
} )}
</Motion> </Motion>
</div> </div>
</div> </div>

View File

@@ -120,13 +120,17 @@ export default class ListTimeline extends React.PureComponent {
if (typeof list === 'undefined') { if (typeof list === 'undefined') {
return ( return (
<Column> <Column>
<LoadingIndicator /> <div className='scrollable'>
<LoadingIndicator />
</div>
</Column> </Column>
); );
} else if (list === false) { } else if (list === false) {
return ( return (
<Column> <Column>
<MissingIndicator /> <div className='scrollable'>
<MissingIndicator />
</div>
</Column> </Column>
); );
} }

View File

@@ -133,7 +133,7 @@ export default class Notification extends ImmutablePureComponent {
const { notification } = this.props; const { notification } = this.props;
const account = notification.get('account'); const account = notification.get('account');
const displayNameHtml = { __html: account.get('display_name_html') }; const displayNameHtml = { __html: account.get('display_name_html') };
const link = <Permalink className='notification__display-name' href={account.get('url')} title={account.get('acct')} to={`/accounts/${account.get('id')}`} dangerouslySetInnerHTML={displayNameHtml} />; const link = <bdi><Permalink className='notification__display-name' href={account.get('url')} title={account.get('acct')} to={`/accounts/${account.get('id')}`} dangerouslySetInnerHTML={displayNameHtml} /></bdi>;
switch(notification.get('type')) { switch(notification.get('type')) {
case 'follow': case 'follow':

View File

@@ -25,7 +25,7 @@ export default class ColumnHeader extends React.PureComponent {
} }
return ( return (
<div role='heading' tabIndex='0' className={`column-header ${active ? 'active' : ''}`} onClick={this.handleClick} id={columnHeaderId || null}> <div role='button heading' tabIndex='0' className={`column-header ${active ? 'active' : ''}`} onClick={this.handleClick} id={columnHeaderId || null}>
{icon} {icon}
{type} {type}
</div> </div>

View File

@@ -37,6 +37,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
columns: ImmutablePropTypes.list.isRequired, columns: ImmutablePropTypes.list.isRequired,
isModalOpen: PropTypes.bool.isRequired,
singleColumn: PropTypes.bool, singleColumn: PropTypes.bool,
children: PropTypes.node, children: PropTypes.node,
}; };
@@ -144,7 +145,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
} }
render () { render () {
const { columns, children, singleColumn } = this.props; const { columns, children, singleColumn, isModalOpen } = this.props;
const { shouldAnimate } = this.state; const { shouldAnimate } = this.state;
const columnIndex = getIndex(this.context.router.history.location.pathname); const columnIndex = getIndex(this.context.router.history.location.pathname);
@@ -159,7 +160,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
} }
return ( return (
<div className='columns-area' ref={this.setRef}> <div className={`columns-area ${ isModalOpen ? 'unscrollable' : '' }`} ref={this.setRef}>
{columns.map(column => { {columns.map(column => {
const params = column.get('params', null) === null ? null : column.get('params').toJS(); const params = column.get('params', null) === null ? null : column.get('params').toJS();

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage, injectIntl } from 'react-intl'; import { FormattedMessage, injectIntl } from 'react-intl';
import axios from 'axios'; import api from '../../../api';
@injectIntl @injectIntl
export default class EmbedModal extends ImmutablePureComponent { export default class EmbedModal extends ImmutablePureComponent {
@@ -23,7 +23,7 @@ export default class EmbedModal extends ImmutablePureComponent {
this.setState({ loading: true }); this.setState({ loading: true });
axios.post('/api/web/embed', { url }).then(res => { api().post('/api/web/embed', { url }).then(res => {
this.setState({ loading: false, oembed: res.data }); this.setState({ loading: false, oembed: res.data });
const iframeDocument = this.iframe.contentWindow.document; const iframeDocument = this.iframe.contentWindow.document;

View File

@@ -113,13 +113,11 @@ export default class ModalRoot extends React.PureComponent {
<div style={{ pointerEvents: visible ? 'auto' : 'none' }}> <div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
<div role='presentation' className='modal-root__overlay' onClick={onClose} /> <div role='presentation' className='modal-root__overlay' onClick={onClose} />
<div role='dialog' className='modal-root__container'> <div role='dialog' className='modal-root__container'>
{ {visible && (
visible ? <BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
(<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}> {(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />}
{(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />} </BundleContainer>
</BundleContainer>) : )}
null
}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -24,14 +24,23 @@ const messages = defineMessages({
const PageOne = ({ acct, domain }) => ( const PageOne = ({ acct, domain }) => (
<div className='onboarding-modal__page onboarding-modal__page-one'> <div className='onboarding-modal__page onboarding-modal__page-one'>
<div style={{ flex: '0 0 auto' }}> <div className='onboarding-modal__page-one__lead'>
<div className='onboarding-modal__page-one__elephant-friend' />
</div>
<div>
<h1><FormattedMessage id='onboarding.page_one.welcome' defaultMessage='Welcome to Mastodon!' /></h1> <h1><FormattedMessage id='onboarding.page_one.welcome' defaultMessage='Welcome to Mastodon!' /></h1>
<p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' /></p> <p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' /></p>
<p><FormattedMessage id='onboarding.page_one.handle' defaultMessage='You are on {domain}, so your full handle is {handle}' values={{ domain, handle: <strong>@{acct}@{domain}</strong> }} /></p> </div>
<div className='onboarding-modal__page-one__extra'>
<div className='display-case'>
<div className='display-case__label'>
<FormattedMessage id='onboarding.page_one.full_handle' defaultMessage='Your full handle' />
</div>
<div className='display-case__case'>
@{acct}@{domain}
</div>
</div>
<p><FormattedMessage id='onboarding.page_one.handle_hint' defaultMessage='This is what you would tell your friends to search for.' /></p>
</div> </div>
</div> </div>
); );
@@ -46,22 +55,23 @@ const PageTwo = ({ myAccount }) => (
<div className='figure non-interactive'> <div className='figure non-interactive'>
<div className='pseudo-drawer'> <div className='pseudo-drawer'>
<NavigationBar account={myAccount} /> <NavigationBar account={myAccount} />
<ComposeForm
text='Awoo! #introductions'
suggestions={ImmutableList()}
mentionedDomains={[]}
spoiler={false}
onChange={noop}
onSubmit={noop}
onPaste={noop}
onPickEmoji={noop}
onChangeSpoilerText={noop}
onClearSuggestions={noop}
onFetchSuggestions={noop}
onSuggestionSelected={noop}
showSearch
/>
</div> </div>
<ComposeForm
text='Awoo! #introductions'
suggestions={ImmutableList()}
mentionedDomains={[]}
spoiler={false}
onChange={noop}
onSubmit={noop}
onPaste={noop}
onPickEmoji={noop}
onChangeSpoilerText={noop}
onClearSuggestions={noop}
onFetchSuggestions={noop}
onSuggestionSelected={noop}
showSearch
/>
</div> </div>
<p><FormattedMessage id='onboarding.page_two.compose' defaultMessage='Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.' /></p> <p><FormattedMessage id='onboarding.page_two.compose' defaultMessage='Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.' /></p>
@@ -251,18 +261,12 @@ export default class OnboardingModal extends React.PureComponent {
const hasMore = currentIndex < pages.length - 1; const hasMore = currentIndex < pages.length - 1;
const nextOrDoneBtn = hasMore ? ( const nextOrDoneBtn = hasMore ? (
<button <button onClick={this.handleNext} className='onboarding-modal__nav onboarding-modal__next shake-bottom'>
onClick={this.handleNext} <FormattedMessage id='onboarding.next' defaultMessage='Next' /> <i className='fa fa-fw fa-chevron-right' />
className='onboarding-modal__nav onboarding-modal__next'
>
<FormattedMessage id='onboarding.next' defaultMessage='Next' />
</button> </button>
) : ( ) : (
<button <button onClick={this.handleClose} className='onboarding-modal__nav onboarding-modal__done shake-bottom'>
onClick={this.handleClose} <FormattedMessage id='onboarding.done' defaultMessage='Done' /> <i className='fa fa-fw fa-check' />
className='onboarding-modal__nav onboarding-modal__done'
>
<FormattedMessage id='onboarding.done' defaultMessage='Done' />
</button> </button>
); );
@@ -270,9 +274,10 @@ export default class OnboardingModal extends React.PureComponent {
<div className='modal-root__modal onboarding-modal'> <div className='modal-root__modal onboarding-modal'>
<ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className='onboarding-modal__pager'> <ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className='onboarding-modal__pager'>
{pages.map((page, i) => { {pages.map((page, i) => {
const className = classNames('onboarding-modal__page__wrapper', { const className = classNames('onboarding-modal__page__wrapper', `onboarding-modal__page__wrapper-${i}`, {
'onboarding-modal__page__wrapper--active': i === currentIndex, 'onboarding-modal__page__wrapper--active': i === currentIndex,
}); });
return ( return (
<div key={i} className={className}>{page}</div> <div key={i} className={className}>{page}</div>
); );
@@ -294,6 +299,7 @@ export default class OnboardingModal extends React.PureComponent {
const className = classNames('onboarding-modal__dot', { const className = classNames('onboarding-modal__dot', {
active: i === currentIndex, active: i === currentIndex,
}); });
return ( return (
<div <div
key={`dot-${i}`} key={`dot-${i}`}

View File

@@ -37,14 +37,14 @@ export default class UploadArea extends React.PureComponent {
return ( return (
<Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}> <Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}>
{({ backgroundOpacity, backgroundScale }) => {({ backgroundOpacity, backgroundScale }) => (
<div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}> <div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}>
<div className='upload-area__drop'> <div className='upload-area__drop'>
<div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} /> <div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} />
<div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div> <div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div>
</div> </div>
</div> </div>
} )}
</Motion> </Motion>
); );
} }

View File

@@ -3,6 +3,7 @@ import ColumnsArea from '../components/columns_area';
const mapStateToProps = state => ({ const mapStateToProps = state => ({
columns: state.getIn(['settings', 'columns']), columns: state.getIn(['settings', 'columns']),
isModalOpen: !!state.get('modal').modalType,
}); });
export default connect(mapStateToProps, null, null, { withRef: true })(ColumnsArea); export default connect(mapStateToProps, null, null, { withRef: true })(ColumnsArea);

View File

@@ -47,6 +47,7 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, { timelineId }) => ({ const mapStateToProps = (state, { timelineId }) => ({
statusIds: getStatusIds(state, { type: timelineId }), statusIds: getStatusIds(state, { type: timelineId }),
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true), isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
hasMore: !!state.getIn(['timelines', timelineId, 'next']), hasMore: !!state.getIn(['timelines', timelineId, 'next']),
}); });

View File

@@ -67,7 +67,7 @@
"confirmations.delete_list.confirm": "Delete", "confirmations.delete_list.confirm": "Delete",
"confirmations.delete_list.message": "هل تود حقا حذف هذه القائمة ؟", "confirmations.delete_list.message": "هل تود حقا حذف هذه القائمة ؟",
"confirmations.domain_block.confirm": "إخفاء إسم النطاق كاملا", "confirmations.domain_block.confirm": "إخفاء إسم النطاق كاملا",
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.domain_block.message": "متأكد من أنك تود حظر إسم النطاق {domain} بالكامل ؟ في غالب الأحيان يُستَحسَن كتم أو حظر بعض الحسابات بدلا من حظر نطاق بالكامل.",
"confirmations.mute.confirm": "أكتم", "confirmations.mute.confirm": "أكتم",
"confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟", "confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟",
"confirmations.unfollow.confirm": "إلغاء المتابعة", "confirmations.unfollow.confirm": "إلغاء المتابعة",
@@ -92,7 +92,7 @@
"empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.", "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
"empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.", "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
"empty_column.home.public_timeline": "الخيط العام", "empty_column.home.public_timeline": "الخيط العام",
"empty_column.list": "هذه القائمة فارغة.", "empty_column.list": "هذه القائمة فارغة مؤقتا و لكن سوف تمتلئ تدريجيا عندما يبدأ الأعضاء المُنتَمين إليها بنشر تبويقات.",
"empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.", "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
"empty_column.public": "لا يوجد أي شيء هنا ! قم بنشر شيء ما للعامة، أو إتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام", "empty_column.public": "لا يوجد أي شيء هنا ! قم بنشر شيء ما للعامة، أو إتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام",
"follow_request.authorize": "ترخيص", "follow_request.authorize": "ترخيص",
@@ -123,7 +123,7 @@
"keyboard_shortcuts.reply": "للردّ", "keyboard_shortcuts.reply": "للردّ",
"keyboard_shortcuts.search": "للتركيز على البحث", "keyboard_shortcuts.search": "للتركيز على البحث",
"keyboard_shortcuts.toot": "لتحرير تبويق جديد", "keyboard_shortcuts.toot": "لتحرير تبويق جديد",
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.unfocus": "لإلغاء التركيز على حقل النص أو نافذة البحث",
"keyboard_shortcuts.up": "للإنتقال إلى أعلى القائمة", "keyboard_shortcuts.up": "للإنتقال إلى أعلى القائمة",
"lightbox.close": "إغلاق", "lightbox.close": "إغلاق",
"lightbox.next": "التالي", "lightbox.next": "التالي",

View File

@@ -92,7 +92,7 @@
"empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.", "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.",
"empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.", "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.",
"empty_column.home.public_timeline": "la línia de temps pública", "empty_column.home.public_timeline": "la línia de temps pública",
"empty_column.list": "Encara no hi ha res en aquesta llista.", "empty_column.list": "Encara no hi ha res en aquesta llista. Quan els membres d'aquesta llista publiquin nous estats, apareixeran aquí.",
"empty_column.notifications": "Encara no tens notificacions. Interactua amb altres per iniciar la conversa.", "empty_column.notifications": "Encara no tens notificacions. Interactua amb altres per iniciar la conversa.",
"empty_column.public": "No hi ha res aquí! Escriu alguna cosa públicament o segueix manualment usuaris d'altres instàncies per omplir-ho", "empty_column.public": "No hi ha res aquí! Escriu alguna cosa públicament o segueix manualment usuaris d'altres instàncies per omplir-ho",
"follow_request.authorize": "Autoritzar", "follow_request.authorize": "Autoritzar",

View File

@@ -174,7 +174,8 @@
"onboarding.page_four.home": "The home timeline shows posts from people you follow.", "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
"onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.", "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
"onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
"onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}", "onboarding.page_one.full_handle": "Your full handle",
"onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
"onboarding.page_one.welcome": "Welcome to Mastodon!", "onboarding.page_one.welcome": "Welcome to Mastodon!",
"onboarding.page_six.admin": "Your instance's admin is {admin}.", "onboarding.page_six.admin": "Your instance's admin is {admin}.",
"onboarding.page_six.almost_done": "Almost done...", "onboarding.page_six.almost_done": "Almost done...",

View File

@@ -7,22 +7,22 @@
"account.followers": "پیگیران", "account.followers": "پیگیران",
"account.follows": "پی می‌گیرد", "account.follows": "پی می‌گیرد",
"account.follows_you": "پیگیر شماست", "account.follows_you": "پیگیر شماست",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "پنهان کردن بازبوق‌های @{name}",
"account.media": "رسانه", "account.media": "رسانه",
"account.mention": "نام‌بردن از @{name}", "account.mention": "نام‌بردن از @{name}",
"account.moved_to": "{name} has moved to:", "account.moved_to": "{name} منتقل شده است به:",
"account.mute": "بی‌صدا کردن @{name}", "account.mute": "بی‌صدا کردن @{name}",
"account.mute_notifications": "Mute notifications from @{name}", "account.mute_notifications": "بی‌صداکردن اعلان‌ها از طرف @{name}",
"account.posts": "نوشته‌ها", "account.posts": "نوشته‌ها",
"account.report": "گزارش @{name}", "account.report": "گزارش @{name}",
"account.requested": "در انتظار پذیرش", "account.requested": "در انتظار پذیرش",
"account.share": "هم‌رسانی نمایهٔ @{name}", "account.share": "هم‌رسانی نمایهٔ @{name}",
"account.show_reblogs": "Show boosts from @{name}", "account.show_reblogs": "نشان‌دادن بازبوق‌های @{name}",
"account.unblock": "رفع انسداد @{name}", "account.unblock": "رفع انسداد @{name}",
"account.unblock_domain": "رفع پنهان‌سازی از {domain}", "account.unblock_domain": "رفع پنهان‌سازی از {domain}",
"account.unfollow": "پایان پیگیری", "account.unfollow": "پایان پیگیری",
"account.unmute": "باصدا کردن @{name}", "account.unmute": "باصدا کردن @{name}",
"account.unmute_notifications": "Unmute notifications from @{name}", "account.unmute_notifications": "باصداکردن اعلان‌ها از طرف @{name}",
"account.view_full_profile": "نمایش نمایهٔ کامل", "account.view_full_profile": "نمایش نمایهٔ کامل",
"boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید", "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
"bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.", "bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.",
@@ -36,7 +36,7 @@
"column.favourites": "پسندیده‌ها", "column.favourites": "پسندیده‌ها",
"column.follow_requests": "درخواست‌های پیگیری", "column.follow_requests": "درخواست‌های پیگیری",
"column.home": "خانه", "column.home": "خانه",
"column.lists": "Lists", "column.lists": "فهرست‌ها",
"column.mutes": "کاربران بی‌صداشده", "column.mutes": "کاربران بی‌صداشده",
"column.notifications": "اعلان‌ها", "column.notifications": "اعلان‌ها",
"column.pins": "نوشته‌های ثابت", "column.pins": "نوشته‌های ثابت",
@@ -65,7 +65,7 @@
"confirmations.delete.confirm": "پاک کن", "confirmations.delete.confirm": "پاک کن",
"confirmations.delete.message": "آیا واقعاً می‌خواهید این نوشته را پاک کنید؟", "confirmations.delete.message": "آیا واقعاً می‌خواهید این نوشته را پاک کنید؟",
"confirmations.delete_list.confirm": "Delete", "confirmations.delete_list.confirm": "Delete",
"confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "confirmations.delete_list.message": "آیا واقعاً می‌خواهید این فهرست را برای همیشه پاک کنید؟",
"confirmations.domain_block.confirm": "پنهان‌سازی کل دامین", "confirmations.domain_block.confirm": "پنهان‌سازی کل دامین",
"confirmations.domain_block.message": "آیا جدی جدی می‌خواهید کل دامین {domain} را مسدود کنید؟ بیشتر وقت‌ها مسدودکردن یا بی‌صداکردن چند حساب کاربری خاص کافی است و توصیه می‌شود.", "confirmations.domain_block.message": "آیا جدی جدی می‌خواهید کل دامین {domain} را مسدود کنید؟ بیشتر وقت‌ها مسدودکردن یا بی‌صداکردن چند حساب کاربری خاص کافی است و توصیه می‌شود.",
"confirmations.mute.confirm": "بی‌صدا کن", "confirmations.mute.confirm": "بی‌صدا کن",
@@ -92,7 +92,7 @@
"empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.", "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.",
"empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.", "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.",
"empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا", "empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا",
"empty_column.list": "There is nothing in this list yet.", "empty_column.list": "در این فهرست هنوز چیزی نیست. وقتی اعضای این فهرست چیزی بنویسند، این‌جا ظاهر خواهد شد.",
"empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشته‌های دیگران واکنش نشان دهید تا گفتگو آغاز شود.", "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشته‌های دیگران واکنش نشان دهید تا گفتگو آغاز شود.",
"empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا این‌جا پر شود", "empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا این‌جا پر شود",
"follow_request.authorize": "اجازه دهید", "follow_request.authorize": "اجازه دهید",
@@ -108,46 +108,46 @@
"home.column_settings.show_reblogs": "نمایش بازبوق‌ها", "home.column_settings.show_reblogs": "نمایش بازبوق‌ها",
"home.column_settings.show_replies": "نمایش پاسخ‌ها", "home.column_settings.show_replies": "نمایش پاسخ‌ها",
"home.settings": "تنظیمات ستون", "home.settings": "تنظیمات ستون",
"keyboard_shortcuts.back": "to navigate back", "keyboard_shortcuts.back": "برای بازگشت",
"keyboard_shortcuts.boost": "to boost", "keyboard_shortcuts.boost": "برای بازبوقیدن",
"keyboard_shortcuts.column": "to focus a status in one of the columns", "keyboard_shortcuts.column": "برای برجسته‌کردن یک نوشته در یکی از ستون‌ها",
"keyboard_shortcuts.compose": "to focus the compose textarea", "keyboard_shortcuts.compose": "برای فعال‌کردن کادر نوشتهٔ تازه",
"keyboard_shortcuts.description": "Description", "keyboard_shortcuts.description": "Description",
"keyboard_shortcuts.down": "to move down in the list", "keyboard_shortcuts.down": "برای پایین‌رفتن در فهرست",
"keyboard_shortcuts.enter": "to open status", "keyboard_shortcuts.enter": "to open status",
"keyboard_shortcuts.favourite": "to favourite", "keyboard_shortcuts.favourite": "برای پسندیدن",
"keyboard_shortcuts.heading": "Keyboard Shortcuts", "keyboard_shortcuts.heading": "Keyboard Shortcuts",
"keyboard_shortcuts.hotkey": "Hotkey", "keyboard_shortcuts.hotkey": "میان‌بر",
"keyboard_shortcuts.legend": "to display this legend", "keyboard_shortcuts.legend": "برای نمایش این راهنما",
"keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.mention": "برای نام‌بردن از نویسنده",
"keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.reply": "برای پاسخ‌دادن",
"keyboard_shortcuts.search": "to focus search", "keyboard_shortcuts.search": "برای فعال‌کردن جستجو",
"keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.toot": "برای آغاز یک بوق تازه",
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.unfocus": "برای برداشتن توجه از نوشتن/جستجو",
"keyboard_shortcuts.up": "to move up in the list", "keyboard_shortcuts.up": "برای بالا رفتن در فهرست",
"lightbox.close": "بستن", "lightbox.close": "بستن",
"lightbox.next": "بعدی", "lightbox.next": "بعدی",
"lightbox.previous": "قبلی", "lightbox.previous": "قبلی",
"lists.account.add": "Add to list", "lists.account.add": "افزودن به فهرست",
"lists.account.remove": "Remove from list", "lists.account.remove": "پاک‌کردن از فهرست",
"lists.delete": "Delete list", "lists.delete": "حذف فهرست",
"lists.edit": "Edit list", "lists.edit": "ویرایش فهرست",
"lists.new.create": "Add list", "lists.new.create": "افزودن فهرست",
"lists.new.title_placeholder": "New list title", "lists.new.title_placeholder": "نام فهرست تازه",
"lists.search": "Search among people you follow", "lists.search": "بین کسانی که پی می‌گیرید بگردید",
"lists.subheading": "Your lists", "lists.subheading": "فهرست‌های شما",
"loading_indicator.label": "بارگیری...", "loading_indicator.label": "بارگیری...",
"media_gallery.toggle_visible": "تغییر پیدایی", "media_gallery.toggle_visible": "تغییر پیدایی",
"missing_indicator.label": "پیدا نشد", "missing_indicator.label": "پیدا نشد",
"mute_modal.hide_notifications": "Hide notifications from this user?", "mute_modal.hide_notifications": "اعلان‌های این کاربر پنهان شود؟",
"navigation_bar.blocks": "کاربران مسدودشده", "navigation_bar.blocks": "کاربران مسدودشده",
"navigation_bar.community_timeline": "نوشته‌های محلی", "navigation_bar.community_timeline": "نوشته‌های محلی",
"navigation_bar.edit_profile": "ویرایش نمایه", "navigation_bar.edit_profile": "ویرایش نمایه",
"navigation_bar.favourites": "پسندیده‌ها", "navigation_bar.favourites": "پسندیده‌ها",
"navigation_bar.follow_requests": "درخواست‌های پیگیری", "navigation_bar.follow_requests": "درخواست‌های پیگیری",
"navigation_bar.info": "اطلاعات تکمیلی", "navigation_bar.info": "اطلاعات تکمیلی",
"navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", "navigation_bar.keyboard_shortcuts": "میان‌برهای صفحه‌کلید",
"navigation_bar.lists": "Lists", "navigation_bar.lists": "فهرست‌ها",
"navigation_bar.logout": "خروج", "navigation_bar.logout": "خروج",
"navigation_bar.mutes": "کاربران بی‌صداشده", "navigation_bar.mutes": "کاربران بی‌صداشده",
"navigation_bar.pins": "نوشته‌های ثابت", "navigation_bar.pins": "نوشته‌های ثابت",
@@ -174,7 +174,7 @@
"onboarding.page_four.home": "ستون «خانه» نوشته‌های کسانی را نشان می‌دهد که شما پی می‌گیرید.", "onboarding.page_four.home": "ستون «خانه» نوشته‌های کسانی را نشان می‌دهد که شما پی می‌گیرید.",
"onboarding.page_four.notifications": "ستون «اعلان‌ها» ارتباط‌های شما با دیگران را نشان می‌دهد.", "onboarding.page_four.notifications": "ستون «اعلان‌ها» ارتباط‌های شما با دیگران را نشان می‌دهد.",
"onboarding.page_one.federation": "ماستدون شبکه‌ای از سرورهای مستقل است که با پیوستن به یکدیگر یک شبکهٔ اجتماعی بزرگ را تشکیل می‌دهند.", "onboarding.page_one.federation": "ماستدون شبکه‌ای از سرورهای مستقل است که با پیوستن به یکدیگر یک شبکهٔ اجتماعی بزرگ را تشکیل می‌دهند.",
"onboarding.page_one.handle": "شما روی سرور {domain} هستید، بنابراین شناسهٔ کامل شما {handle} است.", "onboarding.page_one.handle": "شما روی سرور {domain} هستید، بنابراین شناسهٔ کامل شما {handle} است",
"onboarding.page_one.welcome": "به ماستدون خوش آمدید!", "onboarding.page_one.welcome": "به ماستدون خوش آمدید!",
"onboarding.page_six.admin": "نشانی مسئول سرور شما {admin} است.", "onboarding.page_six.admin": "نشانی مسئول سرور شما {admin} است.",
"onboarding.page_six.almost_done": "الان تقریباً آماده‌اید...", "onboarding.page_six.almost_done": "الان تقریباً آماده‌اید...",
@@ -199,7 +199,7 @@
"privacy.unlisted.short": "فهرست‌نشده", "privacy.unlisted.short": "فهرست‌نشده",
"relative_time.days": "{number}d", "relative_time.days": "{number}d",
"relative_time.hours": "{number}h", "relative_time.hours": "{number}h",
"relative_time.just_now": "now", "relative_time.just_now": "الان",
"relative_time.minutes": "{number}m", "relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s", "relative_time.seconds": "{number}s",
"reply_indicator.cancel": "لغو", "reply_indicator.cancel": "لغو",
@@ -222,7 +222,7 @@
"status.load_more": "بیشتر نشان بده", "status.load_more": "بیشتر نشان بده",
"status.media_hidden": "تصویر پنهان شده", "status.media_hidden": "تصویر پنهان شده",
"status.mention": "نام‌بردن از @{name}", "status.mention": "نام‌بردن از @{name}",
"status.more": "More", "status.more": "بیشتر",
"status.mute": "Mute @{name}", "status.mute": "Mute @{name}",
"status.mute_conversation": "بی‌صداکردن گفتگو", "status.mute_conversation": "بی‌صداکردن گفتگو",
"status.open": "این نوشته را باز کن", "status.open": "این نوشته را باز کن",
@@ -244,7 +244,7 @@
"tabs_bar.home": "خانه", "tabs_bar.home": "خانه",
"tabs_bar.local_timeline": "محلی", "tabs_bar.local_timeline": "محلی",
"tabs_bar.notifications": "اعلان‌ها", "tabs_bar.notifications": "اعلان‌ها",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.",
"upload_area.title": "برای بارگذاری به این‌جا بکشید", "upload_area.title": "برای بارگذاری به این‌جا بکشید",
"upload_button.label": "افزودن تصویر", "upload_button.label": "افزودن تصویر",
"upload_form.description": "نوشتهٔ توضیحی برای کم‌بینایان و نابینایان", "upload_form.description": "نوشتهٔ توضیحی برای کم‌بینایان و نابینایان",

View File

@@ -92,7 +92,7 @@
"empty_column.hashtag": "Aínda non hai nada con esta etiqueta.", "empty_column.hashtag": "Aínda non hai nada con esta etiqueta.",
"empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.", "empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.",
"empty_column.home.public_timeline": "a liña temporal pública", "empty_column.home.public_timeline": "a liña temporal pública",
"empty_column.list": "Aínda non hai nada en esta lista.", "empty_column.list": "Aínda non hai nada en esta lista. Cando as usuarias incluídas na lista publiquen mensaxes, aparecerán aquí.",
"empty_column.notifications": "Aínda non ten notificacións. Interactúe con outras para iniciar unha conversa.", "empty_column.notifications": "Aínda non ten notificacións. Interactúe con outras para iniciar unha conversa.",
"empty_column.public": "Nada por aquí! Escriba algo de xeito público, ou siga manualmente usuarias de outras instancias para ir enchéndoa", "empty_column.public": "Nada por aquí! Escriba algo de xeito público, ou siga manualmente usuarias de outras instancias para ir enchéndoa",
"follow_request.authorize": "Autorizar", "follow_request.authorize": "Autorizar",
@@ -109,7 +109,7 @@
"home.column_settings.show_replies": "Mostrar respostas", "home.column_settings.show_replies": "Mostrar respostas",
"home.settings": "Axustes da columna", "home.settings": "Axustes da columna",
"keyboard_shortcuts.back": "voltar atrás", "keyboard_shortcuts.back": "voltar atrás",
"keyboard_shortcuts.boost": "repetir", "keyboard_shortcuts.boost": "promover",
"keyboard_shortcuts.column": "destacar un estado en unha das columnas", "keyboard_shortcuts.column": "destacar un estado en unha das columnas",
"keyboard_shortcuts.compose": "Foco no área de escritura", "keyboard_shortcuts.compose": "Foco no área de escritura",
"keyboard_shortcuts.description": "Descrición", "keyboard_shortcuts.description": "Descrición",
@@ -227,8 +227,8 @@
"status.mute_conversation": "Acalar conversa", "status.mute_conversation": "Acalar conversa",
"status.open": "Expandir este estado", "status.open": "Expandir este estado",
"status.pin": "Fixar no perfil", "status.pin": "Fixar no perfil",
"status.reblog": "Promocionar", "status.reblog": "Promover",
"status.reblogged_by": "{name} promocionado", "status.reblogged_by": "{name} promoveu",
"status.reply": "Resposta", "status.reply": "Resposta",
"status.replyAll": "Resposta a conversa", "status.replyAll": "Resposta a conversa",
"status.report": "Informar @{name}", "status.report": "Informar @{name}",

View File

@@ -25,11 +25,11 @@
"account.unmute_notifications": "@{name}의 알림 뮤트 해제", "account.unmute_notifications": "@{name}의 알림 뮤트 해제",
"account.view_full_profile": "전체 프로필 보기", "account.view_full_profile": "전체 프로필 보기",
"boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.", "boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.",
"bundle_column_error.retry": "다시 시도", "bundle_column_error.retry": "다시 시도",
"bundle_column_error.title": "네트워크 에러", "bundle_column_error.title": "네트워크 에러",
"bundle_modal_error.close": "닫기", "bundle_modal_error.close": "닫기",
"bundle_modal_error.message": "Something went wrong while loading this component.", "bundle_modal_error.message": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.",
"bundle_modal_error.retry": "다시 시도", "bundle_modal_error.retry": "다시 시도",
"column.blocks": "차단 중인 사용자", "column.blocks": "차단 중인 사용자",
"column.community": "로컬 타임라인", "column.community": "로컬 타임라인",
@@ -50,7 +50,7 @@
"column_header.unpin": "고정 해제", "column_header.unpin": "고정 해제",
"column_subheading.navigation": "내비게이션", "column_subheading.navigation": "내비게이션",
"column_subheading.settings": "설정", "column_subheading.settings": "설정",
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.hashtag_warning": "이 툿은 어떤 해시태그로도 검색 되지 않습니다. 전체공개로 게시 된 툿만이 해시태그로 검색 될 수 있습니다.",
"compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.", "compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.",
"compose_form.lock_disclaimer.lock": "비공개", "compose_form.lock_disclaimer.lock": "비공개",
"compose_form.placeholder": "지금 무엇을 하고 있나요?", "compose_form.placeholder": "지금 무엇을 하고 있나요?",
@@ -135,7 +135,7 @@
"lists.new.create": "리스트 추가", "lists.new.create": "리스트 추가",
"lists.new.title_placeholder": "새 리스트의 이름", "lists.new.title_placeholder": "새 리스트의 이름",
"lists.search": "팔로우 중인 사람들 중에서 찾기", "lists.search": "팔로우 중인 사람들 중에서 찾기",
"lists.subheading": "Your lists", "lists.subheading": "당신의 리스트",
"loading_indicator.label": "불러오는 중...", "loading_indicator.label": "불러오는 중...",
"media_gallery.toggle_visible": "표시 전환", "media_gallery.toggle_visible": "표시 전환",
"missing_indicator.label": "찾을 수 없습니다", "missing_indicator.label": "찾을 수 없습니다",
@@ -178,7 +178,7 @@
"onboarding.page_one.welcome": "Mastodon에 어서 오세요!", "onboarding.page_one.welcome": "Mastodon에 어서 오세요!",
"onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.", "onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.",
"onboarding.page_six.almost_done": "이상입니다.", "onboarding.page_six.almost_done": "이상입니다.",
"onboarding.page_six.appetoot": "Bon Appetoot!", "onboarding.page_six.appetoot": "본 아페툿!",
"onboarding.page_six.apps_available": "iOS、Android 또는 다른 플랫폼에서 사용할 수 있는 {apps}이 있습니다.", "onboarding.page_six.apps_available": "iOS、Android 또는 다른 플랫폼에서 사용할 수 있는 {apps}이 있습니다.",
"onboarding.page_six.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.", "onboarding.page_six.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.",
"onboarding.page_six.guidelines": "커뮤니티 가이드라인", "onboarding.page_six.guidelines": "커뮤니티 가이드라인",
@@ -213,7 +213,7 @@
"search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 유저 이름 그리고 해시태그를 표시합니다", "search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 유저 이름 그리고 해시태그를 표시합니다",
"search_popout.tips.user": "유저", "search_popout.tips.user": "유저",
"search_results.total": "{count, number}건의 결과", "search_results.total": "{count, number}건의 결과",
"standalone.public_title": "A look inside...", "standalone.public_title": "지금 이런 이야기를 하고 있습니다…",
"status.block": "@{name} 차단", "status.block": "@{name} 차단",
"status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다", "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
"status.delete": "삭제", "status.delete": "삭제",
@@ -247,7 +247,7 @@
"ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.", "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
"upload_area.title": "드래그 & 드롭으로 업로드", "upload_area.title": "드래그 & 드롭으로 업로드",
"upload_button.label": "미디어 추가", "upload_button.label": "미디어 추가",
"upload_form.description": "Describe for the visually impaired", "upload_form.description": "시각장애인을 위한 설명",
"upload_form.undo": "재시도", "upload_form.undo": "재시도",
"upload_progress.label": "업로드 중...", "upload_progress.label": "업로드 중...",
"video.close": "동영상 닫기", "video.close": "동영상 닫기",

View File

@@ -100,10 +100,10 @@
"getting_started.appsshort": "Apps", "getting_started.appsshort": "Apps",
"getting_started.faq": "FAQ", "getting_started.faq": "FAQ",
"getting_started.heading": "Beginnen", "getting_started.heading": "Beginnen",
"getting_started.open_source_notice": "Mastodon is open-sourcesoftware. Je kunt bijdragen of problemen melden op GitHub via {github}.", "getting_started.open_source_notice": "Mastodon is vrije software. Je kunt bijdragen of problemen melden op GitHub via {github}.",
"getting_started.userguide": "Gebruikersgids", "getting_started.userguide": "Gebruikersgids",
"home.column_settings.advanced": "Geavanceerd", "home.column_settings.advanced": "Geavanceerd",
"home.column_settings.basic": "Basis", "home.column_settings.basic": "Algemeen",
"home.column_settings.filter_regex": "Wegfilteren met reguliere expressies", "home.column_settings.filter_regex": "Wegfilteren met reguliere expressies",
"home.column_settings.show_reblogs": "Boosts tonen", "home.column_settings.show_reblogs": "Boosts tonen",
"home.column_settings.show_replies": "Reacties tonen", "home.column_settings.show_replies": "Reacties tonen",
@@ -146,7 +146,7 @@
"navigation_bar.favourites": "Favorieten", "navigation_bar.favourites": "Favorieten",
"navigation_bar.follow_requests": "Volgverzoeken", "navigation_bar.follow_requests": "Volgverzoeken",
"navigation_bar.info": "Uitgebreide informatie", "navigation_bar.info": "Uitgebreide informatie",
"navigation_bar.keyboard_shortcuts": "Toetsenbord sneltoetsen", "navigation_bar.keyboard_shortcuts": "Sneltoetsen",
"navigation_bar.lists": "Lijsten", "navigation_bar.lists": "Lijsten",
"navigation_bar.logout": "Afmelden", "navigation_bar.logout": "Afmelden",
"navigation_bar.mutes": "Genegeerde gebruikers", "navigation_bar.mutes": "Genegeerde gebruikers",
@@ -180,7 +180,7 @@
"onboarding.page_six.almost_done": "Bijna klaar...", "onboarding.page_six.almost_done": "Bijna klaar...",
"onboarding.page_six.appetoot": "Veel succes!", "onboarding.page_six.appetoot": "Veel succes!",
"onboarding.page_six.apps_available": "Er zijn {apps} beschikbaar voor iOS, Android en andere platformen.", "onboarding.page_six.apps_available": "Er zijn {apps} beschikbaar voor iOS, Android en andere platformen.",
"onboarding.page_six.github": "Mastodon kost niets, en is open-source- en vrije software. Je kan bugs melden, nieuwe mogelijkheden aanvragen en als ontwikkelaar meewerken op {github}.", "onboarding.page_six.github": "Mastodon kost niets en is vrije software. Je kan bugs melden, nieuwe mogelijkheden aanvragen en als ontwikkelaar meewerken op {github}.",
"onboarding.page_six.guidelines": "communityrichtlijnen", "onboarding.page_six.guidelines": "communityrichtlijnen",
"onboarding.page_six.read_guidelines": "Vergeet niet de {guidelines} van {domain} te lezen!", "onboarding.page_six.read_guidelines": "Vergeet niet de {guidelines} van {domain} te lezen!",
"onboarding.page_six.various_app": "mobiele apps", "onboarding.page_six.various_app": "mobiele apps",

View File

@@ -92,7 +92,7 @@
"empty_column.hashtag": "Nie ma wpisów oznaczonych tym hashtagiem. Możesz napisać pierwszy!", "empty_column.hashtag": "Nie ma wpisów oznaczonych tym hashtagiem. Możesz napisać pierwszy!",
"empty_column.home": "Nie śledzisz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.", "empty_column.home": "Nie śledzisz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.",
"empty_column.home.public_timeline": "publiczna oś czasu", "empty_column.home.public_timeline": "publiczna oś czasu",
"empty_column.list": "Nie ma nic na tej liście.", "empty_column.list": "Nie ma nic na tej liście. Kiedy członkowie listy dodadzą nowe wpisy, pojawia się one tutaj.",
"empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.", "empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.",
"empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.", "empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.",
"follow_request.authorize": "Autoryzuj", "follow_request.authorize": "Autoryzuj",

View File

@@ -50,7 +50,7 @@
"column_header.unpin": "Desafixar", "column_header.unpin": "Desafixar",
"column_subheading.navigation": "Navegação", "column_subheading.navigation": "Navegação",
"column_subheading.settings": "Configurações", "column_subheading.settings": "Configurações",
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.hashtag_warning": "Esse toot não será listado em nenhuma hashtag por ser não listado. Somente toots públicos podem ser pesquisados por hashtag.",
"compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.", "compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.",
"compose_form.lock_disclaimer.lock": "trancada", "compose_form.lock_disclaimer.lock": "trancada",
"compose_form.placeholder": "No que você está pensando?", "compose_form.placeholder": "No que você está pensando?",
@@ -92,7 +92,7 @@
"empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.", "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.",
"empty_column.home": "Você ainda não segue usuário algo. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.", "empty_column.home": "Você ainda não segue usuário algo. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.",
"empty_column.home.public_timeline": "global", "empty_column.home.public_timeline": "global",
"empty_column.list": "Ainda não há nada nesta lista.", "empty_column.list": "Ainda não há nada nesta lista. Quando membros dessa lista fizerem novas postagens, elas aparecerão aqui.",
"empty_column.notifications": "Você ainda não possui notificações. Interaja com outros usuários para começar a conversar.", "empty_column.notifications": "Você ainda não possui notificações. Interaja com outros usuários para começar a conversar.",
"empty_column.public": "Não há nada aqui! Escreva algo publicamente ou siga manualmente usuários de outras instâncias", "empty_column.public": "Não há nada aqui! Escreva algo publicamente ou siga manualmente usuários de outras instâncias",
"follow_request.authorize": "Autorizar", "follow_request.authorize": "Autorizar",
@@ -223,7 +223,7 @@
"status.media_hidden": "Mídia escondida", "status.media_hidden": "Mídia escondida",
"status.mention": "Mencionar @{name}", "status.mention": "Mencionar @{name}",
"status.more": "Mais", "status.more": "Mais",
"status.mute": "Mute @{name}", "status.mute": "Silenciar @{name}",
"status.mute_conversation": "Silenciar conversa", "status.mute_conversation": "Silenciar conversa",
"status.open": "Expandir", "status.open": "Expandir",
"status.pin": "Fixar no perfil", "status.pin": "Fixar no perfil",

View File

@@ -35,11 +35,11 @@
"column.community": "Local", "column.community": "Local",
"column.favourites": "Favoritos", "column.favourites": "Favoritos",
"column.follow_requests": "Seguidores Pendentes", "column.follow_requests": "Seguidores Pendentes",
"column.home": "Home", "column.home": "Início",
"column.lists": "Listas", "column.lists": "Listas",
"column.mutes": "Utilizadores silenciados", "column.mutes": "Utilizadores silenciados",
"column.notifications": "Notificações", "column.notifications": "Notificações",
"column.pins": "Pinned toot", "column.pins": "Posts fixos",
"column.public": "Global", "column.public": "Global",
"column_back_button.label": "Voltar", "column_back_button.label": "Voltar",
"column_header.hide_settings": "Esconder preferências", "column_header.hide_settings": "Esconder preferências",
@@ -47,7 +47,7 @@
"column_header.moveRight_settings": "Mover coluna para a direita", "column_header.moveRight_settings": "Mover coluna para a direita",
"column_header.pin": "Fixar", "column_header.pin": "Fixar",
"column_header.show_settings": "Mostrar preferências", "column_header.show_settings": "Mostrar preferências",
"column_header.unpin": "Remover fixar", "column_header.unpin": "Desafixar",
"column_subheading.navigation": "Navegação", "column_subheading.navigation": "Navegação",
"column_subheading.settings": "Preferências", "column_subheading.settings": "Preferências",
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
@@ -92,7 +92,7 @@
"empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.", "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.",
"empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.",
"empty_column.home.public_timeline": "global", "empty_column.home.public_timeline": "global",
"empty_column.list": "Ainda não existem publicações nesta lista.", "empty_column.list": "Ainda não existem publicações nesta lista. Quando membros desta lista fizerem novas publicações, elas aparecerão aqui.",
"empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.", "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.",
"empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos", "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos",
"follow_request.authorize": "Autorizar", "follow_request.authorize": "Autorizar",
@@ -226,7 +226,7 @@
"status.mute": "Mute @{name}", "status.mute": "Mute @{name}",
"status.mute_conversation": "Silenciar conversa", "status.mute_conversation": "Silenciar conversa",
"status.open": "Expandir", "status.open": "Expandir",
"status.pin": "Pin on profile", "status.pin": "Fixar no perfil",
"status.reblog": "Partilhar", "status.reblog": "Partilhar",
"status.reblogged_by": "{name} partilhou", "status.reblogged_by": "{name} partilhou",
"status.reply": "Responder", "status.reply": "Responder",
@@ -234,7 +234,7 @@
"status.report": "Denunciar @{name}", "status.report": "Denunciar @{name}",
"status.sensitive_toggle": "Clique para ver", "status.sensitive_toggle": "Clique para ver",
"status.sensitive_warning": "Conteúdo sensível", "status.sensitive_warning": "Conteúdo sensível",
"status.share": "Share", "status.share": "Compartilhar",
"status.show_less": "Mostrar menos", "status.show_less": "Mostrar menos",
"status.show_more": "Mostrar mais", "status.show_more": "Mostrar mais",
"status.unmute_conversation": "Deixar de silenciar esta conversa", "status.unmute_conversation": "Deixar de silenciar esta conversa",

View File

@@ -195,8 +195,8 @@
"privacy.private.short": "Iba sledujúci", "privacy.private.short": "Iba sledujúci",
"privacy.public.long": "Pošli všetkým", "privacy.public.long": "Pošli všetkým",
"privacy.public.short": "Verejne", "privacy.public.short": "Verejne",
"privacy.unlisted.long": "Neposielať verejne", "privacy.unlisted.long": "Neposielať do verejných časových osí",
"privacy.unlisted.short": "Nie je v zozname", "privacy.unlisted.short": "Verejne mimo osí",
"relative_time.days": "{number}d", "relative_time.days": "{number}d",
"relative_time.hours": "{number}h", "relative_time.hours": "{number}h",
"relative_time.just_now": "now", "relative_time.just_now": "now",

View File

@@ -11,7 +11,7 @@
"account.media": "Mediji", "account.media": "Mediji",
"account.mention": "Pomeni korisnika @{name}", "account.mention": "Pomeni korisnika @{name}",
"account.moved_to": "{name} se pomerio na:", "account.moved_to": "{name} se pomerio na:",
"account.mute": "Mutiraj @{name}", "account.mute": "Ućutkaj korisnika @{name}",
"account.mute_notifications": "Isključi obaveštenja od korisnika @{name}", "account.mute_notifications": "Isključi obaveštenja od korisnika @{name}",
"account.posts": "Statusa", "account.posts": "Statusa",
"account.report": "Prijavi @{name}", "account.report": "Prijavi @{name}",
@@ -21,7 +21,7 @@
"account.unblock": "Odblokiraj korisnika @{name}", "account.unblock": "Odblokiraj korisnika @{name}",
"account.unblock_domain": "Odblokiraj domen {domain}", "account.unblock_domain": "Odblokiraj domen {domain}",
"account.unfollow": "Otprati", "account.unfollow": "Otprati",
"account.unmute": "Odmutiraj @{name}", "account.unmute": "Ukloni ućutkavanje korisniku @{name}",
"account.unmute_notifications": "Uključi nazad obaveštenja od korisnika @{name}", "account.unmute_notifications": "Uključi nazad obaveštenja od korisnika @{name}",
"account.view_full_profile": "Vidi ceo profil", "account.view_full_profile": "Vidi ceo profil",
"boost_modal.combo": "Možete pritisnuti {combo} da preskočite ovo sledeći put", "boost_modal.combo": "Možete pritisnuti {combo} da preskočite ovo sledeći put",
@@ -37,10 +37,10 @@
"column.follow_requests": "Zahtevi za praćenje", "column.follow_requests": "Zahtevi za praćenje",
"column.home": "Početna", "column.home": "Početna",
"column.lists": "Liste", "column.lists": "Liste",
"column.mutes": "Mutirani korisnici", "column.mutes": "Ućutkani korisnici",
"column.notifications": "Obaveštenja", "column.notifications": "Obaveštenja",
"column.pins": "Prikačeni tutovi", "column.pins": "Prikačeni tutovi",
"column.public": "Združena lajna", "column.public": "Federisana lajna",
"column_back_button.label": "Nazad", "column_back_button.label": "Nazad",
"column_header.hide_settings": "Sakrij postavke", "column_header.hide_settings": "Sakrij postavke",
"column_header.moveLeft_settings": "Pomeri kolonu ulevo", "column_header.moveLeft_settings": "Pomeri kolonu ulevo",
@@ -50,6 +50,7 @@
"column_header.unpin": "Otkači", "column_header.unpin": "Otkači",
"column_subheading.navigation": "Navigacija", "column_subheading.navigation": "Navigacija",
"column_subheading.settings": "Postavke", "column_subheading.settings": "Postavke",
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
"compose_form.lock_disclaimer": "Vaš nalog nije {locked}. Svako može da Vas zaprati i da vidi objave namenjene samo Vašim pratiocima.", "compose_form.lock_disclaimer": "Vaš nalog nije {locked}. Svako može da Vas zaprati i da vidi objave namenjene samo Vašim pratiocima.",
"compose_form.lock_disclaimer.lock": "zaključan", "compose_form.lock_disclaimer.lock": "zaključan",
"compose_form.placeholder": "Šta Vam je na umu?", "compose_form.placeholder": "Šta Vam je na umu?",
@@ -66,9 +67,9 @@
"confirmations.delete_list.confirm": "Obriši", "confirmations.delete_list.confirm": "Obriši",
"confirmations.delete_list.message": "Da li ste sigurni da želite da bespovratno obrišete ovu listu?", "confirmations.delete_list.message": "Da li ste sigurni da želite da bespovratno obrišete ovu listu?",
"confirmations.domain_block.confirm": "Sakrij ceo domen", "confirmations.domain_block.confirm": "Sakrij ceo domen",
"confirmations.domain_block.message": "Da li ste stvarno, stvarno sigurno da želite da blokirate ceo domen {domain}? U većini slučajeva, par dobrih blokiranja ili mutiranja su dovoljna i preporučljiva.", "confirmations.domain_block.message": "Da li ste stvarno, stvarno sigurno da želite da blokirate ceo domen {domain}? U većini slučajeva, par dobrih blokiranja ili ućutkavanja su dovoljna i preporučljiva.",
"confirmations.mute.confirm": "Mutiraj", "confirmations.mute.confirm": "Ućutkaj",
"confirmations.mute.message": "Da li stvarno želite da mutirate korisnika {name}?", "confirmations.mute.message": "Da li stvarno želite da ućutkate korisnika {name}?",
"confirmations.unfollow.confirm": "Otprati", "confirmations.unfollow.confirm": "Otprati",
"confirmations.unfollow.message": "Da li ste sigurni da želite da otpratite korisnika {name}?", "confirmations.unfollow.message": "Da li ste sigurni da želite da otpratite korisnika {name}?",
"embed.instructions": "Ugradi ovaj status na Vaš veb sajt kopiranjem koda ispod.", "embed.instructions": "Ugradi ovaj status na Vaš veb sajt kopiranjem koda ispod.",
@@ -148,10 +149,10 @@
"navigation_bar.keyboard_shortcuts": "Prečice na tastaturi", "navigation_bar.keyboard_shortcuts": "Prečice na tastaturi",
"navigation_bar.lists": "Liste", "navigation_bar.lists": "Liste",
"navigation_bar.logout": "Odjava", "navigation_bar.logout": "Odjava",
"navigation_bar.mutes": "Mutirani korisnici", "navigation_bar.mutes": "Ućutkani korisnici",
"navigation_bar.pins": "Prikačeni tutovi", "navigation_bar.pins": "Prikačeni tutovi",
"navigation_bar.preferences": "Podešavanja", "navigation_bar.preferences": "Podešavanja",
"navigation_bar.public_timeline": "Združena lajna", "navigation_bar.public_timeline": "Federisana lajna",
"notification.favourite": "{name} je stavio Vaš status kao omiljeni", "notification.favourite": "{name} je stavio Vaš status kao omiljeni",
"notification.follow": "{name} Vas je zapratio", "notification.follow": "{name} Vas je zapratio",
"notification.mention": "{name} Vas je pomenuo", "notification.mention": "{name} Vas je pomenuo",
@@ -169,7 +170,7 @@
"notifications.column_settings.sound": "Puštaj zvuk", "notifications.column_settings.sound": "Puštaj zvuk",
"onboarding.done": "Gotovo", "onboarding.done": "Gotovo",
"onboarding.next": "Sledeće", "onboarding.next": "Sledeće",
"onboarding.page_five.public_timelines": "Lokalna lajna prikazuje sve javne statuse od svih na domenu {domain}. Združena lajna prikazuje javne statuse od svih ljudi koje prate korisnici sa domena {domain}. Ovo su javne lajne, sjajan način da otkrijete nove ljude.", "onboarding.page_five.public_timelines": "Lokalna lajna prikazuje sve javne statuse od svih na domenu {domain}. Federisana lajna prikazuje javne statuse od svih ljudi koje prate korisnici sa domena {domain}. Ovo su javne lajne, sjajan način da otkrijete nove ljude.",
"onboarding.page_four.home": "Početna lajna prikazuje statuse ljudi koje Vi pratite.", "onboarding.page_four.home": "Početna lajna prikazuje statuse ljudi koje Vi pratite.",
"onboarding.page_four.notifications": "Kolona sa obaveštenjima Vam prikazuje kada neko priča sa Vama.", "onboarding.page_four.notifications": "Kolona sa obaveštenjima Vam prikazuje kada neko priča sa Vama.",
"onboarding.page_one.federation": "Mastodont je mreža nezavisnih servera koji se uvezuju da naprave jednu veću društvenu mrežu. Ove servere zovemo instancama.", "onboarding.page_one.federation": "Mastodont je mreža nezavisnih servera koji se uvezuju da naprave jednu veću društvenu mrežu. Ove servere zovemo instancama.",
@@ -213,6 +214,7 @@
"search_popout.tips.user": "korisnik", "search_popout.tips.user": "korisnik",
"search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}", "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}",
"standalone.public_title": "Pogled iznutra...", "standalone.public_title": "Pogled iznutra...",
"status.block": "Block @{name}",
"status.cannot_reblog": "Ovaj status ne može da se podrži", "status.cannot_reblog": "Ovaj status ne može da se podrži",
"status.delete": "Obriši", "status.delete": "Obriši",
"status.embed": "Ugradi na sajt", "status.embed": "Ugradi na sajt",
@@ -221,7 +223,8 @@
"status.media_hidden": "Multimedija sakrivena", "status.media_hidden": "Multimedija sakrivena",
"status.mention": "Pomeni korisnika @{name}", "status.mention": "Pomeni korisnika @{name}",
"status.more": "Još", "status.more": "Još",
"status.mute_conversation": "Mutiraj prepisku", "status.mute": "Mute @{name}",
"status.mute_conversation": "Ućutkaj prepisku",
"status.open": "Proširi ovaj status", "status.open": "Proširi ovaj status",
"status.pin": "Prikači na profil", "status.pin": "Prikači na profil",
"status.reblog": "Podrži", "status.reblog": "Podrži",
@@ -237,7 +240,7 @@
"status.unmute_conversation": "Uključi prepisku", "status.unmute_conversation": "Uključi prepisku",
"status.unpin": "Otkači sa profila", "status.unpin": "Otkači sa profila",
"tabs_bar.compose": "Napiši", "tabs_bar.compose": "Napiši",
"tabs_bar.federated_timeline": "Združeno", "tabs_bar.federated_timeline": "Federisano",
"tabs_bar.home": "Početna", "tabs_bar.home": "Početna",
"tabs_bar.local_timeline": "Lokalno", "tabs_bar.local_timeline": "Lokalno",
"tabs_bar.notifications": "Obaveštenja", "tabs_bar.notifications": "Obaveštenja",

View File

@@ -11,7 +11,7 @@
"account.media": "Медији", "account.media": "Медији",
"account.mention": "Помени корисника @{name}", "account.mention": "Помени корисника @{name}",
"account.moved_to": "{name} се померио на:", "account.moved_to": "{name} се померио на:",
"account.mute": "Мутирај @{name}", "account.mute": "Ућуткај корисника @{name}",
"account.mute_notifications": "Искључи обавештења од корисника @{name}", "account.mute_notifications": "Искључи обавештења од корисника @{name}",
"account.posts": "Статуса", "account.posts": "Статуса",
"account.report": "Пријави @{name}", "account.report": "Пријави @{name}",
@@ -21,7 +21,7 @@
"account.unblock": "Одблокирај корисника @{name}", "account.unblock": "Одблокирај корисника @{name}",
"account.unblock_domain": "Одблокирај домен {domain}", "account.unblock_domain": "Одблокирај домен {domain}",
"account.unfollow": "Отпрати", "account.unfollow": "Отпрати",
"account.unmute": "Одмутирај @{name}", "account.unmute": "Уклони ућуткавање кориснику @{name}",
"account.unmute_notifications": "Укључи назад обавештења од корисника @{name}", "account.unmute_notifications": "Укључи назад обавештења од корисника @{name}",
"account.view_full_profile": "Види цео профил", "account.view_full_profile": "Види цео профил",
"boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут", "boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут",
@@ -37,10 +37,10 @@
"column.follow_requests": "Захтеви за праћење", "column.follow_requests": "Захтеви за праћење",
"column.home": "Почетна", "column.home": "Почетна",
"column.lists": "Листе", "column.lists": "Листе",
"column.mutes": "Мутирани корисници", "column.mutes": "Ућуткани корисници",
"column.notifications": "Обавештења", "column.notifications": "Обавештења",
"column.pins": "Прикачени тутови", "column.pins": "Прикачени тутови",
"column.public": "Здружена лајна", "column.public": "Федерисана лајна",
"column_back_button.label": "Назад", "column_back_button.label": "Назад",
"column_header.hide_settings": "Сакриј поставке", "column_header.hide_settings": "Сакриј поставке",
"column_header.moveLeft_settings": "Помери колону улево", "column_header.moveLeft_settings": "Помери колону улево",
@@ -67,9 +67,9 @@
"confirmations.delete_list.confirm": "Обриши", "confirmations.delete_list.confirm": "Обриши",
"confirmations.delete_list.message": "Да ли сте сигурни да желите да бесповратно обришете ову листу?", "confirmations.delete_list.message": "Да ли сте сигурни да желите да бесповратно обришете ову листу?",
"confirmations.domain_block.confirm": "Сакриј цео домен", "confirmations.domain_block.confirm": "Сакриј цео домен",
"confirmations.domain_block.message": "Да ли сте стварно, стварно сигурно да желите да блокирате цео домен {domain}? У већини случајева, пар добрих блокирања или мутирања су довољна и препоручљива.", "confirmations.domain_block.message": "Да ли сте стварно, стварно сигурно да желите да блокирате цео домен {domain}? У већини случајева, пар добрих блокирања или ућуткавања су довољна и препоручљива.",
"confirmations.mute.confirm": "Мутирај", "confirmations.mute.confirm": "Ућуткај",
"confirmations.mute.message": "Да ли стварно желите да мутирате корисника {name}?", "confirmations.mute.message": "Да ли стварно желите да ућуткате корисника {name}?",
"confirmations.unfollow.confirm": "Отпрати", "confirmations.unfollow.confirm": "Отпрати",
"confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?", "confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?",
"embed.instructions": "Угради овај статус на Ваш веб сајт копирањем кода испод.", "embed.instructions": "Угради овај статус на Ваш веб сајт копирањем кода испод.",
@@ -149,10 +149,10 @@
"navigation_bar.keyboard_shortcuts": "Пречице на тастатури", "navigation_bar.keyboard_shortcuts": "Пречице на тастатури",
"navigation_bar.lists": "Листе", "navigation_bar.lists": "Листе",
"navigation_bar.logout": "Одјава", "navigation_bar.logout": "Одјава",
"navigation_bar.mutes": "Мутирани корисници", "navigation_bar.mutes": "Ућуткани корисници",
"navigation_bar.pins": "Прикачени тутови", "navigation_bar.pins": "Прикачени тутови",
"navigation_bar.preferences": "Подешавања", "navigation_bar.preferences": "Подешавања",
"navigation_bar.public_timeline": "Здружена лајна", "navigation_bar.public_timeline": "Федерисана лајна",
"notification.favourite": "{name} је ставио Ваш статус као омиљени", "notification.favourite": "{name} је ставио Ваш статус као омиљени",
"notification.follow": "{name} Вас је запратио", "notification.follow": "{name} Вас је запратио",
"notification.mention": "{name} Вас је поменуо", "notification.mention": "{name} Вас је поменуо",
@@ -170,7 +170,7 @@
"notifications.column_settings.sound": "Пуштај звук", "notifications.column_settings.sound": "Пуштај звук",
"onboarding.done": "Готово", "onboarding.done": "Готово",
"onboarding.next": "Следеће", "onboarding.next": "Следеће",
"onboarding.page_five.public_timelines": "Локална лајна приказује све јавне статусе од свих на домену {domain}. Здружена лајна приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне лајне, сјајан начин да откријете нове људе.", "onboarding.page_five.public_timelines": "Локална лајна приказује све јавне статусе од свих на домену {domain}. Федерисана лајна приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне лајне, сјајан начин да откријете нове људе.",
"onboarding.page_four.home": "Почетна лајна приказује статусе људи које Ви пратите.", "onboarding.page_four.home": "Почетна лајна приказује статусе људи које Ви пратите.",
"onboarding.page_four.notifications": "Колона са обавештењима Вам приказује када неко прича са Вама.", "onboarding.page_four.notifications": "Колона са обавештењима Вам приказује када неко прича са Вама.",
"onboarding.page_one.federation": "Мастодонт је мрежа независних сервера који се увезују да направе једну већу друштвену мрежу. Ове сервере зовемо инстанцама.", "onboarding.page_one.federation": "Мастодонт је мрежа независних сервера који се увезују да направе једну већу друштвену мрежу. Ове сервере зовемо инстанцама.",
@@ -224,7 +224,7 @@
"status.mention": "Помени корисника @{name}", "status.mention": "Помени корисника @{name}",
"status.more": "Још", "status.more": "Још",
"status.mute": "Mute @{name}", "status.mute": "Mute @{name}",
"status.mute_conversation": "Мутирај преписку", "status.mute_conversation": "Ућуткај преписку",
"status.open": "Прошири овај статус", "status.open": "Прошири овај статус",
"status.pin": "Прикачи на профил", "status.pin": "Прикачи на профил",
"status.reblog": "Подржи", "status.reblog": "Подржи",
@@ -240,7 +240,7 @@
"status.unmute_conversation": "Укључи преписку", "status.unmute_conversation": "Укључи преписку",
"status.unpin": "Откачи са профила", "status.unpin": "Откачи са профила",
"tabs_bar.compose": "Напиши", "tabs_bar.compose": "Напиши",
"tabs_bar.federated_timeline": "Здружено", "tabs_bar.federated_timeline": "Федерисано",
"tabs_bar.home": "Почетна", "tabs_bar.home": "Почетна",
"tabs_bar.local_timeline": "Локално", "tabs_bar.local_timeline": "Локално",
"tabs_bar.notifications": "Обавештења", "tabs_bar.notifications": "Обавештења",

View File

@@ -0,0 +1,2 @@
[
]

View File

@@ -50,7 +50,7 @@
"column_header.unpin": "取消固定", "column_header.unpin": "取消固定",
"column_subheading.navigation": "导航", "column_subheading.navigation": "导航",
"column_subheading.settings": "设置", "column_subheading.settings": "设置",
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.hashtag_warning": "这条嘟文被设置为“不公开”,因此它不会出现在任何话题标签的列表下。只有公开的嘟文才能通过话题标签进行搜索。",
"compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。", "compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。",
"compose_form.lock_disclaimer.lock": "开启保护", "compose_form.lock_disclaimer.lock": "开启保护",
"compose_form.placeholder": "在想啥?", "compose_form.placeholder": "在想啥?",
@@ -139,6 +139,7 @@
"loading_indicator.label": "加载中……", "loading_indicator.label": "加载中……",
"media_gallery.toggle_visible": "切换显示/隐藏", "media_gallery.toggle_visible": "切换显示/隐藏",
"missing_indicator.label": "找不到内容", "missing_indicator.label": "找不到内容",
"missing_indicator.sublabel": "无法找到此资源",
"mute_modal.hide_notifications": "同时隐藏来自这个用户的通知", "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知",
"navigation_bar.blocks": "被屏蔽的用户", "navigation_bar.blocks": "被屏蔽的用户",
"navigation_bar.community_timeline": "本站时间轴", "navigation_bar.community_timeline": "本站时间轴",
@@ -170,11 +171,12 @@
"notifications.column_settings.sound": "播放音效", "notifications.column_settings.sound": "播放音效",
"onboarding.done": "出发!", "onboarding.done": "出发!",
"onboarding.next": "下一步", "onboarding.next": "下一步",
"onboarding.page_five.public_timelines": "本站时间轴显示的是由本站({domain})用户发布的所有公开嘟文。跨站公共时间轴显示的的是由本站用户关注对象所发布的所有公开嘟文。这些就是寻人好去处的公共时间轴啦。", "onboarding.page_five.public_timelines": "本站时间轴显示的是由本站({domain})用户发布的所有公开嘟文。跨站公共时间轴显示的的是由本站用户关注对象所发布的所有公开嘟文。这些就是寻人好去处的公共时间轴啦。",
"onboarding.page_four.home": "你的主页时间轴上显示的是你的关注对象所发布的嘟文。", "onboarding.page_four.home": "你的主页时间轴上显示的是你的关注对象所发布的嘟文。",
"onboarding.page_four.notifications": "如果有人与你互动了,他们就会出现在通知栏中哦~", "onboarding.page_four.notifications": "如果有人与你互动了,他们就会出现在通知栏中哦~",
"onboarding.page_one.federation": "Mastodon 是由一系列独立的服务器共同打造的强大的社交网络,我们将这些各自独立而又相互连接的服务器叫做实例。", "onboarding.page_one.federation": "Mastodon 是由一系列独立的服务器共同打造的强大的社交网络,我们将这些各自独立而又相互连接的服务器叫做实例。",
"onboarding.page_one.handle": "你是在 {domain} 上注册的,所以你的完整用户地址是 {handle}。", "onboarding.page_one.full_handle": "你的完整用户地址",
"onboarding.page_one.handle_hint": "你的朋友们需要这个才能通过搜索功能找到你。",
"onboarding.page_one.welcome": "欢迎来到 Mastodon", "onboarding.page_one.welcome": "欢迎来到 Mastodon",
"onboarding.page_six.admin": "{admin} 是你所在服务器实例的管理员。", "onboarding.page_six.admin": "{admin} 是你所在服务器实例的管理员。",
"onboarding.page_six.almost_done": "差不多了……", "onboarding.page_six.almost_done": "差不多了……",
@@ -184,8 +186,8 @@
"onboarding.page_six.guidelines": "社区指南", "onboarding.page_six.guidelines": "社区指南",
"onboarding.page_six.read_guidelines": "别忘了看看 {domain} 的{guidelines}", "onboarding.page_six.read_guidelines": "别忘了看看 {domain} 的{guidelines}",
"onboarding.page_six.various_app": "移动设备应用", "onboarding.page_six.various_app": "移动设备应用",
"onboarding.page_three.profile": "你可以修改你的个人资料,比如头像、简介和昵称等偏好设置。", "onboarding.page_three.profile": "你可以修改你的个人资料,比如头像、简介和昵称等偏好设置。",
"onboarding.page_three.search": "你可以通过搜索功能寻找用户和话题标签,比如{illustration}或者{introductions}。如果你想搜索其他实例上的用户,就需要输入完整用户地址(@用户名@域名)哦。", "onboarding.page_three.search": "你可以通过搜索功能寻找用户和话题标签,比如{illustration}”,或是“{introductions}。如果你想搜索其他实例上的用户,就需要输入完整用户地址(@用户名@域名)哦。",
"onboarding.page_two.compose": "在撰写栏中开始嘟嘟吧!下方的按钮分别可以用来上传图片、修改嘟文可见范围,以及添加警告信息。", "onboarding.page_two.compose": "在撰写栏中开始嘟嘟吧!下方的按钮分别可以用来上传图片、修改嘟文可见范围,以及添加警告信息。",
"onboarding.skip": "跳过", "onboarding.skip": "跳过",
"privacy.change": "设置嘟文可见范围", "privacy.change": "设置嘟文可见范围",
@@ -197,6 +199,8 @@
"privacy.public.short": "公开", "privacy.public.short": "公开",
"privacy.unlisted.long": "所有人可见,但不会出现在公共时间轴上", "privacy.unlisted.long": "所有人可见,但不会出现在公共时间轴上",
"privacy.unlisted.short": "不公开", "privacy.unlisted.short": "不公开",
"regeneration_indicator.label": "加载中……",
"regeneration_indicator.sublabel": "你的主页时间轴正在准备中!",
"relative_time.days": "{number}天", "relative_time.days": "{number}天",
"relative_time.hours": "{number}时", "relative_time.hours": "{number}时",
"relative_time.just_now": "刚刚", "relative_time.just_now": "刚刚",
@@ -214,7 +218,7 @@
"search_popout.tips.user": "用户", "search_popout.tips.user": "用户",
"search_results.total": "共 {count, number} 个结果", "search_results.total": "共 {count, number} 个结果",
"standalone.public_title": "大家都在干啥?", "standalone.public_title": "大家都在干啥?",
"status.block": "Block @{name}", "status.block": "屏蔽 @{name}",
"status.cannot_reblog": "无法转嘟这条嘟文", "status.cannot_reblog": "无法转嘟这条嘟文",
"status.delete": "删除", "status.delete": "删除",
"status.embed": "嵌入", "status.embed": "嵌入",
@@ -223,7 +227,7 @@
"status.media_hidden": "隐藏媒体内容", "status.media_hidden": "隐藏媒体内容",
"status.mention": "提及 @{name}", "status.mention": "提及 @{name}",
"status.more": "更多", "status.more": "更多",
"status.mute": "Mute @{name}", "status.mute": "隐藏 @{name}",
"status.mute_conversation": "隐藏此对话", "status.mute_conversation": "隐藏此对话",
"status.open": "展开嘟文", "status.open": "展开嘟文",
"status.pin": "在个人资料页面置顶", "status.pin": "在个人资料页面置顶",

View File

@@ -30,7 +30,7 @@ const initialTimeline = ImmutableMap({
items: ImmutableList(), items: ImmutableList(),
}); });
const normalizeTimeline = (state, timeline, statuses, next) => { const normalizeTimeline = (state, timeline, statuses, next, isPartial) => {
const oldIds = state.getIn([timeline, 'items'], ImmutableList()); const oldIds = state.getIn([timeline, 'items'], ImmutableList());
const ids = ImmutableList(statuses.map(status => status.get('id'))).filter(newId => !oldIds.includes(newId)); const ids = ImmutableList(statuses.map(status => status.get('id'))).filter(newId => !oldIds.includes(newId));
const wasLoaded = state.getIn([timeline, 'loaded']); const wasLoaded = state.getIn([timeline, 'loaded']);
@@ -40,7 +40,8 @@ const normalizeTimeline = (state, timeline, statuses, next) => {
mMap.set('loaded', true); mMap.set('loaded', true);
mMap.set('isLoading', false); mMap.set('isLoading', false);
if (!hadNext) mMap.set('next', next); if (!hadNext) mMap.set('next', next);
mMap.set('items', wasLoaded ? ids.concat(oldIds) : ids); mMap.set('items', wasLoaded ? ids.concat(oldIds) : oldIds.concat(ids));
mMap.set('isPartial', isPartial);
})); }));
}; };
@@ -124,7 +125,7 @@ export default function timelines(state = initialState, action) {
case TIMELINE_EXPAND_FAIL: case TIMELINE_EXPAND_FAIL:
return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false)); return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false));
case TIMELINE_REFRESH_SUCCESS: case TIMELINE_REFRESH_SUCCESS:
return normalizeTimeline(state, action.timeline, fromJS(action.statuses), action.next); return normalizeTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial);
case TIMELINE_EXPAND_SUCCESS: case TIMELINE_EXPAND_SUCCESS:
return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next); return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next);
case TIMELINE_UPDATE: case TIMELINE_UPDATE:

View File

@@ -0,0 +1 @@
require('../styles/mailer.scss');

View File

@@ -6,7 +6,6 @@
@import 'mastodon/reset'; @import 'mastodon/reset';
@import 'mastodon/basics'; @import 'mastodon/basics';
@import 'mastodon/modal';
@import 'mastodon/containers'; @import 'mastodon/containers';
@import 'mastodon/lists'; @import 'mastodon/lists';
@import 'mastodon/footer'; @import 'mastodon/footer';
@@ -15,7 +14,9 @@
@import 'mastodon/forms'; @import 'mastodon/forms';
@import 'mastodon/accounts'; @import 'mastodon/accounts';
@import 'mastodon/stream_entries'; @import 'mastodon/stream_entries';
@import 'mastodon/boost';
@import 'mastodon/components'; @import 'mastodon/components';
@import 'mastodon/modal';
@import 'mastodon/emoji_picker'; @import 'mastodon/emoji_picker';
@import 'mastodon/about'; @import 'mastodon/about';
@import 'mastodon/tables'; @import 'mastodon/tables';

View File

@@ -0,0 +1,546 @@
@import 'mastodon/variables';
@import 'fonts/roboto';
table,
td,
div {
box-sizing: border-box;
}
html,
body {
width: 100% !important;
min-width: 100%;
margin: 0;
padding: 0;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
.email-body {
td,
div,
a,
span {
line-height: inherit;
}
}
a {
&,
&:visited,
span {
text-decoration: none;
color: $ui-highlight-color;
}
#outlook & {
padding: 0;
}
}
img {
outline: none;
border: 0;
text-decoration: none;
-ms-interpolation-mode: bicubic;
clear: both;
line-height: 100%;
}
table {
border-spacing: 0;
mso-table-lspace: 0;
mso-table-rspace: 0;
}
td {
vertical-align: top;
}
.email-table,
.content-section,
.column,
.column-cell {
width: 100%;
min-width: 100%;
}
.email-body {
font-size: 0 !important;
line-height: 100%;
text-align: center;
padding-left: 16px;
padding-right: 16px;
}
.email-start {
padding-top: 32px;
}
.email-end {
padding-bottom: 32px;
}
.email-body,
html,
body {
background-color: lighten($ui-base-color, 4%);
}
.email-container,
.email-row,
.col-0,
.col-1,
.col-2,
.col-3,
.col-4,
.col-5,
.col-6, {
font-size: 0;
display: inline-block;
width: 100%;
min-width: 100%;
min-width: 0 !important;
vertical-align: top;
}
.content-cell {
width: 100%;
min-width: 100%;
min-width: 0 !important;
}
.column-cell {
padding-top: 16px;
padding-bottom: 16px;
vertical-align: top;
&.button-cell {
padding-top: 0;
}
}
.email-container {
max-width: 632px;
margin: 0 auto;
text-align: center;
}
.email-row {
display: block;
max-width: 600px !important;
margin: 0 auto;
text-align: center;
clear: both;
}
.col-0 {
max-width: 50px;
}
.col-1 {
max-width: 100px;
}
.col-2 {
max-width: 200px;
}
.col-3 {
max-width: 300px;
}
.col-4 {
max-width: 400px;
}
.col-5 {
max-width: 500px;
}
.col-6 {
max-width: 600px;
}
.column-cell,
.column-cell td,
p {
font-family: Helvetica, Arial, sans-serif;
@media only screen {
font-family: 'mastodon-font-sans-serif', sans-serif !important;
}
}
.email-body .column-cell,
.column-cell,
p {
font-size: 15px;
line-height: 23px;
color: $ui-primary-color;
mso-line-height-rule: exactly;
text-rendering: optimizelegibility;
}
p {
display: block;
margin-top: 0;
margin-bottom: 16px;
&.small {
font-size: 13px;
}
&.lead {
font-size: 19px;
line-height: 27px;
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: $ui-secondary-color;
margin-left: 0;
margin-right: 0;
margin-top: 20px;
margin-bottom: 8px;
padding: 0;
font-weight: 500;
}
h1 {
font-size: 26px;
line-height: 36px;
}
h2 {
font-size: 23px;
line-height: 30px;
}
h3 {
font-size: 19px;
line-height: 25px;
}
h5 {
font-size: 16px;
line-height: 21px;
font-weight: 700;
color: lighten($ui-base-color, 34%);
}
.input-cell {
h5 {
margin-top: 4px;
}
}
.input {
td {
background: darken($ui-base-color, 8%);
border-radius: 4px;
padding: 16px;
line-height: 20px;
mso-line-height-rule: exactly;
border-radius: 4px;
text-align: center;
font-weight: 500;
font-size: 17px;
}
}
.content-cell,
.blank-cell {
width: 100%;
font-size: 0;
text-align: center;
vertical-align: top;
padding-left: 16px;
padding-right: 16px;
}
.content-cell {
background-color: darken($ui-base-color, 4%);
&.darker {
background-color: darken($ui-base-color, 8%);
}
}
.hero {
background-color: $ui-base-color;
padding-top: 20px;
}
.hero-with-button {
h1 {
margin-bottom: 4px;
}
p.lead {
margin-bottom: 32px;
}
padding-bottom: 16px;
}
.header {
border-radius: 5px 5px 0 0;
background-color: darken($ui-base-color, 8%);
.column-cell {
text-align: center;
padding-top: 20px;
padding-bottom: 8px;
}
}
.content-start {
padding-top: 32px;
}
.content-end {
border-radius: 0 0 5px 5px;
padding-top: 16px;
}
.footer {
.column-cell,
p {
color: lighten($ui-base-color, 34%);
}
p {
margin-bottom: 0;
font-size: 13px;
&.small {
margin-bottom: 0;
}
}
a {
color: lighten($ui-base-color, 34%);
text-decoration: underline;
}
img {
opacity: 0.3;
}
}
.logo {
position: relative;
left: -4px;
}
.button {
display: table;
margin-left: auto;
margin-right: auto;
td {
line-height: 20px;
mso-line-height-rule: exactly;
border-radius: 4px;
text-align: center;
font-weight: 500;
font-size: 17px;
padding: 0 !important;
a,
a span {
color: $primary-text-color;
display: block !important;
text-align: center !important;
vertical-align: top !important;
line-height: inherit !important;
}
a {
padding: 10px 22px !important;
line-height: 26px !important;
font-weight: 500 !important;
}
}
&.button-small {
td {
border-radius: 4px;
font-size: 14px;
padding: 8px 16px;
a {
padding: 5px 16px !important;
line-height: 26px !important;
}
}
}
}
.button-default {
background-color: darken($ui-base-color, 8%);
}
.button-primary {
background-color: darken($ui-highlight-color, 3%);
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.padded {
padding-left: 16px;
padding-right: 16px;
}
.padded-bottom {
padding-bottom: 32px;
}
.margin-bottom {
margin-bottom: 20px;
}
.hero-icon {
width: 64px;
td {
text-align: center;
vertical-align: middle;
line-height: 100%;
mso-line-height-rule: exactly;
padding: 16px;
border-radius: 80px;
background: $success-green;
}
img {
max-width: 32px;
width: 32px;
height: 32px;
display: block;
line-height: 100%;
}
}
.hr {
width: 100%;
td {
font-size: 0;
line-height: 1px;
mso-line-height-rule: exactly;
min-height: 1px;
overflow: hidden;
height: 2px;
background-color: transparent !important;
border-top: 1px solid lighten($ui-base-color, 8%);
}
}
.status {
padding-bottom: 32px;
.status-header {
td {
font-size: 14px;
padding-bottom: 15px;
}
bdi {
color: $white;
font-size: 16px;
display: block;
font-weight: 500;
}
td:first-child {
padding-right: 10px;
}
img {
width: 48px;
height: 48px;
border-radius: 4px;
}
}
p {
font-size: 19px;
margin-bottom: 20px;
&.status-footer {
color: lighten($ui-base-color, 26%);
font-size: 14px;
margin-bottom: 0;
a {
color: lighten($ui-base-color, 26%);
}
}
}
}
.border-top {
border-top: 1px solid lighten($ui-base-color, 8%);
}
ul {
padding-left: 15px;
margin-top: 0;
margin-bottom: 0;
padding-top: 16px;
li {
margin-bottom: 16px;
color: lighten($ui-base-color, 26%);
span {
color: $ui-primary-color;
}
}
}
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) {
body {
min-height: 1024px !important;
}
}
@media (max-width: 697px) {
.email-container,
.col-1,
.col-2,
.col-3,
.col-4,
.col-5,
.col-6 {
width: 100% !important;
max-width: none !important;
}
.email-start {
padding-top: 16px !important;
}
.email-end {
padding-bottom: 16px !important;
}
.padded {
padding-left: 0 !important;
padding-right: 0 !important;
}
}

View File

@@ -398,10 +398,12 @@
} }
} }
&__content {
max-width: calc(100% - 90px);
}
&__title { &__title {
overflow: hidden; word-wrap: break-word;
text-overflow: ellipsis;
white-space: nowrap;
} }
&__timestamp { &__timestamp {
@@ -415,7 +417,7 @@
color: $ui-primary-color; color: $ui-primary-color;
font-family: 'mastodon-font-monospace', monospace; font-family: 'mastodon-font-monospace', monospace;
font-size: 12px; font-size: 12px;
white-space: nowrap; word-wrap: break-word;
min-height: 20px; min-height: 20px;
} }

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