Browse Source

Merge branch 'master' into live

master
Zac 1 month ago
parent
commit
2fda87fcc0
100 changed files with 1510 additions and 729 deletions
  1. 1
    1
      .codeclimate.yml
  2. 2
    2
      .env.production.sample
  3. 2
    2
      .rubocop.yml
  4. 67
    0
      CHANGELOG.md
  5. 12
    12
      Gemfile
  6. 84
    80
      Gemfile.lock
  7. 3
    3
      app/controllers/accounts_controller.rb
  8. 1
    1
      app/controllers/api/v1/accounts/follower_accounts_controller.rb
  9. 1
    1
      app/controllers/api/v1/accounts/following_accounts_controller.rb
  10. 2
    2
      app/controllers/api/v1/timelines/public_controller.rb
  11. 7
    0
      app/controllers/auth/sessions_controller.rb
  12. 1
    13
      app/controllers/concerns/localized.rb
  13. 8
    4
      app/controllers/settings/identity_proofs_controller.rb
  14. 6
    1
      app/controllers/well_known/webfinger_controller.rb
  15. 3
    3
      app/helpers/home_helper.rb
  16. 1
    0
      app/helpers/settings_helper.rb
  17. 19
    0
      app/helpers/webfinger_helper.rb
  18. 1
    1
      app/javascript/flavours/glitch/actions/streaming.js
  19. 1
    1
      app/javascript/flavours/glitch/actions/timelines.js
  20. 6
    4
      app/javascript/flavours/glitch/features/account/components/header.js
  21. 5
    12
      app/javascript/flavours/glitch/features/compose/components/compose_form.js
  22. 6
    2
      app/javascript/flavours/glitch/features/compose/components/poll_form.js
  23. 30
    0
      app/javascript/flavours/glitch/features/public_timeline/components/column_settings.js
  24. 1
    1
      app/javascript/flavours/glitch/features/public_timeline/containers/column_settings_container.js
  25. 16
    13
      app/javascript/flavours/glitch/features/public_timeline/index.js
  26. 1
    0
      app/javascript/flavours/glitch/features/ui/components/columns_area.js
  27. 0
    13
      app/javascript/flavours/glitch/styles/about.scss
  28. 5
    0
      app/javascript/flavours/glitch/styles/components/accounts.scss
  29. 1
    0
      app/javascript/flavours/glitch/styles/components/search.scss
  30. 5
    0
      app/javascript/flavours/glitch/styles/statuses.scss
  31. 0
    6
      app/javascript/flavours/glitch/styles/widgets.scss
  32. 1
    0
      app/javascript/images/logo_transparent_white.svg
  33. 1
    1
      app/javascript/mastodon/actions/streaming.js
  34. 1
    1
      app/javascript/mastodon/actions/timelines.js
  35. 6
    4
      app/javascript/mastodon/features/account/components/header.js
  36. 6
    2
      app/javascript/mastodon/features/compose/components/poll_form.js
  37. 30
    0
      app/javascript/mastodon/features/public_timeline/components/column_settings.js
  38. 1
    1
      app/javascript/mastodon/features/public_timeline/containers/column_settings_container.js
  39. 16
    13
      app/javascript/mastodon/features/public_timeline/index.js
  40. 3
    12
      app/javascript/mastodon/features/ui/components/__tests__/column-test.js
  41. 1
    0
      app/javascript/mastodon/features/ui/components/columns_area.js
  42. 3
    0
      app/javascript/mastodon/locales/ar.json
  43. 3
    0
      app/javascript/mastodon/locales/ast.json
  44. 3
    0
      app/javascript/mastodon/locales/bg.json
  45. 3
    0
      app/javascript/mastodon/locales/bn.json
  46. 3
    0
      app/javascript/mastodon/locales/br.json
  47. 3
    0
      app/javascript/mastodon/locales/ca.json
  48. 11
    8
      app/javascript/mastodon/locales/co.json
  49. 3
    0
      app/javascript/mastodon/locales/cs.json
  50. 3
    0
      app/javascript/mastodon/locales/cy.json
  51. 5
    2
      app/javascript/mastodon/locales/da.json
  52. 3
    0
      app/javascript/mastodon/locales/de.json
  53. 22
    5
      app/javascript/mastodon/locales/defaultMessages.json
  54. 3
    0
      app/javascript/mastodon/locales/el.json
  55. 2
    0
      app/javascript/mastodon/locales/en.json
  56. 34
    31
      app/javascript/mastodon/locales/eo.json
  57. 3
    0
      app/javascript/mastodon/locales/es-AR.json
  58. 3
    0
      app/javascript/mastodon/locales/es.json
  59. 3
    0
      app/javascript/mastodon/locales/et.json
  60. 3
    0
      app/javascript/mastodon/locales/eu.json
  61. 3
    0
      app/javascript/mastodon/locales/fa.json
  62. 3
    0
      app/javascript/mastodon/locales/fi.json
  63. 8
    5
      app/javascript/mastodon/locales/fr.json
  64. 3
    0
      app/javascript/mastodon/locales/ga.json
  65. 8
    5
      app/javascript/mastodon/locales/gl.json
  66. 3
    0
      app/javascript/mastodon/locales/he.json
  67. 3
    0
      app/javascript/mastodon/locales/hi.json
  68. 3
    0
      app/javascript/mastodon/locales/hr.json
  69. 5
    2
      app/javascript/mastodon/locales/hu.json
  70. 17
    14
      app/javascript/mastodon/locales/hy.json
  71. 3
    0
      app/javascript/mastodon/locales/id.json
  72. 3
    0
      app/javascript/mastodon/locales/io.json
  73. 3
    0
      app/javascript/mastodon/locales/is.json
  74. 3
    0
      app/javascript/mastodon/locales/it.json
  75. 3
    0
      app/javascript/mastodon/locales/ja.json
  76. 3
    0
      app/javascript/mastodon/locales/ka.json
  77. 3
    0
      app/javascript/mastodon/locales/kab.json
  78. 3
    0
      app/javascript/mastodon/locales/kk.json
  79. 3
    0
      app/javascript/mastodon/locales/kn.json
  80. 3
    0
      app/javascript/mastodon/locales/ko.json
  81. 3
    0
      app/javascript/mastodon/locales/lt.json
  82. 3
    0
      app/javascript/mastodon/locales/lv.json
  83. 3
    0
      app/javascript/mastodon/locales/mk.json
  84. 3
    0
      app/javascript/mastodon/locales/ml.json
  85. 3
    0
      app/javascript/mastodon/locales/mr.json
  86. 3
    0
      app/javascript/mastodon/locales/ms.json
  87. 3
    0
      app/javascript/mastodon/locales/nl.json
  88. 3
    0
      app/javascript/mastodon/locales/nn.json
  89. 3
    0
      app/javascript/mastodon/locales/no.json
  90. 4
    1
      app/javascript/mastodon/locales/oc.json
  91. 3
    0
      app/javascript/mastodon/locales/pl.json
  92. 126
    123
      app/javascript/mastodon/locales/pt-BR.json
  93. 33
    30
      app/javascript/mastodon/locales/pt-PT.json
  94. 196
    193
      app/javascript/mastodon/locales/ro.json
  95. 4
    1
      app/javascript/mastodon/locales/ru.json
  96. 445
    0
      app/javascript/mastodon/locales/sc.json
  97. 4
    1
      app/javascript/mastodon/locales/sk.json
  98. 7
    4
      app/javascript/mastodon/locales/sl.json
  99. 95
    92
      app/javascript/mastodon/locales/sq.json
  100. 0
    0
      app/javascript/mastodon/locales/sr-Latn.json

+ 1
- 1
.codeclimate.yml View File

@@ -30,7 +30,7 @@ plugins:
channel: eslint-6
rubocop:
enabled: true
channel: rubocop-0-76
channel: rubocop-0-82
sass-lint:
enabled: true
exclude_patterns:

+ 2
- 2
.env.production.sample View File

@@ -33,7 +33,7 @@ LOCAL_DOMAIN=example.com
# ALTERNATE_DOMAINS=example1.com,example2.com

# Application secrets
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web bundle exec rake secret` if you use docker compose)
SECRET_KEY_BASE=
OTP_SECRET=

@@ -42,7 +42,7 @@ OTP_SECRET=
# You should only generate this once per instance. If you later decide to change it, all push subscription will
# be invalidated, requiring the users to access the website again to resubscribe.
#
# Generate with `RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose)
# Generate with `RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web bundle exec rake mastodon:webpush:generate_vapid_key` if you use docker compose)
#
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
VAPID_PRIVATE_KEY=

+ 2
- 2
.rubocop.yml View File

@@ -2,7 +2,7 @@ require:
- rubocop-rails

AllCops:
TargetRubyVersion: 2.3
TargetRubyVersion: 2.4
Exclude:
- 'spec/**/*'
- 'db/**/*'
@@ -46,7 +46,7 @@ Metrics/ClassLength:
Metrics/CyclomaticComplexity:
Max: 25

Metrics/LineLength:
Layout/LineLength:
AllowURI: true
Enabled: false


+ 67
- 0
CHANGELOG.md View File

@@ -3,6 +3,73 @@ Changelog

All notable changes to this project will be documented in this file.

## [v3.1.4] - 2020-05-14
### Added

- Add `vi` to available locales ([taicv](https://github.com/tootsuite/mastodon/pull/13542))
- Add ability to remove identity proofs from account ([Gargron](https://github.com/tootsuite/mastodon/pull/13682))
- Add ability to exclude local content from federated timeline ([noellabo](https://github.com/tootsuite/mastodon/pull/13504), [noellabo](https://github.com/tootsuite/mastodon/pull/13745))
- Add `remote` param to `GET /api/v1/timelines/public` REST API
- Add `public/remote` / `public:remote` variants to streaming API
- "Remote only" option in federated timeline column settings in web UI
- Add ability to exclude remote content from hashtag timelines in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/13502))
- No changes to REST API
- "Local only" option in hashtag column settings in web UI
- Add Capistrano tasks that reload the services after deploying ([berkes](https://github.com/tootsuite/mastodon/pull/12642))
- Add `invites_enabled` attribute to `GET /api/v1/instance` in REST API ([ThibG](https://github.com/tootsuite/mastodon/pull/13501))
- Add `tootctl emoji export` command ([lfuelling](https://github.com/tootsuite/mastodon/pull/13534))
- Add separate cache directory for non-local uploads ([Gargron](https://github.com/tootsuite/mastodon/pull/12821), [Hanage999](https://github.com/tootsuite/mastodon/pull/13593), [mayaeh](https://github.com/tootsuite/mastodon/pull/13551))
- Add `tootctl upgrade storage-schema` command to move old non-local uploads to the cache directory
- Add buttons to delete header and avatar from profile settings ([sternenseemann](https://github.com/tootsuite/mastodon/pull/13234))
- Add emoji graphics and shortcodes from Twemoji 12.1.5 ([DeeUnderscore](https://github.com/tootsuite/mastodon/pull/13021))

### Changed

- Change error message when trying to migrate to an account that does not have current account set as an alias to be more clear ([TheEvilSkeleton](https://github.com/tootsuite/mastodon/pull/13746))
- Change delivery failure tracking to work with hostnames instead of URLs ([Gargron](https://github.com/tootsuite/mastodon/pull/13437), [noellabo](https://github.com/tootsuite/mastodon/pull/13481), [noellabo](https://github.com/tootsuite/mastodon/pull/13482), [noellabo](https://github.com/tootsuite/mastodon/pull/13535))
- Change Content-Security-Policy to not need unsafe-inline style-src ([ThibG](https://github.com/tootsuite/mastodon/pull/13679), [ThibG](https://github.com/tootsuite/mastodon/pull/13692), [ThibG](https://github.com/tootsuite/mastodon/pull/13576), [ThibG](https://github.com/tootsuite/mastodon/pull/13575), [ThibG](https://github.com/tootsuite/mastodon/pull/13438))
- Change how RSS items are titled and formatted ([ThibG](https://github.com/tootsuite/mastodon/pull/13592), [ykzts](https://github.com/tootsuite/mastodon/pull/13591))

### Fixed

- Fix dropdown of muted and followed accounts offering option to hide boosts in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13748))
- Fix "You are already signed in" alert being shown at wrong times ([ThibG](https://github.com/tootsuite/mastodon/pull/13547))
- Fix retrying of failed-to-download media files not actually working ([noellabo](https://github.com/tootsuite/mastodon/pull/13741))
- Fix first poll option not being focused when adding a poll in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13740))
- Fix `sr` locale being selected over `sr-Latn` ([ThibG](https://github.com/tootsuite/mastodon/pull/13693))
- Fix error within error when limiting backtrace to 3 lines ([Gargron](https://github.com/tootsuite/mastodon/pull/13120))
- Fix `tootctl media remove-orphans` crashing on "Import" files ([ThibG](https://github.com/tootsuite/mastodon/pull/13685))
- Fix regression in `tootctl media remove-orphans` ([Gargron](https://github.com/tootsuite/mastodon/pull/13405))
- Fix old unique jobs digests not having been cleaned up ([Gargron](https://github.com/tootsuite/mastodon/pull/13683))
- Fix own following/followers not showing muted users ([ThibG](https://github.com/tootsuite/mastodon/pull/13614))
- Fix list of followed people ignoring sorting on Follows & Followers page ([taras2358](https://github.com/tootsuite/mastodon/pull/13676))
- Fix wrong pgHero Content-Security-Policy when `CDN_HOST` is set ([ThibG](https://github.com/tootsuite/mastodon/pull/13595))
- Fix needlessly deduplicating usernames on collisions with remote accounts when signing-up through SAML/CAS ([kaiyou](https://github.com/tootsuite/mastodon/pull/13581))
- Fix page incorrectly scrolling when bringing up dropdown menus in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13574))
- Fix messed up z-index when NoScript blocks media/previews in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13449))
- Fix "See what's happening" page showing public instead of local timeline for logged-in users ([ThibG](https://github.com/tootsuite/mastodon/pull/13499))
- Fix not being able to resolve public resources in development environment ([Gargron](https://github.com/tootsuite/mastodon/pull/13505))
- Fix uninformative error message when uploading unsupported image files ([ThibG](https://github.com/tootsuite/mastodon/pull/13540))
- Fix expanded video player issues in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13541), [eai04191](https://github.com/tootsuite/mastodon/pull/13533))
- Fix and refactor keyboard navigation in dropdown menus in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13528))
- Fix uploaded image orientation being messed up in some browsers in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13493))
- Fix actions log crash when displaying updates of deleted announcements in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13489))
- Fix search not working due to proxy settings when using hidden services ([Gargron](https://github.com/tootsuite/mastodon/pull/13488))
- Fix poll refresh button not being debounced in web UI ([rasjonell](https://github.com/tootsuite/mastodon/pull/13485), [ThibG](https://github.com/tootsuite/mastodon/pull/13490))
- Fix confusing error when failing to add an alias to an unknown account ([ThibG](https://github.com/tootsuite/mastodon/pull/13480))
- Fix "Email changed" notification sometimes having wrong e-mail ([ThibG](https://github.com/tootsuite/mastodon/pull/13475))
- Fix varioues issues on the account aliases page ([ThibG](https://github.com/tootsuite/mastodon/pull/13452))
- Fix API footer link in web UI ([bubblineyuri](https://github.com/tootsuite/mastodon/pull/13441))
- Fix pagination of following, followers, follow requests, blocks and mutes lists in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13445))
- Fix styling of polls in JS-less fallback on public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/13436))
- Fix trying to delete already deleted file when post-processing ([Gargron](https://github.com/tootsuite/mastodon/pull/13406))

### Security

- Fix Doorkeeper vulnerability that exposed app secret to users who authorized the app and reset secret of the web UI that could have been exposed ([dependabot-preview[bot]](https://github.com/tootsuite/mastodon/pull/13613), [Gargron](https://github.com/tootsuite/mastodon/pull/13688))
- For apps that self-register on behalf of every individual user (such as most mobile apps), this is a non-issue
- The issue only affects developers of apps who are shared between multiple users, such as server-side apps like cross-posters

## [v3.1.3] - 2020-04-05
### Added


+ 12
- 12
Gemfile View File

@@ -20,7 +20,7 @@ gem 'makara', '~> 0.4'
gem 'pghero', '~> 2.4'
gem 'dotenv-rails', '~> 2.7'

gem 'aws-sdk-s3', '~> 1.63', require: false
gem 'aws-sdk-s3', '~> 1.64', require: false
gem 'fog-core', '<= 2.1.0'
gem 'fog-openstack', '~> 0.3', require: false
gem 'paperclip', '~> 6.0'
@@ -49,7 +49,7 @@ gem 'omniauth-saml', '~> 1.10'
gem 'omniauth', '~> 1.9'

gem 'discard', '~> 1.2'
gem 'doorkeeper', '~> 5.3'
gem 'doorkeeper', '~> 5.4'
gem 'fast_blank', '~> 1.0'
gem 'fastimage'
gem 'goldfinger', '~> 2.1'
@@ -57,12 +57,12 @@ gem 'hiredis', '~> 0.6'
gem 'redis-namespace', '~> 1.7'
gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b'
gem 'htmlentities', '~> 4.3'
gem 'http', '~> 4.3'
gem 'http', '~> 4.4'
gem 'http_accept_language', '~> 2.1'
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2', submodules: true
gem 'httplog', '~> 1.4.2'
gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.1'
gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.3.1', require: 'mime/types/columnar'
gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532'
@@ -75,7 +75,7 @@ gem 'parallel', '~> 1.19'
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
gem 'pundit', '~> 2.1'
gem 'premailer-rails'
gem 'rack-attack', '~> 6.2'
gem 'rack-attack', '~> 6.3'
gem 'rack-cors', '~> 1.1', require: 'rack/cors'
gem 'rails-i18n', '~> 5.1'
gem 'rails-settings-cached', '~> 0.6'
@@ -96,8 +96,8 @@ gem 'strong_migrations', '~> 0.6'
gem 'tty-command', '~> 0.9', require: false
gem 'tty-prompt', '~> 0.21', require: false
gem 'twitter-text', '~> 1.14'
gem 'tzinfo-data', '~> 1.2019'
gem 'webpacker', '~> 4.2'
gem 'tzinfo-data', '~> 1.2020'
gem 'webpacker', '~> 5.1'
gem 'webpush'

gem 'json-ld'
@@ -110,7 +110,7 @@ group :development, :test do
gem 'fabrication', '~> 2.21'
gem 'fuubar', '~> 2.5'
gem 'i18n-tasks', '~> 0.9', require: false
gem 'pry-byebug', '~> 3.8'
gem 'pry-byebug', '~> 3.9'
gem 'pry-rails', '~> 0.3'
gem 'rspec-rails', '~> 4.0'
end
@@ -120,7 +120,7 @@ group :production, :test do
end

group :test do
gem 'capybara', '~> 3.31'
gem 'capybara', '~> 3.32'
gem 'climate_control', '~> 0.2'
gem 'faker', '~> 2.11'
gem 'microformats', '~> 4.2'
@@ -135,18 +135,18 @@ end
group :development do
gem 'active_record_query_trace', '~> 1.7'
gem 'annotate', '~> 3.1'
gem 'better_errors', '~> 2.6'
gem 'better_errors', '~> 2.7'
gem 'binding_of_caller', '~> 0.7'
gem 'bullet', '~> 6.1'
gem 'letter_opener', '~> 1.7'
gem 'letter_opener_web', '~> 1.4'
gem 'memory_profiler'
gem 'rubocop', '~> 0.79', require: false
gem 'rubocop', '~> 0.82', require: false
gem 'rubocop-rails', '~> 2.5', require: false
gem 'brakeman', '~> 4.8', require: false
gem 'bundler-audit', '~> 0.6', require: false

gem 'capistrano', '~> 3.13'
gem 'capistrano', '~> 3.14'
gem 'capistrano-rails', '~> 1.4'
gem 'capistrano-rbenv', '~> 2.1'
gem 'capistrano-yarn', '~> 2.0'

+ 84
- 80
Gemfile.lock View File

@@ -92,23 +92,23 @@ GEM
av (0.9.0)
cocaine (~> 0.5.3)
aws-eventstream (1.1.0)
aws-partitions (1.303.0)
aws-sdk-core (3.94.0)
aws-partitions (1.312.0)
aws-sdk-core (3.95.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.30.0)
aws-sdk-kms (1.31.0)
aws-sdk-core (~> 3, >= 3.71.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.63.0)
aws-sdk-s3 (1.64.0)
aws-sdk-core (~> 3, >= 3.83.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.1.2)
aws-sigv4 (1.1.3)
aws-eventstream (~> 1.0, >= 1.0.2)
bcrypt (3.1.13)
better_errors (2.6.0)
better_errors (2.7.0)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
@@ -118,8 +118,8 @@ GEM
ffi (~> 1.10.0)
bootsnap (1.4.6)
msgpack (~> 1.0)
brakeman (4.8.0)
browser (4.0.0)
brakeman (4.8.1)
browser (4.1.0)
builder (3.2.4)
bullet (6.1.0)
activesupport (>= 3.0.0)
@@ -127,8 +127,8 @@ GEM
bundler-audit (0.6.1)
bundler (>= 1.2.0, < 3)
thor (~> 0.18)
byebug (11.1.1)
capistrano (3.13.0)
byebug (11.1.3)
capistrano (3.14.0)
airbrussh (>= 1.0.0)
i18n
rake (>= 10.0.0)
@@ -143,7 +143,7 @@ GEM
sshkit (~> 1.3)
capistrano-yarn (2.0.2)
capistrano (~> 3.0)
capybara (3.31.0)
capybara (3.32.1)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
@@ -194,26 +194,26 @@ GEM
docile (1.3.2)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.3.1)
doorkeeper (5.4.0)
railties (>= 5)
dotenv (2.7.5)
dotenv-rails (2.7.5)
dotenv (= 2.7.5)
railties (>= 3.2, < 6.1)
e2mmap (0.1.0)
elasticsearch (7.6.0)
elasticsearch-api (= 7.6.0)
elasticsearch-transport (= 7.6.0)
elasticsearch-api (7.6.0)
elasticsearch (7.7.0)
elasticsearch-api (= 7.7.0)
elasticsearch-transport (= 7.7.0)
elasticsearch-api (7.7.0)
multi_json
elasticsearch-dsl (0.1.9)
elasticsearch-transport (7.6.0)
elasticsearch-transport (7.7.0)
faraday (~> 1)
multi_json
encryptor (3.0.0)
equatable (0.6.1)
erubi (1.9.0)
et-orbi (1.2.3)
et-orbi (1.2.4)
tzinfo
excon (0.73.0)
fabrication (2.21.1)
@@ -240,7 +240,7 @@ GEM
fog-json (>= 1.0)
ipaddress (>= 0.8)
formatador (0.2.5)
fugit (1.3.3)
fugit (1.3.5)
et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.1)
fuubar (2.5.0)
@@ -270,7 +270,7 @@ GEM
hiredis (0.6.3)
hkdf (0.3.0)
htmlentities (4.3.4)
http (4.3.0)
http (4.4.1)
addressable (~> 2.3)
http-cookie (~> 1.0)
http-form_data (~> 2.2)
@@ -303,7 +303,7 @@ GEM
jmespath (1.4.0)
json (2.3.0)
json-canonicalization (0.2.0)
json-ld (3.1.3)
json-ld (3.1.4)
htmlentities (~> 4.3)
json-canonicalization (~> 0.2)
link_header (~> 0.0, >= 0.0.8)
@@ -314,21 +314,21 @@ GEM
json-ld (~> 3.1)
rdf (~> 3.1)
jsonapi-renderer (0.2.2)
jwt (2.1.0)
kaminari (1.1.1)
jwt (2.2.1)
kaminari (1.2.0)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.1.1)
kaminari-activerecord (= 1.1.1)
kaminari-core (= 1.1.1)
kaminari-actionview (1.1.1)
kaminari-actionview (= 1.2.0)
kaminari-activerecord (= 1.2.0)
kaminari-core (= 1.2.0)
kaminari-actionview (1.2.0)
actionview
kaminari-core (= 1.1.1)
kaminari-activerecord (1.1.1)
kaminari-core (= 1.2.0)
kaminari-activerecord (1.2.0)
activerecord
kaminari-core (= 1.1.1)
kaminari-core (1.1.1)
launchy (2.4.3)
addressable (~> 2.3)
kaminari-core (= 1.2.0)
kaminari-core (1.2.0)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener_web (1.4.0)
@@ -353,14 +353,14 @@ GEM
mario-redis-lock (1.2.1)
redis (>= 3.0.5)
memory_profiler (0.9.14)
method_source (0.9.2)
method_source (1.0.0)
microformats (4.2.0)
json (~> 2.2)
nokogiri (~> 1.10)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2020.0425)
mimemagic (0.3.4)
mimemagic (0.3.5)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.0)
@@ -369,9 +369,9 @@ GEM
multipart-post (2.1.1)
necromancer (0.5.1)
net-ldap (0.16.2)
net-scp (2.0.0)
net-ssh (>= 2.6.5, < 6.0.0)
net-ssh (5.2.0)
net-scp (3.0.0)
net-ssh (>= 2.6.5, < 7.0.0)
net-ssh (6.0.2)
nio4r (2.5.2)
nokogiri (1.10.9)
mini_portile2 (~> 2.4.0)
@@ -407,40 +407,40 @@ GEM
parallel (1.19.1)
parallel_tests (2.32.0)
parallel
parser (2.7.1.1)
parser (2.7.1.2)
ast (~> 2.4.0)
parslet (2.0.0)
pastel (0.7.3)
pastel (0.7.4)
equatable (~> 0.6)
tty-color (~> 0.5)
pg (1.2.3)
pghero (2.4.1)
pghero (2.4.2)
activerecord (>= 5)
pkg-config (1.4.1)
premailer (1.11.1)
addressable
css_parser (>= 1.6.0)
htmlentities (>= 4.0.0)
premailer-rails (1.10.3)
premailer-rails (1.11.1)
actionmailer (>= 3)
premailer (~> 1.7, >= 1.7.9)
private_address_check (0.5.0)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
pry-byebug (3.8.0)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.9.0)
byebug (~> 11.0)
pry (~> 0.10)
pry (~> 0.13.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (4.0.4)
public_suffix (4.0.5)
puma (4.3.3)
nio4r (~> 2.0)
pundit (2.1.0)
activesupport (>= 3.0.0)
raabro (1.1.6)
raabro (1.3.1)
rack (2.2.2)
rack-attack (6.2.2)
rack-attack (6.3.0)
rack (>= 1.0, < 3)
rack-cors (1.1.1)
rack (>= 2.0.0)
@@ -491,13 +491,13 @@ GEM
rdf-normalize (0.4.0)
rdf (~> 3.1)
redcarpet (3.5.0)
redis (4.1.3)
redis (4.1.4)
redis-actionpack (5.2.0)
actionpack (>= 5, < 7)
redis-rack (>= 2.1.0, < 3)
redis-store (>= 1.1.0, < 2)
redis-activesupport (5.0.4)
activesupport (>= 3, < 6)
redis-activesupport (5.2.0)
activesupport (>= 3, < 7)
redis-store (>= 1.3, < 2)
redis-namespace (1.7.0)
redis (>= 3.0.4)
@@ -516,15 +516,16 @@ GEM
responders (3.0.0)
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.4)
rotp (2.1.2)
rpam2 (4.0.2)
rqrcode (1.1.2)
chunky_png (~> 1.0)
rqrcode_core (~> 0.1)
rqrcode_core (0.1.2)
rspec-core (3.9.1)
rspec-support (~> 3.9.1)
rspec-expectations (3.9.1)
rspec-core (3.9.2)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
@@ -541,16 +542,17 @@ GEM
rspec-sidekiq (3.0.3)
rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0)
rspec-support (3.9.2)
rspec-support (3.9.3)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
rubocop (0.79.0)
rubocop (0.82.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0)
rexml
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-rails (2.5.2)
activesupport
rack (>= 1.1)
@@ -565,9 +567,10 @@ GEM
crass (~> 1.0.2)
nokogiri (>= 1.8.0)
nokogumbo (~> 2.0)
sidekiq (6.0.4)
semantic_range (2.3.0)
sidekiq (6.0.7)
connection_pool (>= 2.2.2)
rack (>= 2.0.0)
rack (~> 2.0)
rack-protection (>= 2.0.0)
redis (>= 4.1.0)
sidekiq-bulk (0.2.0)
@@ -607,7 +610,7 @@ GEM
stoplight (2.2.0)
streamio-ffmpeg (3.0.2)
multi_json (~> 1.8)
strong_migrations (0.6.2)
strong_migrations (0.6.6)
activerecord (>= 5)
temple (0.8.2)
terminal-table (1.8.0)
@@ -635,12 +638,12 @@ GEM
unf (~> 0.1.0)
tzinfo (1.2.7)
thread_safe (~> 0.1)
tzinfo-data (1.2019.3)
tzinfo-data (1.2020.1)
tzinfo (>= 1.0.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
unicode-display_width (1.6.1)
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
uniform_notifier (1.13.0)
warden (1.2.8)
rack (>= 2.0.6)
@@ -648,10 +651,11 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (4.2.2)
activesupport (>= 4.2)
webpacker (5.1.1)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 4.2)
railties (>= 5.2)
semantic_range (>= 2.3.0)
webpush (0.3.8)
hkdf (~> 0.2)
jwt (~> 2.0)
@@ -670,8 +674,8 @@ DEPENDENCIES
active_record_query_trace (~> 1.7)
addressable (~> 2.7)
annotate (~> 3.1)
aws-sdk-s3 (~> 1.63)
better_errors (~> 2.6)
aws-sdk-s3 (~> 1.64)
better_errors (~> 2.7)
binding_of_caller (~> 0.7)
blurhash (~> 0.1)
bootsnap (~> 1.4)
@@ -679,11 +683,11 @@ DEPENDENCIES
browser
bullet (~> 6.1)
bundler-audit (~> 0.6)
capistrano (~> 3.13)
capistrano (~> 3.14)
capistrano-rails (~> 1.4)
capistrano-rbenv (~> 2.1)
capistrano-yarn (~> 2.0)
capybara (~> 3.31)
capybara (~> 3.32)
charlock_holmes (~> 0.7.7)
chewy (~> 5.1)
cld3 (~> 3.3.0)
@@ -694,7 +698,7 @@ DEPENDENCIES
devise-two-factor (~> 3.1)
devise_pam_authenticatable2 (~> 9.2)
discard (~> 1.2)
doorkeeper (~> 5.3)
doorkeeper (~> 5.4)
dotenv-rails (~> 2.7)
e2mmap (~> 0.1.0)
fabrication (~> 2.21)
@@ -709,7 +713,7 @@ DEPENDENCIES
health_check!
hiredis (~> 0.6)
htmlentities (~> 4.3)
http (~> 4.3)
http (~> 4.4)
http_accept_language (~> 2.1)
http_parser.rb (~> 0.6)!
httplog (~> 1.4.2)
@@ -718,7 +722,7 @@ DEPENDENCIES
iso-639
json-ld
json-ld-preloaded (~> 3.1)
kaminari (~> 1.1)
kaminari (~> 1.2)
letter_opener (~> 1.7)
letter_opener_web (~> 1.4)
link_header (~> 0.0)
@@ -748,12 +752,12 @@ DEPENDENCIES
posix-spawn!
premailer-rails
private_address_check (~> 0.5)
pry-byebug (~> 3.8)
pry-byebug (~> 3.9)
pry-rails (~> 0.3)
puma (~> 4.3)
pundit (~> 2.1)
rack (~> 2.2.2)
rack-attack (~> 6.2)
rack-attack (~> 6.3)
rack-cors (~> 1.1)
rails (~> 5.2.4.2)
rails-controller-testing (~> 1.0)
@@ -768,7 +772,7 @@ DEPENDENCIES
rspec-rails (~> 4.0)
rspec-sidekiq (~> 3.0)
rspec_junit_formatter (~> 0.4)
rubocop (~> 0.79)
rubocop (~> 0.82)
rubocop-rails (~> 2.5)
ruby-progressbar (~> 1.10)
sanitize (~> 5.1)
@@ -790,9 +794,9 @@ DEPENDENCIES
tty-command (~> 0.9)
tty-prompt (~> 0.21)
twitter-text (~> 1.14)
tzinfo-data (~> 1.2019)
tzinfo-data (~> 1.2020)
webmock (~> 3.8)
webpacker (~> 4.2)
webpacker (~> 5.1)
webpush

RUBY VERSION

+ 3
- 3
app/controllers/accounts_controller.rb View File

@@ -41,7 +41,7 @@ class AccountsController < ApplicationController
format.rss do
expires_in 1.minute, public: true

@statuses = filtered_statuses.without_reblogs.without_replies.limit(PAGE_SIZE)
@statuses = filtered_statuses.without_reblogs.limit(PAGE_SIZE)
@statuses = cache_collection(@statuses, Status)
render xml: RSS::AccountSerializer.render(@account, @statuses, params[:tag])
end
@@ -130,11 +130,11 @@ class AccountsController < ApplicationController
end

def media_requested?
request.path.ends_with?('/media') && !tag_requested?
request.path.split('.').first.ends_with?('/media') && !tag_requested?
end

def replies_requested?
request.path.ends_with?('/with_replies') && !tag_requested?
request.path.split('.').first.ends_with?('/with_replies') && !tag_requested?
end

def tag_requested?

+ 1
- 1
app/controllers/api/v1/accounts/follower_accounts_controller.rb View File

@@ -20,7 +20,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
return [] if hide_results?

scope = default_accounts
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? || current_account.id == @account.id
scope.merge(paginated_follows).to_a
end


+ 1
- 1
app/controllers/api/v1/accounts/following_accounts_controller.rb View File

@@ -20,7 +20,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
return [] if hide_results?

scope = default_accounts
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? || current_account.id == @account.id
scope.merge(paginated_follows).to_a
end


+ 2
- 2
app/controllers/api/v1/timelines/public_controller.rb View File

@@ -39,7 +39,7 @@ class Api::V1::Timelines::PublicController < Api::BaseController
end

def public_timeline_statuses
Status.as_public_timeline(current_account, truthy_param?(:local))
Status.as_public_timeline(current_account, truthy_param?(:remote) ? :remote : truthy_param?(:local))
end

def insert_pagination_headers
@@ -47,7 +47,7 @@ class Api::V1::Timelines::PublicController < Api::BaseController
end

def pagination_params(core_params)
params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params)
params.slice(:local, :remote, :limit, :only_media).permit(:local, :remote, :limit, :only_media).merge(core_params)
end

def next_path

+ 7
- 0
app/controllers/auth/sessions_controller.rb View File

@@ -113,6 +113,13 @@ class Auth::SessionsController < Devise::SessionsController
render :two_factor
end

def require_no_authentication
super
# Delete flash message that isn't entirely useful and may be confusing in
# most cases because /web doesn't display/clear flash messages.
flash.delete(:alert) if flash[:alert] == I18n.t('devise.failure.already_authenticated')
end

private

def set_pack

+ 1
- 13
app/controllers/concerns/localized.rb View File

@@ -28,18 +28,6 @@ module Localized
end

def request_locale
preferred_locale || compatible_locale
end

def preferred_locale
http_accept_language.preferred_language_from(available_locales)
end

def compatible_locale
http_accept_language.compatible_language_from(available_locales)
end

def available_locales
I18n.available_locales.reverse
http_accept_language.language_region_compatible_from(I18n.available_locales)
end
end

+ 8
- 4
app/controllers/settings/identity_proofs_controller.rb View File

@@ -22,8 +22,7 @@ class Settings::IdentityProofsController < Settings::BaseController
if current_account.username.casecmp(params[:username]).zero?
render layout: 'auth'
else
flash[:alert] = I18n.t('identity_proofs.errors.wrong_user', proving: params[:username], current: current_account.username)
redirect_to settings_identity_proofs_path
redirect_to settings_identity_proofs_path, alert: I18n.t('identity_proofs.errors.wrong_user', proving: params[:username], current: current_account.username)
end
end

@@ -35,11 +34,16 @@ class Settings::IdentityProofsController < Settings::BaseController
PostStatusService.new.call(current_user.account, text: post_params[:status_text]) if publish_proof?
redirect_to @proof.on_success_path(params[:user_agent])
else
flash[:alert] = I18n.t('identity_proofs.errors.failed', provider: @proof.provider.capitalize)
redirect_to settings_identity_proofs_path
redirect_to settings_identity_proofs_path, alert: I18n.t('identity_proofs.errors.failed', provider: @proof.provider.capitalize)
end
end

def destroy
@proof = current_account.identity_proofs.find(params[:id])
@proof.destroy!
redirect_to settings_identity_proofs_path, success: I18n.t('identity_proofs.removed')
end

private

def check_enabled

+ 6
- 1
app/controllers/well_known/webfinger_controller.rb View File

@@ -8,7 +8,8 @@ module WellKnown
before_action :set_account
before_action :check_account_suspension

rescue_from ActiveRecord::RecordNotFound, ActionController::ParameterMissing, with: :not_found
rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from ActionController::ParameterMissing, WebfingerResource::InvalidRequest, with: :bad_request

def show
expires_in 3.days, public: true
@@ -37,6 +38,10 @@ module WellKnown
expires_in(3.minutes, public: true) && gone if @account.suspended?
end

def bad_request
head 400
end

def not_found
head 404
end

+ 3
- 3
app/helpers/home_helper.rb View File

@@ -7,13 +7,13 @@ module HomeHelper
}
end

def account_link_to(account, button = '', size: 36, path: nil)
def account_link_to(account, button = '', path: nil)
content_tag(:div, class: 'account') do
content_tag(:div, class: 'account__wrapper') do
section = if account.nil?
content_tag(:div, class: 'account__display-name') do
content_tag(:div, class: 'account__avatar-wrapper') do
content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})")
image_tag(full_asset_url('avatars/original/missing.png', skip_pipeline: true), class: 'account__avatar')
end +
content_tag(:span, class: 'display-name') do
content_tag(:strong, t('about.contact_missing')) +
@@ -23,7 +23,7 @@ module HomeHelper
else
link_to(path || ActivityPub::TagManager.instance.url_for(account), class: 'account__display-name') do
content_tag(:div, class: 'account__avatar-wrapper') do
content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{full_asset_url(current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url)})")
image_tag(full_asset_url(current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url), class: 'account__avatar')
end +
content_tag(:span, class: 'display-name') do
content_tag(:bdi) do

+ 1
- 0
app/helpers/settings_helper.rb View File

@@ -69,6 +69,7 @@ module SettingsHelper
tr: 'Türkçe',
uk: 'Українська',
ur: 'اُردُو',
vi: 'Tiếng Việt',
'zh-CN': '简体中文',
'zh-HK': '繁體中文(香港)',
'zh-TW': '繁體中文(臺灣)',

+ 19
- 0
app/helpers/webfinger_helper.rb View File

@@ -0,0 +1,19 @@
# frozen_string_literal: true

module WebfingerHelper
def webfinger!(uri)
hidden_service_uri = /\.(onion|i2p)(:\d+)?$/.match(uri)

raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if !Rails.configuration.x.access_to_hidden_service && hidden_service_uri

opts = {
ssl: !hidden_service_uri,

headers: {
'User-Agent': Mastodon::Version.user_agent,
},
}

Goldfinger::Client.new(uri, opts.merge(Rails.configuration.x.http_client_proxy)).finger
end
end

+ 1
- 1
app/javascript/flavours/glitch/actions/streaming.js View File

@@ -73,7 +73,7 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => {

export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);
export const connectPublicStream = ({ onlyMedia } = {}) => connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`);
export const connectPublicStream = ({ onlyMedia, onlyRemote } = {}) => connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`);
export const connectHashtagStream = (id, tag, accept) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept);
export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);

+ 1
- 1
app/javascript/flavours/glitch/actions/timelines.js View File

@@ -121,7 +121,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
};

export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
export const expandPublicTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
export const expandPublicTimeline = ({ maxId, onlyMedia, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandDirectTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done);
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });

+ 6
- 4
app/javascript/flavours/glitch/features/account/components/header.js View File

@@ -186,10 +186,12 @@ class Header extends ImmutablePureComponent {
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
} else {
if (account.getIn(['relationship', 'following'])) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
} else {
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
if (!account.getIn(['relationship', 'muting'])) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
} else {
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
}
}

menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });

+ 5
- 12
app/javascript/flavours/glitch/features/compose/components/compose_form.js View File

@@ -158,19 +158,12 @@ class ComposeForm extends ImmutablePureComponent {
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
}

// When the escape key is released, we focus the UI.
handleKeyUp = ({ key, ctrlKey, keyCode, metaKey, altKey }) => {
if (key === 'Escape') {
document.querySelector('.ui').parentElement.focus();
}

// We submit the status on control/meta + enter.
if (keyCode === 13 && (ctrlKey || metaKey)) {
handleKeyDown = (e) => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
this.handleSubmit();
}

// Submit the status with secondary visibility on alt + enter.
if (keyCode === 13 && altKey) {
if (e.keyCode == 13 && e.altKey) {
this.handleSecondarySubmit();
}
}
@@ -305,7 +298,7 @@ class ComposeForm extends ImmutablePureComponent {
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
value={spoilerText}
onChange={this.handleChangeSpoiler}
onKeyUp={this.handleKeyUp}
onKeyDown={this.handleKeyDown}
disabled={!spoiler}
ref={this.handleRefSpoilerText}
suggestions={this.props.suggestions}
@@ -325,7 +318,7 @@ class ComposeForm extends ImmutablePureComponent {
disabled={isSubmitting}
value={this.props.text}
onChange={this.handleChange}
onKeyUp={this.handleKeyUp}
onKeyDown={this.handleKeyDown}
suggestions={this.props.suggestions}
onFocus={this.handleFocus}
onSuggestionsFetchRequested={onFetchSuggestions}

+ 6
- 2
app/javascript/flavours/glitch/features/compose/components/poll_form.js View File

@@ -28,6 +28,7 @@ class Option extends React.PureComponent {
title: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
isPollMultiple: PropTypes.bool,
autoFocus: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
suggestions: ImmutablePropTypes.list,
@@ -58,7 +59,7 @@ class Option extends React.PureComponent {
}

render () {
const { isPollMultiple, title, index, intl } = this.props;
const { isPollMultiple, title, index, autoFocus, intl } = this.props;

return (
<li>
@@ -75,6 +76,7 @@ class Option extends React.PureComponent {
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
searchTokens={[':']}
autoFocus={autoFocus}
/>
</label>

@@ -125,10 +127,12 @@ class PollForm extends ImmutablePureComponent {
return null;
}

const autoFocusIndex = options.indexOf('');

return (
<div className='compose-form__poll-wrapper'>
<ul>
{options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} {...other} />)}
{options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
{options.size < pollLimits.max_options && (
<label className='poll__text editable'>
<span className={classNames('poll__input')} style={{ opacity: 0 }} />

+ 30
- 0
app/javascript/flavours/glitch/features/public_timeline/components/column_settings.js View File

@@ -0,0 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { injectIntl, FormattedMessage } from 'react-intl';
import SettingToggle from '../../notifications/components/setting_toggle';

export default @injectIntl
class ColumnSettings extends React.PureComponent {

static propTypes = {
settings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
};

render () {
const { settings, onChange } = this.props;

return (
<div>
<div className='column-settings__row'>
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} />
<SettingToggle settings={settings} settingPath={['other', 'onlyRemote']} onChange={onChange} label={<FormattedMessage id='community.column_settings.remote_only' defaultMessage='Remote only' />} />
</div>
</div>
);
}

}

+ 1
- 1
app/javascript/flavours/glitch/features/public_timeline/containers/column_settings_container.js View File

@@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import ColumnSettings from 'flavours/glitch/features/community_timeline/components/column_settings';
import ColumnSettings from '../components/column_settings';
import { changeSetting } from 'flavours/glitch/actions/settings';
import { changeColumnParams } from 'flavours/glitch/actions/columns';

+ 16
- 13
app/javascript/flavours/glitch/features/public_timeline/index.js View File

@@ -19,11 +19,13 @@ const mapStateToProps = (state, { columnId }) => {
const columns = state.getIn(['settings', 'columns']);
const index = columns.findIndex(c => c.get('uuid') === uuid);
const onlyMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'public', 'other', 'onlyMedia']);
const onlyRemote = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyRemote']) : state.getIn(['settings', 'public', 'other', 'onlyRemote']);
const timelineState = state.getIn(['timelines', `public${onlyMedia ? ':media' : ''}`]);

return {
hasUnread: !!timelineState && timelineState.get('unread') > 0,
onlyMedia,
onlyRemote,
};
};

@@ -46,15 +48,16 @@ class PublicTimeline extends React.PureComponent {
multiColumn: PropTypes.bool,
hasUnread: PropTypes.bool,
onlyMedia: PropTypes.bool,
onlyRemote: PropTypes.bool,
};

handlePin = () => {
const { columnId, dispatch, onlyMedia } = this.props;
const { columnId, dispatch, onlyMedia, onlyRemote } = this.props;

if (columnId) {
dispatch(removeColumn(columnId));
} else {
dispatch(addColumn('PUBLIC', { other: { onlyMedia } }));
dispatch(addColumn(onlyRemote ? 'REMOTE' : 'PUBLIC', { other: { onlyMedia, onlyRemote } }));
}
}

@@ -68,19 +71,19 @@ class PublicTimeline extends React.PureComponent {
}

componentDidMount () {
const { dispatch, onlyMedia } = this.props;
const { dispatch, onlyMedia, onlyRemote } = this.props;

dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote }));
}

componentDidUpdate (prevProps) {
if (prevProps.onlyMedia !== this.props.onlyMedia) {
const { dispatch, onlyMedia } = this.props;
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) {
const { dispatch, onlyMedia, onlyRemote } = this.props;

this.disconnect();
dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote }));
}
}

@@ -96,13 +99,13 @@ class PublicTimeline extends React.PureComponent {
}

handleLoadMore = maxId => {
const { dispatch, onlyMedia } = this.props;
const { dispatch, onlyMedia, onlyRemote } = this.props;

dispatch(expandPublicTimeline({ maxId, onlyMedia }));
dispatch(expandPublicTimeline({ maxId, onlyMedia, onlyRemote }));
}

render () {
const { intl, columnId, hasUnread, multiColumn, onlyMedia } = this.props;
const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
const pinned = !!columnId;

return (
@@ -121,7 +124,7 @@ class PublicTimeline extends React.PureComponent {
</ColumnHeader>

<StatusListContainer
timelineId={`public${onlyMedia ? ':media' : ''}`}
timelineId={`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore}
trackScroll={!pinned}
scrollKey={`public_timeline-${columnId}`}

+ 1
- 0
app/javascript/flavours/glitch/features/ui/components/columns_area.js View File

@@ -37,6 +37,7 @@ const componentMap = {
'HOME': HomeTimeline,
'NOTIFICATIONS': Notifications,
'PUBLIC': PublicTimeline,
'REMOTE': PublicTimeline,
'COMMUNITY': CommunityTimeline,
'HASHTAG': HashtagTimeline,
'DIRECT': DirectTimeline,

+ 0
- 13
app/javascript/flavours/glitch/styles/about.scss View File

@@ -545,13 +545,6 @@ $small-breakpoint: 960px;
flex: 0 0 auto;
}

&__avatar {
width: 44px;
height: 44px;
background-size: 44px 44px;
@include avatar-size(44px);
}

.display-name {
font-size: 15px;

@@ -752,12 +745,6 @@ $small-breakpoint: 960px;
display: flex;
align-items: center;
}

.account__avatar {
width: 44px;
height: 44px;
background-size: 44px 44px;
}
}

&__counters__wrapper {

+ 5
- 0
app/javascript/flavours/glitch/styles/components/accounts.scss View File

@@ -38,9 +38,14 @@

.account__avatar {
@include avatar-radius();
display: block;
position: relative;
cursor: pointer;

width: 36px;
height: 36px;
background-size: 36px 36px;

&-inline {
display: inline-block;
vertical-align: middle;

+ 1
- 0
app/javascript/flavours/glitch/styles/components/search.scss View File

@@ -185,6 +185,7 @@

path:last-child {
stroke: lighten($highlight-text-color, 6%) !important;
fill: none !important;
}
}
}

+ 5
- 0
app/javascript/flavours/glitch/styles/statuses.scss View File

@@ -145,6 +145,11 @@
&__avatar {
left: 15px;
top: 17px;

.account__avatar {
width: 48px;
height: 48px;
}
}

&__content {

+ 0
- 6
app/javascript/flavours/glitch/styles/widgets.scss View File

@@ -93,12 +93,6 @@
display: flex;
align-items: center;
}

.account__avatar {
width: 44px;
height: 44px;
background-size: 44px 44px;
}
}

.trends__item {

+ 1
- 0
app/javascript/images/logo_transparent_white.svg 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>

+ 1
- 1
app/javascript/mastodon/actions/streaming.js View File

@@ -73,7 +73,7 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => {

export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);
export const connectPublicStream = ({ onlyMedia } = {}) => connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`);
export const connectPublicStream = ({ onlyMedia, onlyRemote } = {}) => connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`);
export const connectHashtagStream = (id, tag, accept) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept);
export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);

+ 1
- 1
app/javascript/mastodon/actions/timelines.js View File

@@ -107,7 +107,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
};

export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
export const expandPublicTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
export const expandPublicTimeline = ({ maxId, onlyMedia, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });

+ 6
- 4
app/javascript/mastodon/features/account/components/header.js View File

@@ -192,10 +192,12 @@ class Header extends ImmutablePureComponent {
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
} else {
if (account.getIn(['relationship', 'following'])) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
} else {
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
if (!account.getIn(['relationship', 'muting'])) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
} else {
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
}
}

menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });

+ 6
- 2
app/javascript/mastodon/features/compose/components/poll_form.js View File

@@ -27,6 +27,7 @@ class Option extends React.PureComponent {
title: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
isPollMultiple: PropTypes.bool,
autoFocus: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
onToggleMultiple: PropTypes.func.isRequired,
@@ -71,7 +72,7 @@ class Option extends React.PureComponent {
}

render () {
const { isPollMultiple, title, index, intl } = this.props;
const { isPollMultiple, title, index, autoFocus, intl } = this.props;

return (
<li>
@@ -96,6 +97,7 @@ class Option extends React.PureComponent {
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
searchTokens={[':']}
autoFocus={autoFocus}
/>
</label>

@@ -146,10 +148,12 @@ class PollForm extends ImmutablePureComponent {
return null;
}

const autoFocusIndex = options.indexOf('');

return (
<div className='compose-form__poll-wrapper'>
<ul>
{options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} {...other} />)}
{options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
</ul>

<div className='poll__footer'>

+ 30
- 0
app/javascript/mastodon/features/public_timeline/components/column_settings.js View File

@@ -0,0 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { injectIntl, FormattedMessage } from 'react-intl';
import SettingToggle from '../../notifications/components/setting_toggle';

export default @injectIntl
class ColumnSettings extends React.PureComponent {

static propTypes = {
settings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
};

render () {
const { settings, onChange } = this.props;

return (
<div>
<div className='column-settings__row'>
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} />
<SettingToggle settings={settings} settingPath={['other', 'onlyRemote']} onChange={onChange} label={<FormattedMessage id='community.column_settings.remote_only' defaultMessage='Remote only' />} />
</div>
</div>
);
}

}

+ 1
- 1
app/javascript/mastodon/features/public_timeline/containers/column_settings_container.js View File

@@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import ColumnSettings from '../../community_timeline/components/column_settings';
import ColumnSettings from '../components/column_settings';
import { changeSetting } from '../../../actions/settings';
import { changeColumnParams } from '../../../actions/columns';


+ 16
- 13
app/javascript/mastodon/features/public_timeline/index.js View File

@@ -19,11 +19,13 @@ const mapStateToProps = (state, { columnId }) => {
const columns = state.getIn(['settings', 'columns']);
const index = columns.findIndex(c => c.get('uuid') === uuid);
const onlyMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'public', 'other', 'onlyMedia']);
const onlyRemote = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyRemote']) : state.getIn(['settings', 'public', 'other', 'onlyRemote']);
const timelineState = state.getIn(['timelines', `public${onlyMedia ? ':media' : ''}`]);

return {
hasUnread: !!timelineState && timelineState.get('unread') > 0,
onlyMedia,
onlyRemote,
};
};

@@ -47,15 +49,16 @@ class PublicTimeline extends React.PureComponent {
multiColumn: PropTypes.bool,
hasUnread: PropTypes.bool,
onlyMedia: PropTypes.bool,
onlyRemote: PropTypes.bool,
};

handlePin = () => {
const { columnId, dispatch, onlyMedia } = this.props;
const { columnId, dispatch, onlyMedia, onlyRemote } = this.props;

if (columnId) {
dispatch(removeColumn(columnId));
} else {
dispatch(addColumn('PUBLIC', { other: { onlyMedia } }));
dispatch(addColumn(onlyRemote ? 'REMOTE' : 'PUBLIC', { other: { onlyMedia, onlyRemote } }));
}
}

@@ -69,19 +72,19 @@ class PublicTimeline extends React.PureComponent {
}

componentDidMount () {
const { dispatch, onlyMedia } = this.props;
const { dispatch, onlyMedia, onlyRemote } = this.props;

dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote }));
}

componentDidUpdate (prevProps) {
if (prevProps.onlyMedia !== this.props.onlyMedia) {
const { dispatch, onlyMedia } = this.props;
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) {
const { dispatch, onlyMedia, onlyRemote } = this.props;

this.disconnect();
dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia, onlyRemote }));
}
}

@@ -97,13 +100,13 @@ class PublicTimeline extends React.PureComponent {
}

handleLoadMore = maxId => {
const { dispatch, onlyMedia } = this.props;
const { dispatch, onlyMedia, onlyRemote } = this.props;

dispatch(expandPublicTimeline({ maxId, onlyMedia }));
dispatch(expandPublicTimeline({ maxId, onlyMedia, onlyRemote }));
}

render () {
const { intl, shouldUpdateScroll, columnId, hasUnread, multiColumn, onlyMedia } = this.props;
const { intl, shouldUpdateScroll, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
const pinned = !!columnId;

return (
@@ -122,7 +125,7 @@ class PublicTimeline extends React.PureComponent {
</ColumnHeader>

<StatusListContainer
timelineId={`public${onlyMedia ? ':media' : ''}`}
timelineId={`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore}
trackScroll={!pinned}
scrollKey={`public_timeline-${columnId}`}

+ 3
- 12
app/javascript/mastodon/features/ui/components/__tests__/column-test.js View File

@@ -5,30 +5,21 @@ import ColumnHeader from '../column_header';

describe('<Column />', () => {
describe('<ColumnHeader /> click handler', () => {
const originalRaf = global.requestAnimationFrame;

beforeEach(() => {
global.requestAnimationFrame = jest.fn();
});

afterAll(() => {
global.requestAnimationFrame = originalRaf;
});

it('runs the scroll animation if the column contains scrollable content', () => {
const wrapper = mount(
<Column heading='notifications'>
<div className='scrollable' />
</Column>,
);
const scrollToMock = jest.fn();
wrapper.find(Column).find('.scrollable').getDOMNode().scrollTo = scrollToMock;
wrapper.find(ColumnHeader).find('button').simulate('click');
expect(global.requestAnimationFrame.mock.calls.length).toEqual(1);
expect(scrollToMock).toHaveBeenCalledWith({ behavior: 'smooth', top: 0 });
});

it('does not try to scroll if there is no scrollable content', () => {
const wrapper = mount(<Column heading='notifications' />);
wrapper.find(ColumnHeader).find('button').simulate('click');
expect(global.requestAnimationFrame.mock.calls.length).toEqual(0);
});
});
});

+ 1
- 0
app/javascript/mastodon/features/ui/components/columns_area.js View File

@@ -37,6 +37,7 @@ const componentMap = {
'HOME': HomeTimeline,
'NOTIFICATIONS': Notifications,
'PUBLIC': PublicTimeline,
'REMOTE': PublicTimeline,
'COMMUNITY': CommunityTimeline,
'HASHTAG': HashtagTimeline,
'DIRECT': DirectTimeline,

+ 3
- 0
app/javascript/mastodon/locales/ar.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "عرض الإعدادات",
"column_header.unpin": "فك التدبيس",
"column_subheading.settings": "الإعدادات",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "الوسائط فقط",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "لن يَظهر هذا التبويق إلا للمستخدمين المذكورين.",
"compose_form.direct_message_warning_learn_more": "اقرأ المزيد",
"compose_form.hashtag_warning": "هذا التبويق لن يُدرَج تحت أي وسم كان بما أنه غير مُدرَج. لا يُسمح بالبحث إلّا عن التبويقات العمومية عن طريق الوسوم.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "الإبلاغ عن خلل",
"follow_request.authorize": "ترخيص",
"follow_request.reject": "رفض",
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
"getting_started.developers": "المُطوِّرون",
"getting_started.directory": "دليل الصفحات التعريفية",
"getting_started.documentation": "الدليل",

+ 3
- 0
app/javascript/mastodon/locales/ast.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Amosar axustes",
"column_header.unpin": "Desfixar",
"column_subheading.settings": "Axustes",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Namái multimedia",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Esti barritu namái va unviase a los usuarios mentaos.",
"compose_form.direct_message_warning_learn_more": "Learn more",
"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.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Report issue",
"follow_request.authorize": "Autorizar",
"follow_request.reject": "Refugar",
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
"getting_started.developers": "Desendolcadores",
"getting_started.directory": "Direutoriu de perfiles",
"getting_started.documentation": "Documentación",

+ 3
- 0
app/javascript/mastodon/locales/bg.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Show settings",
"column_header.unpin": "Unpin",
"column_subheading.settings": "Settings",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Media only",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
"compose_form.direct_message_warning_learn_more": "Learn more",
"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.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Report issue",
"follow_request.authorize": "Authorize",
"follow_request.reject": "Reject",
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
"getting_started.developers": "Developers",
"getting_started.directory": "Profile directory",
"getting_started.documentation": "Documentation",

+ 3
- 0
app/javascript/mastodon/locales/bn.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "সেটিং দেখান",
"column_header.unpin": "পিন খুলুন",
"column_subheading.settings": "সেটিং",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "শুধুমাত্র ছবি বা ভিডিও",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "শুধুমাত্র যাদেরকে উল্লেখ করা হয়েছে তাদেরকেই এই টুটটি পাঠানো হবে ।",
"compose_form.direct_message_warning_learn_more": "আরো জানুন",
"compose_form.hashtag_warning": "কোনো হ্যাশট্যাগের ভেতরে এই টুটটি থাকবেনা কারণ এটি তালিকাবহির্ভূত। শুধুমাত্র প্রকাশ্য ঠোটগুলো হ্যাশট্যাগের ভেতরে খুঁজে পাওয়া যাবে।",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "সমস্যার প্রতিবেদন করুন",
"follow_request.authorize": "অনুমতি দিন",
"follow_request.reject": "প্রত্যাখ্যান করুন",
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
"getting_started.developers": "তৈরিকারকদের জন্য",
"getting_started.directory": "নিজস্ব-পাতাগুলির তালিকা",
"getting_started.documentation": "নথিপত্র",

+ 3
- 0
app/javascript/mastodon/locales/br.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Diskouez an arventennoù",
"column_header.unpin": "Dispilhennañ",
"column_subheading.settings": "Arventennoù",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Nemet Mediaoù",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "An toud-mañ a vo kaset nemet d'an implijer·ezed·ien meneget.",
"compose_form.direct_message_warning_learn_more": "Gouzout hiroc'h",
"compose_form.hashtag_warning": "Ne vo ket lakaet an toud-mañ er rolloù gerioù-klik dre mard eo anlistennet. N'eus nemet an toudoù foran a c'hall bezañ klasket dre c'her-klik.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Danevellañ ur fazi",
"follow_request.authorize": "Aotren",
"follow_request.reject": "Nac'hañ",
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
"getting_started.developers": "Diorroerien",
"getting_started.directory": "Roll ar profiloù",
"getting_started.documentation": "Teuliadur",

+ 3
- 0
app/javascript/mastodon/locales/ca.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Mostra la configuració",
"column_header.unpin": "No fixis",
"column_subheading.settings": "Configuració",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Només multimèdia",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Aquest tut només serà enviat als usuaris esmentats.",
"compose_form.direct_message_warning_learn_more": "Aprèn més",
"compose_form.hashtag_warning": "Aquesta tut no es mostrarà en cap etiqueta ja que no està llistat. Només els tuts públics poden ser cercats per etiqueta.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Informa d'un problema",
"follow_request.authorize": "Autoritzar",
"follow_request.reject": "Rebutjar",
"follow_requests.unlocked_explanation": "Tot i que el teu compte no està bloquejat, el personal de {domain} ha pensat que és possible que vulguis revisar les sol·licituds de seguiment d’aquests comptes de forma manual.",
"getting_started.developers": "Desenvolupadors",
"getting_started.directory": "Directori de perfils",
"getting_started.documentation": "Documentació",

+ 11
- 8
app/javascript/mastodon/locales/co.json View File

@@ -3,7 +3,7 @@
"account.badges.bot": "Bot",
"account.badges.group": "Gruppu",
"account.block": "Bluccà @{name}",
"account.block_domain": "Piattà tuttu da {domain}",
"account.block_domain": "Piattà u duminiu {domain}",
"account.blocked": "Bluccatu",
"account.cancel_follow_request": "Annullà a dumanda d'abbunamentu",
"account.direct": "Missaghju direttu @{name}",
@@ -74,7 +74,9 @@
"column_header.show_settings": "Mustrà i parametri",
"column_header.unpin": "Spuntarulà",
"column_subheading.settings": "Parametri",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Solu media",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Solu l'utilizatori mintuvati puderenu vede stu statutu.",
"compose_form.direct_message_warning_learn_more": "Amparà di più",
"compose_form.hashtag_warning": "Stu statutu ùn hè \"Micca listatu\" è ùn sarà micca listatu indè e circate da hashtag. Per esse vistu in quesse, u statutu deve esse \"Pubblicu\".",
@@ -100,15 +102,15 @@
"confirmations.block.confirm": "Bluccà",
"confirmations.block.message": "Site sicuru·a che vulete bluccà @{name}?",
"confirmations.delete.confirm": "Toglie",
"confirmations.delete.message": "Site sicuru·a che vulete supprime stu statutu?",
"confirmations.delete.message": "Site sicuru·a che vulete sguassà stu statutu?",
"confirmations.delete_list.confirm": "Toglie",
"confirmations.delete_list.message": "Site sicuru·a che vulete supprime sta lista?",
"confirmations.delete_list.message": "Site sicuru·a che vulete toglie sta lista?",
"confirmations.domain_block.confirm": "Piattà tuttu u duminiu",
"confirmations.domain_block.message": "Site sicuru·a che vulete piattà tuttu à {domain}? Saria forse abbastanza di bluccà ò piattà alcuni conti da quallà. Ùn viderete più nunda da quallà indè e linee pubbliche o e nutificazione. I vostri abbunati da stu duminiu saranu tolti.",
"confirmations.domain_block.message": "Site veramente sicuru·a che vulete piattà tuttu à {domain}? Saria forse abbastanza di bluccà ò piattà alcuni conti da quallà. Ùn viderete più nunda da quallà indè e linee pubbliche o e nutificazione. I vostri abbunati da stu duminiu saranu tolti.",
"confirmations.logout.confirm": "Scunnettassi",
"confirmations.logout.message": "Site sicuru·a che vulete scunnettà vi?",
"confirmations.mute.confirm": "Piattà",
"confirmations.mute.explanation": "Quessu hà da piattà i statuti da sta persona è i posti chì a mintuvanu, mà ellu·a puderà sempre vede i vostri statuti è siguità vi.",
"confirmations.mute.explanation": "Quessu hà da piattà i statuti da sta persona è i posti chì a mintuvanu, ma ellu·a puderà sempre vede i vostri statuti è siguità vi.",
"confirmations.mute.message": "Site sicuru·a che vulete piattà @{name}?",
"confirmations.redraft.confirm": "Sguassà è riscrive",
"confirmations.redraft.message": "Site sicuru·a chè vulete sguassà stu statutu è riscrivelu? I favuriti è spartere saranu persi, è e risposte diventeranu orfane.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Palisà prublemu",
"follow_request.authorize": "Auturizà",
"follow_request.reject": "Righjittà",
"follow_requests.unlocked_explanation": "U vostru contu ùn hè micca privatu, ma a squadra d'amministrazione di {domain} pensa chì e dumande d'abbunamentu di questi conti anu bisognu d'esse verificate manualmente.",
"getting_started.developers": "Sviluppatori",
"getting_started.directory": "Annuariu di i prufili",
"getting_started.documentation": "Ducumentazione",
@@ -205,7 +208,7 @@
"introduction.interactions.reply.text": "Pudete risponde à d'altre persone o a i vostri propii statuti, cio chì i ligarà indè una cunversazione.",
"introduction.welcome.action": "Andemu!",
"introduction.welcome.headline": "Primi passi",
"introduction.welcome.text": "Benvenutu·a indè u fediverse! In qualchi minuta, puderete diffonde missaghji è parlà à i vostri amichi nant'à una varietà maiò di servori. Mà quess'istanza, {domain}, hè speciale—ghjè induve hè uspitatu u vostru prufile, allora ricurdatevi di u so nome.",
"introduction.welcome.text": "Benvenutu·a indè u fediverse! In qualchi minuta, puderete diffonde missaghji è parlà à i vostri amichi nant'à una varietà maiò di servori. Ma quess'istanza, {domain}, hè speciale—ghjè induve hè uspitatu u vostru prufile, allora ricurdatevi di u so nome.",
"keyboard_shortcuts.back": "rivultà",
"keyboard_shortcuts.blocked": "per apre una lista d'utilizatori bluccati",
"keyboard_shortcuts.boost": "sparte",
@@ -245,7 +248,7 @@
"lightbox.view_context": "Vede u cuntestu",
"lists.account.add": "Aghjunghje à a lista",
"lists.account.remove": "Toglie di a lista",
"lists.delete": "Supprime a lista",
"lists.delete": "Toglie a lista",
"lists.edit": "Mudificà a lista",
"lists.edit.submit": "Cambià u titulu",
"lists.new.create": "Aghjunghje",
@@ -413,7 +416,7 @@
"trends.trending_now": "Tindenze d'avà",
"ui.beforeunload": "A bruttacopia sarà persa s'ellu hè chjosu Mastodon.",
"upload_area.title": "Drag & drop per caricà un fugliale",
"upload_button.label": "Aghjunghje un media (JPEG, PNG, GIF, WebM, MP4, MOV)",
"upload_button.label": "Aghjunghje un media ({formats})",
"upload_error.limit": "Limita di caricamentu di fugliali trapassata.",
"upload_error.poll": "Ùn si pò micca caricà fugliali cù i scandagli.",
"upload_form.audio_description": "Discrizzione per i ciochi",

+ 3
- 0
app/javascript/mastodon/locales/cs.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Zobrazit nastavení",
"column_header.unpin": "Odepnout",
"column_subheading.settings": "Nastavení",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Pouze média",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Tento toot bude odeslán pouze zmíněným uživatelům.",
"compose_form.direct_message_warning_learn_more": "Zjistit více",
"compose_form.hashtag_warning": "Tento toot nebude zobrazen pod žádným hashtagem, neboť je neuvedený. Pouze veřejné tooty mohou být vyhledány podle hashtagu.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Nahlásit problém",
"follow_request.authorize": "Autorizovat",
"follow_request.reject": "Odmítnout",
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
"getting_started.developers": "Vývojáři",
"getting_started.directory": "Adresář profilů",
"getting_started.documentation": "Dokumentace",

+ 3
- 0
app/javascript/mastodon/locales/cy.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Dangos gosodiadau",
"column_header.unpin": "Dadbinio",
"column_subheading.settings": "Gosodiadau",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Cyfryngau yn unig",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Mi fydd y tŵt hwn ond yn cael ei anfon at y defnyddwyr sy'n cael eu crybwyll.",
"compose_form.direct_message_warning_learn_more": "Dysgu mwy",
"compose_form.hashtag_warning": "Ni fydd y tŵt hwn wedi ei restru o dan unrhyw hashnod gan ei fod heb ei restru. Dim ond tŵtiau cyhoeddus gellid chwilota amdanynt drwy hashnod.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Rhoi gwybod am broblem",
"follow_request.authorize": "Caniatau",
"follow_request.reject": "Gwrthod",
"follow_requests.unlocked_explanation": "Er nid yw eich cyfrif wedi'i gloi, oedd y staff {domain} yn meddwl efallai hoffech adolygu ceisiadau dilyn o'r cyfrifau rhain wrth law.",
"getting_started.developers": "Datblygwyr",
"getting_started.directory": "Cyfeiriadur proffil",
"getting_started.documentation": "Dogfennaeth",

+ 5
- 2
app/javascript/mastodon/locales/da.json View File

@@ -1,7 +1,7 @@
{
"account.add_or_remove_from_list": "Tilføj eller fjern fra lister",
"account.badges.bot": "Robot",
"account.badges.group": "Group",
"account.badges.group": "Gruppe",
"account.block": "Bloker @{name}",
"account.block_domain": "Skjul alt fra {domain}",
"account.blocked": "Blokeret",
@@ -74,7 +74,9 @@
"column_header.show_settings": "Vis indstillinger",
"column_header.unpin": "Fastgør ikke længere",
"column_subheading.settings": "Indstillinger",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Kun medie",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Dette trut vil kun blive sendt til de nævnte brugere.",
"compose_form.direct_message_warning_learn_more": "Lær mere",
"compose_form.hashtag_warning": "Dette trut vil ikke blive vist under noget hashtag da det ikke er listet. Kun offentlige trut kan blive vist under søgninger med hashtags.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Rapportér problem",
"follow_request.authorize": "Godkend",
"follow_request.reject": "Afvis",
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
"getting_started.developers": "Udviklere",
"getting_started.directory": "Profilliste",
"getting_started.documentation": "Dokumentation",
@@ -335,7 +338,7 @@
"relative_time.just_now": "nu",
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"relative_time.today": "today",
"relative_time.today": "i dag",
"reply_indicator.cancel": "Annuller",
"report.forward": "Videresend til {target}",
"report.forward_hint": "Kontoen er fra en anden server. Vil du også sende en anonym kopi af anmeldelsen dertil?",

+ 3
- 0
app/javascript/mastodon/locales/de.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Einstellungen anzeigen",
"column_header.unpin": "Lösen",
"column_subheading.settings": "Einstellungen",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Nur Medien",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Dieser Beitrag wird nur für die erwähnten Nutzer sichtbar sein.",
"compose_form.direct_message_warning_learn_more": "Mehr erfahren",
"compose_form.hashtag_warning": "Dieser Beitrag wird nicht durch Hashtags entdeckbar sein, weil er ungelistet ist. Nur öffentliche Beiträge tauchen in Hashtag-Zeitleisten auf.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Problem melden",
"follow_request.authorize": "Erlauben",
"follow_request.reject": "Ablehnen",
"follow_requests.unlocked_explanation": "Auch wenn dein Konto nicht gesperrt ist, haben die Mitarbeiter von {domain} gedacht, dass es besser wäre den Follow manuell zu bestätigen.",
"getting_started.developers": "Entwickler",
"getting_started.directory": "Profilverzeichnis",
"getting_started.documentation": "Dokumentation",

+ 22
- 5
app/javascript/mastodon/locales/defaultMessages.json View File

@@ -471,6 +471,10 @@
},
{
"descriptors": [
{
"defaultMessage": "Show thread",
"id": "status.show_thread"
},
{
"defaultMessage": "Read more",
"id": "status.read_more"
@@ -499,10 +503,6 @@
{
"defaultMessage": "{name} boosted",
"id": "status.reblogged_by"
},
{
"defaultMessage": "Show thread",
"id": "status.show_thread"
}
],
"path": "app/javascript/mastodon/components/status.json"
@@ -1706,6 +1706,10 @@
{
"defaultMessage": "Include additional tags in this column",
"id": "hashtag.column_settings.tag_toggle"
},
{
"defaultMessage": "Local only",
"id": "community.column_settings.local_only"
}
],
"path": "app/javascript/mastodon/features/hashtag_timeline/components/column_settings.json"
@@ -2267,6 +2271,19 @@
],
"path": "app/javascript/mastodon/features/pinned_statuses/index.json"
},
{
"descriptors": [
{
"defaultMessage": "Media only",
"id": "community.column_settings.media_only"
},
{
"defaultMessage": "Remote only",
"id": "community.column_settings.remote_only"
}
],
"path": "app/javascript/mastodon/features/public_timeline/components/column_settings.json"
},
{
"descriptors": [
{
@@ -2965,4 +2982,4 @@
],
"path": "app/javascript/mastodon/features/video/index.json"
}
]
]

+ 3
- 0
app/javascript/mastodon/locales/el.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Εμφάνιση ρυθμίσεων",
"column_header.unpin": "Ξεκαρφίτσωμα",
"column_subheading.settings": "Ρυθμίσεις",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Μόνο πολυμέσα",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Αυτό το τουτ θα σταλεί μόνο στους αναφερόμενους χρήστες.",
"compose_form.direct_message_warning_learn_more": "Μάθετε περισσότερα",
"compose_form.hashtag_warning": "Αυτό το τουτ δεν θα εμφανίζεται κάτω από κανένα hashtag καθώς είναι αφανές. Μόνο τα δημόσια τουτ μπορούν να αναζητηθούν ανά hashtag.",
@@ -164,6 +166,7 @@
"errors.unexpected_crash.report_issue": "Αναφορά προβλήματος",
"follow_request.authorize": "Ενέκρινε",
"follow_request.reject": "Απέρριψε",
"follow_requests.unlocked_explanation": "Παρόλο που ο λογαριασμός σου δεν είναι κλειδωμένος, οι διαχειριστές του {domain} θεώρησαν πως ίσως να θέλεις να ελέγξεις χειροκίνητα αυτά τα αιτήματα ακολούθησης.",
"getting_started.developers": "Ανάπτυξη",
"getting_started.directory": "Κατάλογος λογαριασμών",
"getting_started.documentation": "Τεκμηρίωση",

+ 2
- 0
app/javascript/mastodon/locales/en.json View File

@@ -79,7 +79,9 @@
"column_subheading.lists": "Lists",
"column_subheading.navigation": "Navigation",
"column_subheading.settings": "Settings",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Media Only",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "This toot will only be sent to the mentioned users.",
"compose_form.direct_message_warning_learn_more": "Learn more",
"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.",

+ 34
- 31
app/javascript/mastodon/locales/eo.json View File

@@ -74,7 +74,9 @@
"column_header.show_settings": "Montri agordojn",
"column_header.unpin": "Depingli",
"column_subheading.settings": "Agordado",
"community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Nur aŭdovidaĵoj",
"community.column_settings.remote_only": "Remote only",
"compose_form.direct_message_warning": "Tiu mesaĝo estos sendita nur al menciitaj uzantoj.",
"compose_form.direct_message_warning_learn_more": "Lerni pli",
"compose_form.hashtag_warning": "Ĉi tiu mesaĝo ne estos listigita per ajna kradvorto. Nur publikaj mesaĝoj estas serĉeblaj per kradvortoj.",
@@ -158,12 +160,13 @@
"empty_column.mutes": "Vi ne ankoraŭ silentigis iun uzanton.",
"empty_column.notifications": "Vi ankoraŭ ne havas sciigojn. Interagu kun aliaj por komenci konversacion.",
"empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj serviloj por plenigi la publikan tempolinion",
"error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.",
"error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
"error.unexpected_crash.explanation": "Pro eraro en nia kodo, aŭ problemo de kongruo en via retumilo, ĉi tiu paĝo ne povis esti montrata ĝuste.",
"error.unexpected_crash.next_steps": "Provu refreŝigi la paĝon. Se tio ne helpas, vi ankoraŭ povus uzi Mastodon per malsama retumilo aŭ operaciuma aplikajo.",
"errors.unexpected_crash.copy_stacktrace": "Kopii stakspuron en tondujo",
"errors.unexpected_crash.report_issue": "Raporti problemon",
"follow_request.authorize": "Rajtigi",
"follow_request.reject": "Rifuzi",
"follow_requests.unlocked_explanation": "137/5000\nKvankam via konto ne estas ŝlosita, la dungitaro de {domain} opiniis, ke vi eble volus revizii petojn de sekvadon el ĉi tiuj kontoj permane.",
"getting_started.developers": "Programistoj",
"getting_started.directory": "Profilujo",
"getting_started.documentation": "Dokumentado",
@@ -206,39 +209,39 @@
"introduction.welcome.action": "Ek!",
"introduction.welcome.headline": "Unuaj paŝoj",
"introduction.welcome.text": "Bonvenon en Fediverse! Tre baldaŭ, vi povos disdoni mesaĝojn kaj paroli al viaj amikoj tra granda servila diverseco. Sed ĉi tiu servilo, {domain}, estas speciala: ĝi enhavas vian profilon, do memoru ĝian nomon.",
"keyboard_shortcuts.back": "por reveni",
"keyboard_shortcuts.blocked": "por malfermi la liston de blokitaj uzantoj",
"keyboard_shortcuts.boost": "por diskonigi",
"keyboard_shortcuts.column": "por fokusigi mesaĝon en unu el la kolumnoj",
"keyboard_shortcuts.compose": "por fokusigi la tekstujon",
"keyboard_shortcuts.back": "reveni",
"keyboard_shortcuts.blocked": "malfermi la liston de blokitaj uzantoj",
"keyboard_shortcuts.boost": "diskonigi",
"keyboard_shortcuts.column": "fokusi mesaĝon en unu el la kolumnoj",
"keyboard_shortcuts.compose": "enfokusigi la tekstujon",
"keyboard_shortcuts.description": "Priskribo",
"keyboard_shortcuts.direct": "por malfermi la kolumnon de rektaj mesaĝoj",
"keyboard_shortcuts.down": "por iri suben en la listo",