Compare commits
117 Commits
c83c30d96d
...
34ab5fd793
Author | SHA1 | Date |
---|---|---|
ida schmidt | 34ab5fd793 | |
ida schmidt | 26eded41dd | |
Eugen Rochko | fd868f8ca0 | |
Claire | 4cd33a2c71 | |
Claire | f264cca1d2 | |
Claire | 5e4b04de88 | |
Claire | 3c18311d86 | |
Claire | e5113a8cad | |
Claire | 22cd1e6ab5 | |
Claire | e65ede1ac5 | |
Takeshi Umeda | 1bcb3daf7e | |
Claire | 9c610ca0a4 | |
Claire | 77d0297313 | |
Eugen Rochko | 4b6668868e | |
Eugen Rochko | 5c47a18c8d | |
Eugen Rochko | 8a74d851d2 | |
Claire | 76c2028859 | |
Claire | 3251b8eead | |
Claire | f60bb0784f | |
Claire | c3a6f7b941 | |
Claire | 986397b3a2 | |
Claire | c79d4711e9 | |
Claire | be56033715 | |
Claire | 8815e98aa2 | |
Claire | 4bc1fde105 | |
Claire | 34ab4111a7 | |
Claire | aebcb722aa | |
Claire | 9a468c895b | |
Claire | a1e5ff04e3 | |
Claire | e40d5414cc | |
Claire | 40eaa8706b | |
Claire | 4cc7efcb08 | |
Claire | 9b34647c9b | |
Eugen Rochko | 6b98fd0b4f | |
Claire | c7f534ab95 | |
Eugen Rochko | d5a50e9dfb | |
Jeong Arm | e1cf8d4d37 | |
Jeong Arm | f366a23a23 | |
Claire | aa828aea02 | |
Claire | 123a88b6b5 | |
Claire | e63370db19 | |
Claire | 2396c9061a | |
Holger | 663b58aaae | |
Claire | 75441ac63d | |
Claire | 5899fe70b6 | |
Claire | 2688f18d06 | |
Claire | f51c6cba1f | |
Claire | 4f852448e1 | |
Takeshi Umeda | c02d6c46e3 | |
Takeshi Umeda | 987f945930 | |
Claire | e62f488be5 | |
Eugen Rochko | d6486c969f | |
Eugen Rochko | 2fba280353 | |
Eugen Rochko | 1410dffdf4 | |
Claire | 11d3c065a5 | |
Claire | 526332c545 | |
Claire | be8079f637 | |
Claire | abf4c2ab21 | |
Eugen Rochko | abd7b4636a | |
dependabot[bot] | aafac8dc71 | |
dependabot[bot] | ea837e4a46 | |
dependabot[bot] | 0c64bb45e5 | |
dependabot[bot] | 19ac24ad85 | |
dependabot[bot] | 121d15f11c | |
dependabot[bot] | 75bf15212b | |
dependabot[bot] | 4c41171c54 | |
dependabot[bot] | 1654bea0ab | |
koyu | 8be1ec13ae | |
Claire | 3b27b09acb | |
Jeong Arm | 5ef216d032 | |
dependabot[bot] | f173275e33 | |
dependabot[bot] | f18e3caa1a | |
dependabot[bot] | cf08a595af | |
dependabot[bot] | 30cdedfa6e | |
dependabot[bot] | 2efe711a51 | |
dependabot[bot] | 8fb9dfa9cd | |
dependabot[bot] | a2aa51b7e4 | |
dependabot[bot] | fa1ce2a6cd | |
dependabot[bot] | 2e759b9c10 | |
dependabot[bot] | 1af1d4d015 | |
dependabot[bot] | 01dfa58aab | |
dependabot[bot] | 800a6c4424 | |
dependabot[bot] | 0783ec18c1 | |
dependabot[bot] | 9e6a9e0774 | |
dependabot[bot] | a581da059e | |
dependabot[bot] | f4caad0b6b | |
Valentin Lorentz | 16b524feb6 | |
Mélanie Chauvel | fd5ab80eed | |
Claire | b715cede4d | |
Jeong Arm | fcdae10072 | |
Claire | 12f8f39e25 | |
Yamagishi Kazutoshi | 1db28332b5 | |
dependabot[bot] | 92a9fcf0e1 | |
dependabot[bot] | d237dd9204 | |
dependabot[bot] | 649118714e | |
dependabot[bot] | d8ac96bd39 | |
dependabot[bot] | dc86f709e3 | |
dependabot[bot] | e6265336b6 | |
dependabot[bot] | 8ce6cc8bf9 | |
dependabot[bot] | 0bfb1fecd1 | |
dependabot[bot] | a6b0f0ac83 | |
dependabot[bot] | 6a9389fab8 | |
dependabot[bot] | 3012a12f02 | |
dependabot[bot] | 126e51e71e | |
dependabot[bot] | 6d491d0bba | |
dependabot[bot] | 970f59738f | |
dependabot[bot] | 85f5689a49 | |
Eugen Rochko | 01adffdecb | |
Eugen Rochko | 7f0d58b478 | |
Claire | 9a19227f17 | |
Zero King | 028ba13eb3 | |
Claire | 92f1d739b5 | |
Zero King | 689974b1ed | |
dependabot[bot] | 08d6a7764f | |
dependabot[bot] | 3f599c3433 | |
Claire | 97539b6a96 | |
Mélanie Chauvel | d137d2ab87 |
|
@ -216,24 +216,6 @@ jobs:
|
||||||
name: Run jest
|
name: Run jest
|
||||||
command: yarn test:jest
|
command: yarn test:jest
|
||||||
|
|
||||||
check-i18n:
|
|
||||||
<<: *defaults
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *install_system_dependencies
|
|
||||||
- run:
|
|
||||||
name: Check locale file normalization
|
|
||||||
command: bundle exec i18n-tasks check-normalized
|
|
||||||
- run:
|
|
||||||
name: Check for unused strings
|
|
||||||
command: bundle exec i18n-tasks unused -l en
|
|
||||||
- run:
|
|
||||||
name: Check for wrong string interpolations
|
|
||||||
command: bundle exec i18n-tasks check-consistent-interpolations
|
|
||||||
- run:
|
|
||||||
name: Check that all required locale files exist
|
|
||||||
command: bundle exec rake repo:check_locales_files
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build-and-test:
|
build-and-test:
|
||||||
|
@ -271,6 +253,3 @@ workflows:
|
||||||
- test-webui:
|
- test-webui:
|
||||||
requires:
|
requires:
|
||||||
- install
|
- install
|
||||||
- check-i18n:
|
|
||||||
requires:
|
|
||||||
- install-ruby2.7
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
.bundle
|
.bundle
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
.git
|
||||||
|
.gitattributes
|
||||||
|
.gitignore
|
||||||
|
.github
|
||||||
public/system
|
public/system
|
||||||
public/assets
|
public/assets
|
||||||
public/packs
|
public/packs
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
# not demonstrate all available configuration options. Please look at
|
# not demonstrate all available configuration options. Please look at
|
||||||
# https://docs.joinmastodon.org/admin/config/ for the full documentation.
|
# https://docs.joinmastodon.org/admin/config/ for the full documentation.
|
||||||
|
|
||||||
|
# Note that this file accepts slightly different syntax depending on whether
|
||||||
|
# you are using `docker-compose` or not. In particular, if you use
|
||||||
|
# `docker-compose`, the value of each declared variable will be taken verbatim,
|
||||||
|
# including surrounding quotes.
|
||||||
|
# See: https://github.com/mastodon/mastodon/issues/16895
|
||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
# ----------
|
# ----------
|
||||||
# This identifies your server and cannot be changed safely later
|
# This identifies your server and cannot be changed safely later
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# CODEOWNERS for tootsuite/mastodon
|
# CODEOWNERS for mastodon/mastodon
|
||||||
|
|
||||||
# Translators
|
# Translators
|
||||||
# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address.
|
# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address.
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: Check i18n
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
RAILS_ENV: test
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-i18n:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: '2.7'
|
||||||
|
bundler-cache: true
|
||||||
|
- name: Check locale file normalization
|
||||||
|
run: bundle exec i18n-tasks check-normalized
|
||||||
|
- name: Check for unused strings
|
||||||
|
run: bundle exec i18n-tasks unused -l en
|
||||||
|
- name: Check for wrong string interpolations
|
||||||
|
run: bundle exec i18n-tasks check-consistent-interpolations
|
||||||
|
- name: Check that all required locale files exist
|
||||||
|
run: bundle exec rake repo:check_locales_files
|
|
@ -1,7 +1,7 @@
|
||||||
Authors
|
Authors
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
Mastodon is available on [GitHub](https://github.com/mastodon/mastodon)
|
||||||
and provided thanks to the work of the following contributors:
|
and provided thanks to the work of the following contributors:
|
||||||
|
|
||||||
* [Gargron](https://github.com/Gargron)
|
* [Gargron](https://github.com/Gargron)
|
||||||
|
@ -719,7 +719,7 @@ and provided thanks to the work of the following contributors:
|
||||||
* [西小倉宏信](mailto:nishiko@mindia.jp)
|
* [西小倉宏信](mailto:nishiko@mindia.jp)
|
||||||
* [雨宮美羽](mailto:k737566@gmail.com)
|
* [雨宮美羽](mailto:k737566@gmail.com)
|
||||||
|
|
||||||
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead.
|
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/mastodon/mastodon/graphs/contributors) instead.
|
||||||
|
|
||||||
## Translators
|
## Translators
|
||||||
|
|
||||||
|
|
2710
CHANGELOG.md
2710
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
|
@ -14,7 +14,7 @@ If your contributions are accepted into Mastodon, you can request to be paid thr
|
||||||
|
|
||||||
## Bug reports
|
## Bug reports
|
||||||
|
|
||||||
Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected.
|
Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected.
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
|
@ -44,4 +44,4 @@ It is not always possible to phrase every change in such a manner, but it is des
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to tootsuite/documentation](https://github.com/tootsuite/documentation).
|
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation).
|
||||||
|
|
14
Gemfile
14
Gemfile
|
@ -17,7 +17,7 @@ gem 'makara', '~> 0.5'
|
||||||
gem 'pghero', '~> 2.8'
|
gem 'pghero', '~> 2.8'
|
||||||
gem 'dotenv-rails', '~> 2.7'
|
gem 'dotenv-rails', '~> 2.7'
|
||||||
|
|
||||||
gem 'aws-sdk-s3', '~> 1.94', require: false
|
gem 'aws-sdk-s3', '~> 1.95', require: false
|
||||||
gem 'fog-core', '<= 2.1.0'
|
gem 'fog-core', '<= 2.1.0'
|
||||||
gem 'fog-openstack', '~> 0.3', require: false
|
gem 'fog-openstack', '~> 0.3', require: false
|
||||||
gem 'paperclip', '~> 6.0'
|
gem 'paperclip', '~> 6.0'
|
||||||
|
@ -55,7 +55,7 @@ gem 'redis-namespace', '~> 1.8'
|
||||||
gem 'htmlentities', '~> 4.3'
|
gem 'htmlentities', '~> 4.3'
|
||||||
gem 'http', '~> 4.4'
|
gem 'http', '~> 4.4'
|
||||||
gem 'http_accept_language', '~> 2.1'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
gem 'httplog', '~> 1.4.3'
|
gem 'httplog', '~> 1.5.0'
|
||||||
gem 'idn-ruby', require: 'idn'
|
gem 'idn-ruby', require: 'idn'
|
||||||
gem 'kaminari', '~> 1.2'
|
gem 'kaminari', '~> 1.2'
|
||||||
gem 'link_header', '~> 0.0'
|
gem 'link_header', '~> 0.0'
|
||||||
|
@ -83,7 +83,7 @@ gem 'sidekiq', '~> 6.2'
|
||||||
gem 'sidekiq-scheduler', '~> 3.0'
|
gem 'sidekiq-scheduler', '~> 3.0'
|
||||||
gem 'sidekiq-unique-jobs', '~> 7.0'
|
gem 'sidekiq-unique-jobs', '~> 7.0'
|
||||||
gem 'sidekiq-bulk', '~>0.2.0'
|
gem 'sidekiq-bulk', '~>0.2.0'
|
||||||
gem 'simple-navigation', '~> 4.1'
|
gem 'simple-navigation', '~> 4.3'
|
||||||
gem 'simple_form', '~> 5.1'
|
gem 'simple_form', '~> 5.1'
|
||||||
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||||
gem 'stoplight', '~> 2.2.1'
|
gem 'stoplight', '~> 2.2.1'
|
||||||
|
@ -91,7 +91,7 @@ gem 'strong_migrations', '~> 0.7'
|
||||||
gem 'tty-prompt', '~> 0.23', require: false
|
gem 'tty-prompt', '~> 0.23', require: false
|
||||||
gem 'twitter-text', '~> 3.1.0'
|
gem 'twitter-text', '~> 3.1.0'
|
||||||
gem 'tzinfo-data', '~> 1.2021'
|
gem 'tzinfo-data', '~> 1.2021'
|
||||||
gem 'webpacker', '~> 5.3'
|
gem 'webpacker', '~> 5.4'
|
||||||
gem 'webpush', '~> 0.3'
|
gem 'webpush', '~> 0.3'
|
||||||
gem 'webauthn', '~> 3.0.0.alpha1'
|
gem 'webauthn', '~> 3.0.0.alpha1'
|
||||||
|
|
||||||
|
@ -115,12 +115,12 @@ end
|
||||||
group :test do
|
group :test do
|
||||||
gem 'capybara', '~> 3.35'
|
gem 'capybara', '~> 3.35'
|
||||||
gem 'climate_control', '~> 0.2'
|
gem 'climate_control', '~> 0.2'
|
||||||
gem 'faker', '~> 2.17'
|
gem 'faker', '~> 2.18'
|
||||||
gem 'microformats', '~> 4.2'
|
gem 'microformats', '~> 4.2'
|
||||||
gem 'rails-controller-testing', '~> 1.0'
|
gem 'rails-controller-testing', '~> 1.0'
|
||||||
gem 'rspec-sidekiq', '~> 3.1'
|
gem 'rspec-sidekiq', '~> 3.1'
|
||||||
gem 'simplecov', '~> 0.21', require: false
|
gem 'simplecov', '~> 0.21', require: false
|
||||||
gem 'webmock', '~> 3.12'
|
gem 'webmock', '~> 3.13'
|
||||||
gem 'parallel_tests', '~> 3.7'
|
gem 'parallel_tests', '~> 3.7'
|
||||||
gem 'rspec_junit_formatter', '~> 0.4'
|
gem 'rspec_junit_formatter', '~> 0.4'
|
||||||
end
|
end
|
||||||
|
@ -134,7 +134,7 @@ group :development do
|
||||||
gem 'letter_opener', '~> 1.7'
|
gem 'letter_opener', '~> 1.7'
|
||||||
gem 'letter_opener_web', '~> 1.4'
|
gem 'letter_opener_web', '~> 1.4'
|
||||||
gem 'memory_profiler'
|
gem 'memory_profiler'
|
||||||
gem 'rubocop', '~> 1.14', require: false
|
gem 'rubocop', '~> 1.15', require: false
|
||||||
gem 'rubocop-rails', '~> 2.10', require: false
|
gem 'rubocop-rails', '~> 2.10', require: false
|
||||||
gem 'brakeman', '~> 5.0', require: false
|
gem 'brakeman', '~> 5.0', require: false
|
||||||
gem 'bundler-audit', '~> 0.8', require: false
|
gem 'bundler-audit', '~> 0.8', require: false
|
||||||
|
|
44
Gemfile.lock
44
Gemfile.lock
|
@ -79,7 +79,7 @@ GEM
|
||||||
encryptor (~> 3.0.0)
|
encryptor (~> 3.0.0)
|
||||||
awrence (1.1.1)
|
awrence (1.1.1)
|
||||||
aws-eventstream (1.1.1)
|
aws-eventstream (1.1.1)
|
||||||
aws-partitions (1.452.0)
|
aws-partitions (1.465.0)
|
||||||
aws-sdk-core (3.114.0)
|
aws-sdk-core (3.114.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
aws-partitions (~> 1, >= 1.239.0)
|
aws-partitions (~> 1, >= 1.239.0)
|
||||||
|
@ -88,7 +88,7 @@ GEM
|
||||||
aws-sdk-kms (1.43.0)
|
aws-sdk-kms (1.43.0)
|
||||||
aws-sdk-core (~> 3, >= 3.112.0)
|
aws-sdk-core (~> 3, >= 3.112.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.94.1)
|
aws-sdk-s3 (1.95.1)
|
||||||
aws-sdk-core (~> 3, >= 3.112.0)
|
aws-sdk-core (~> 3, >= 3.112.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
|
@ -211,7 +211,7 @@ GEM
|
||||||
tzinfo
|
tzinfo
|
||||||
excon (0.76.0)
|
excon (0.76.0)
|
||||||
fabrication (2.22.0)
|
fabrication (2.22.0)
|
||||||
faker (2.17.0)
|
faker (2.18.0)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
faraday (1.3.0)
|
faraday (1.3.0)
|
||||||
faraday-net_http (~> 1.0)
|
faraday-net_http (~> 1.0)
|
||||||
|
@ -273,7 +273,7 @@ GEM
|
||||||
http-parser (1.2.1)
|
http-parser (1.2.1)
|
||||||
ffi-compiler (>= 1.0, < 2.0)
|
ffi-compiler (>= 1.0, < 2.0)
|
||||||
http_accept_language (2.1.1)
|
http_accept_language (2.1.1)
|
||||||
httplog (1.4.3)
|
httplog (1.5.0)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rainbow (>= 2.0.0)
|
rainbow (>= 2.0.0)
|
||||||
i18n (1.8.10)
|
i18n (1.8.10)
|
||||||
|
@ -354,17 +354,22 @@ GEM
|
||||||
nokogiri (~> 1)
|
nokogiri (~> 1)
|
||||||
rake
|
rake
|
||||||
mini_mime (1.0.3)
|
mini_mime (1.0.3)
|
||||||
mini_portile2 (2.5.1)
|
mini_portile2 (2.5.2)
|
||||||
|
net-ftp (~> 0.1)
|
||||||
minitest (5.14.4)
|
minitest (5.14.4)
|
||||||
msgpack (1.4.2)
|
msgpack (1.4.2)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
|
net-ftp (0.1.2)
|
||||||
|
net-protocol
|
||||||
|
time
|
||||||
net-ldap (0.17.0)
|
net-ldap (0.17.0)
|
||||||
|
net-protocol (0.1.0)
|
||||||
net-scp (3.0.0)
|
net-scp (3.0.0)
|
||||||
net-ssh (>= 2.6.5, < 7.0.0)
|
net-ssh (>= 2.6.5, < 7.0.0)
|
||||||
net-ssh (6.1.0)
|
net-ssh (6.1.0)
|
||||||
nio4r (2.5.7)
|
nio4r (2.5.7)
|
||||||
nokogiri (1.11.3)
|
nokogiri (1.11.6)
|
||||||
mini_portile2 (~> 2.5.0)
|
mini_portile2 (~> 2.5.0)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogumbo (2.0.4)
|
nokogumbo (2.0.4)
|
||||||
|
@ -428,7 +433,7 @@ GEM
|
||||||
pry-rails (0.3.9)
|
pry-rails (0.3.9)
|
||||||
pry (>= 0.10.4)
|
pry (>= 0.10.4)
|
||||||
public_suffix (4.0.6)
|
public_suffix (4.0.6)
|
||||||
puma (5.3.0)
|
puma (5.3.2)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
pundit (2.1.0)
|
pundit (2.1.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
|
@ -524,7 +529,7 @@ GEM
|
||||||
rspec-support (3.10.2)
|
rspec-support (3.10.2)
|
||||||
rspec_junit_formatter (0.4.1)
|
rspec_junit_formatter (0.4.1)
|
||||||
rspec-core (>= 2, < 4, != 2.12.0)
|
rspec-core (>= 2, < 4, != 2.12.0)
|
||||||
rubocop (1.14.0)
|
rubocop (1.15.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.0.0.0)
|
parser (>= 3.0.0.0)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
@ -569,12 +574,12 @@ GEM
|
||||||
sidekiq (>= 3)
|
sidekiq (>= 3)
|
||||||
thwait
|
thwait
|
||||||
tilt (>= 1.4.0)
|
tilt (>= 1.4.0)
|
||||||
sidekiq-unique-jobs (7.0.9)
|
sidekiq-unique-jobs (7.0.11)
|
||||||
brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
|
brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||||
sidekiq (>= 5.0, < 7.0)
|
sidekiq (>= 5.0, < 7.0)
|
||||||
thor (>= 0.20, < 2.0)
|
thor (>= 0.20, < 2.0)
|
||||||
simple-navigation (4.1.0)
|
simple-navigation (4.3.0)
|
||||||
activesupport (>= 2.3.2)
|
activesupport (>= 2.3.2)
|
||||||
simple_form (5.1.0)
|
simple_form (5.1.0)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
|
@ -609,6 +614,7 @@ GEM
|
||||||
thwait (0.2.0)
|
thwait (0.2.0)
|
||||||
e2mmap
|
e2mmap
|
||||||
tilt (2.0.10)
|
tilt (2.0.10)
|
||||||
|
time (0.1.0)
|
||||||
tpm-key_attestation (0.9.0)
|
tpm-key_attestation (0.9.0)
|
||||||
bindata (~> 2.4)
|
bindata (~> 2.4)
|
||||||
openssl-signature_algorithm (~> 0.4.0)
|
openssl-signature_algorithm (~> 0.4.0)
|
||||||
|
@ -646,11 +652,11 @@ GEM
|
||||||
safety_net_attestation (~> 0.4.0)
|
safety_net_attestation (~> 0.4.0)
|
||||||
securecompare (~> 1.0)
|
securecompare (~> 1.0)
|
||||||
tpm-key_attestation (~> 0.9.0)
|
tpm-key_attestation (~> 0.9.0)
|
||||||
webmock (3.12.2)
|
webmock (3.13.0)
|
||||||
addressable (>= 2.3.6)
|
addressable (>= 2.3.6)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
webpacker (5.3.0)
|
webpacker (5.4.0)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
rack-proxy (>= 0.6.1)
|
rack-proxy (>= 0.6.1)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
|
@ -675,7 +681,7 @@ DEPENDENCIES
|
||||||
active_record_query_trace (~> 1.8)
|
active_record_query_trace (~> 1.8)
|
||||||
addressable (~> 2.7)
|
addressable (~> 2.7)
|
||||||
annotate (~> 3.1)
|
annotate (~> 3.1)
|
||||||
aws-sdk-s3 (~> 1.94)
|
aws-sdk-s3 (~> 1.95)
|
||||||
better_errors (~> 2.9)
|
better_errors (~> 2.9)
|
||||||
binding_of_caller (~> 1.0)
|
binding_of_caller (~> 1.0)
|
||||||
blurhash (~> 0.1)
|
blurhash (~> 0.1)
|
||||||
|
@ -704,7 +710,7 @@ DEPENDENCIES
|
||||||
dotenv-rails (~> 2.7)
|
dotenv-rails (~> 2.7)
|
||||||
ed25519 (~> 1.2)
|
ed25519 (~> 1.2)
|
||||||
fabrication (~> 2.22)
|
fabrication (~> 2.22)
|
||||||
faker (~> 2.17)
|
faker (~> 2.18)
|
||||||
fast_blank (~> 1.0)
|
fast_blank (~> 1.0)
|
||||||
fastimage
|
fastimage
|
||||||
fog-core (<= 2.1.0)
|
fog-core (<= 2.1.0)
|
||||||
|
@ -715,7 +721,7 @@ DEPENDENCIES
|
||||||
htmlentities (~> 4.3)
|
htmlentities (~> 4.3)
|
||||||
http (~> 4.4)
|
http (~> 4.4)
|
||||||
http_accept_language (~> 2.1)
|
http_accept_language (~> 2.1)
|
||||||
httplog (~> 1.4.3)
|
httplog (~> 1.5.0)
|
||||||
i18n-tasks (~> 0.9)
|
i18n-tasks (~> 0.9)
|
||||||
idn-ruby
|
idn-ruby
|
||||||
iso-639
|
iso-639
|
||||||
|
@ -769,7 +775,7 @@ DEPENDENCIES
|
||||||
rspec-rails (~> 5.0)
|
rspec-rails (~> 5.0)
|
||||||
rspec-sidekiq (~> 3.1)
|
rspec-sidekiq (~> 3.1)
|
||||||
rspec_junit_formatter (~> 0.4)
|
rspec_junit_formatter (~> 0.4)
|
||||||
rubocop (~> 1.14)
|
rubocop (~> 1.15)
|
||||||
rubocop-rails (~> 2.10)
|
rubocop-rails (~> 2.10)
|
||||||
ruby-progressbar (~> 1.11)
|
ruby-progressbar (~> 1.11)
|
||||||
sanitize (~> 5.2)
|
sanitize (~> 5.2)
|
||||||
|
@ -778,7 +784,7 @@ DEPENDENCIES
|
||||||
sidekiq-bulk (~> 0.2.0)
|
sidekiq-bulk (~> 0.2.0)
|
||||||
sidekiq-scheduler (~> 3.0)
|
sidekiq-scheduler (~> 3.0)
|
||||||
sidekiq-unique-jobs (~> 7.0)
|
sidekiq-unique-jobs (~> 7.0)
|
||||||
simple-navigation (~> 4.1)
|
simple-navigation (~> 4.3)
|
||||||
simple_form (~> 5.1)
|
simple_form (~> 5.1)
|
||||||
simplecov (~> 0.21)
|
simplecov (~> 0.21)
|
||||||
sprockets (~> 3.7.2)
|
sprockets (~> 3.7.2)
|
||||||
|
@ -791,7 +797,7 @@ DEPENDENCIES
|
||||||
twitter-text (~> 3.1.0)
|
twitter-text (~> 3.1.0)
|
||||||
tzinfo-data (~> 1.2021)
|
tzinfo-data (~> 1.2021)
|
||||||
webauthn (~> 3.0.0.alpha1)
|
webauthn (~> 3.0.0.alpha1)
|
||||||
webmock (~> 3.12)
|
webmock (~> 3.13)
|
||||||
webpacker (~> 5.3)
|
webpacker (~> 5.4)
|
||||||
webpush (~> 0.3)
|
webpush (~> 0.3)
|
||||||
xorcist (~> 1.1)
|
xorcist (~> 1.1)
|
||||||
|
|
10
README.md
10
README.md
|
@ -1,14 +1,14 @@
|
||||||
![Mastodon](https://i.imgur.com/NhZc40l.png)
|
![Mastodon](https://i.imgur.com/NhZc40l.png)
|
||||||
========
|
========
|
||||||
|
|
||||||
[![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases]
|
[![GitHub release](https://img.shields.io/github/release/mastodon/mastodon.svg)][releases]
|
||||||
[![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci]
|
[![Build Status](https://img.shields.io/circleci/project/github/mastodon/mastodon.svg)][circleci]
|
||||||
[![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate]
|
[![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate]
|
||||||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
|
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
|
||||||
[![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker]
|
[![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker]
|
||||||
|
|
||||||
[releases]: https://github.com/tootsuite/mastodon/releases
|
[releases]: https://github.com/mastodon/mastodon/releases
|
||||||
[circleci]: https://circleci.com/gh/tootsuite/mastodon
|
[circleci]: https://circleci.com/gh/mastodon/mastodon
|
||||||
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
|
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
|
||||||
[crowdin]: https://crowdin.com/project/mastodon
|
[crowdin]: https://crowdin.com/project/mastodon
|
||||||
[docker]: https://hub.docker.com/r/tootsuite/mastodon/
|
[docker]: https://hub.docker.com/r/tootsuite/mastodon/
|
||||||
|
@ -82,7 +82,7 @@ Mastodon is **free, open-source software** licensed under **AGPLv3**.
|
||||||
|
|
||||||
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
||||||
|
|
||||||
**IRC channel**: #mastodon on irc.freenode.net
|
**IRC channel**: #mastodon on irc.libera.chat
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ sudo apt-get install \
|
||||||
# Install rvm
|
# Install rvm
|
||||||
read RUBY_VERSION < .ruby-version
|
read RUBY_VERSION < .ruby-version
|
||||||
|
|
||||||
gpg_command="gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
|
gpg_command="gpg --keyserver hkp://pgp.mit.edu --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
|
||||||
$($gpg_command)
|
$($gpg_command)
|
||||||
if [ $? -ne 0 ];then
|
if [ $? -ne 0 ];then
|
||||||
echo "GPG command failed, This prevented RVM from installing."
|
echo "GPG command failed, This prevented RVM from installing."
|
||||||
|
|
4
app.json
4
app.json
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "Mastodon",
|
"name": "Mastodon",
|
||||||
"description": "A GNU Social-compatible microblogging server",
|
"description": "A GNU Social-compatible microblogging server",
|
||||||
"repository": "https://github.com/tootsuite/mastodon",
|
"repository": "https://github.com/mastodon/mastodon",
|
||||||
"logo": "https://github.com/tootsuite.png",
|
"logo": "https://github.com/mastodon.png",
|
||||||
"env": {
|
"env": {
|
||||||
"HEROKU": {
|
"HEROKU": {
|
||||||
"description": "Leave this as true",
|
"description": "Leave this as true",
|
||||||
|
|
|
@ -19,11 +19,11 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
|
||||||
private
|
private
|
||||||
|
|
||||||
def uri_prefix
|
def uri_prefix
|
||||||
signed_request_account.uri[/http(s?):\/\/[^\/]+\//]
|
signed_request_account.uri[Account::URL_PREFIX_RE]
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_items
|
def set_items
|
||||||
@items = @account.followers.where(Account.arel_table[:uri].matches(uri_prefix + '%', false, true)).pluck(:uri)
|
@items = @account.followers.where(Account.arel_table[:uri].matches("#{Account.sanitize_sql_like(uri_prefix)}/%", false, true)).or(@account.followers.where(uri: uri_prefix)).pluck(:uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
def collection_presenter
|
def collection_presenter
|
||||||
|
|
|
@ -11,7 +11,11 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode? && !(signed_request_account.present? && page_requested?))
|
if page_requested?
|
||||||
|
expires_in(1.minute, public: public_fetch_mode? && signed_request_account.nil?)
|
||||||
|
else
|
||||||
|
expires_in(3.minutes, public: public_fetch_mode?)
|
||||||
|
end
|
||||||
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -29,7 +33,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
ActivityPub::CollectionPresenter.new(
|
ActivityPub::CollectionPresenter.new(
|
||||||
id: account_outbox_url(@account),
|
id: outbox_url,
|
||||||
type: :ordered,
|
type: :ordered,
|
||||||
size: @account.statuses_count,
|
size: @account.statuses_count,
|
||||||
first: outbox_url(page: true),
|
first: outbox_url(page: true),
|
||||||
|
@ -47,11 +51,11 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_page
|
def next_page
|
||||||
account_outbox_url(@account, page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT
|
outbox_url(page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT
|
||||||
end
|
end
|
||||||
|
|
||||||
def prev_page
|
def prev_page
|
||||||
account_outbox_url(@account, page: true, min_id: @statuses.first.id) unless @statuses.empty?
|
outbox_url(page: true, min_id: @statuses.first.id) unless @statuses.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_statuses
|
def set_statuses
|
||||||
|
@ -76,4 +80,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
||||||
def set_account
|
def set_account
|
||||||
@account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
|
@account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.headers['Vary'] = 'Signature' if authorized_fetch_mode? || page_requested?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Admin
|
||||||
@statuses = @account.statuses.where(visibility: [:public, :unlisted])
|
@statuses = @account.statuses.where(visibility: [:public, :unlisted])
|
||||||
|
|
||||||
if params[:media]
|
if params[:media]
|
||||||
@statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id))
|
@statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)).reorder('statuses.id desc')
|
||||||
end
|
end
|
||||||
|
|
||||||
@statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE)
|
@statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE)
|
||||||
|
|
|
@ -3,12 +3,11 @@
|
||||||
class Api::V1::Emails::ConfirmationsController < Api::BaseController
|
class Api::V1::Emails::ConfirmationsController < Api::BaseController
|
||||||
before_action :doorkeeper_authorize!
|
before_action :doorkeeper_authorize!
|
||||||
before_action :require_user_owned_by_application!
|
before_action :require_user_owned_by_application!
|
||||||
|
before_action :require_user_not_confirmed!
|
||||||
|
|
||||||
def create
|
def create
|
||||||
if !current_user.confirmed? && current_user.unconfirmed_email.present?
|
current_user.update!(email: params[:email]) if params.key?(:email)
|
||||||
current_user.update!(email: params[:email]) if params.key?(:email)
|
current_user.resend_confirmation_instructions
|
||||||
current_user.resend_confirmation_instructions
|
|
||||||
end
|
|
||||||
|
|
||||||
render_empty
|
render_empty
|
||||||
end
|
end
|
||||||
|
@ -18,4 +17,8 @@ class Api::V1::Emails::ConfirmationsController < Api::BaseController
|
||||||
def require_user_owned_by_application!
|
def require_user_owned_by_application!
|
||||||
render json: { error: 'This method is only available to the application the user originally signed-up with' }, status: :forbidden unless current_user && current_user.created_by_application_id == doorkeeper_token.application_id
|
render json: { error: 'This method is only available to the application the user originally signed-up with' }, status: :forbidden unless current_user && current_user.created_by_application_id == doorkeeper_token.application_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def require_user_not_confirmed!
|
||||||
|
render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden if current_user.confirmed? || current_user.unconfirmed_email.blank?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,6 @@ class Auth::PasswordsController < Devise::PasswordsController
|
||||||
super do |resource|
|
super do |resource|
|
||||||
if resource.errors.empty?
|
if resource.errors.empty?
|
||||||
resource.session_activations.destroy_all
|
resource.session_activations.destroy_all
|
||||||
resource.forget_me!
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Auth::RegistrationsController < Devise::RegistrationsController
|
class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
include Devise::Controllers::Rememberable
|
|
||||||
include RegistrationSpamConcern
|
include RegistrationSpamConcern
|
||||||
|
|
||||||
layout :determine_layout
|
layout :determine_layout
|
||||||
|
@ -30,8 +29,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
super do |resource|
|
super do |resource|
|
||||||
if resource.saved_change_to_encrypted_password?
|
if resource.saved_change_to_encrypted_password?
|
||||||
resource.clear_other_sessions(current_session.session_id)
|
resource.clear_other_sessions(current_session.session_id)
|
||||||
resource.forget_me!
|
|
||||||
remember_me(resource)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Auth::SessionsController < Devise::SessionsController
|
class Auth::SessionsController < Devise::SessionsController
|
||||||
include Devise::Controllers::Rememberable
|
|
||||||
|
|
||||||
layout 'auth'
|
layout 'auth'
|
||||||
|
|
||||||
skip_before_action :require_no_authentication, only: [:create]
|
skip_before_action :require_no_authentication, only: [:create]
|
||||||
|
@ -26,7 +24,6 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
def create
|
def create
|
||||||
super do |resource|
|
super do |resource|
|
||||||
resource.update_sign_in!(request, new_sign_in: true)
|
resource.update_sign_in!(request, new_sign_in: true)
|
||||||
remember_me(resource)
|
|
||||||
flash.delete(:notice)
|
flash.delete(:notice)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -40,7 +37,7 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
end
|
end
|
||||||
|
|
||||||
def webauthn_options
|
def webauthn_options
|
||||||
user = find_user
|
user = User.find_by(id: session[:attempt_user_id])
|
||||||
|
|
||||||
if user.webauthn_enabled?
|
if user.webauthn_enabled?
|
||||||
options_for_get = WebAuthn::Credential.options_for_get(
|
options_for_get = WebAuthn::Credential.options_for_get(
|
||||||
|
@ -58,16 +55,20 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def find_user
|
def find_user
|
||||||
if session[:attempt_user_id]
|
if user_params[:email].present?
|
||||||
|
find_user_from_params
|
||||||
|
elsif session[:attempt_user_id]
|
||||||
User.find_by(id: session[:attempt_user_id])
|
User.find_by(id: session[:attempt_user_id])
|
||||||
else
|
|
||||||
user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication
|
|
||||||
user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication
|
|
||||||
user ||= User.find_for_authentication(email: user_params[:email])
|
|
||||||
user
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_user_from_params
|
||||||
|
user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication
|
||||||
|
user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication
|
||||||
|
user ||= User.find_for_authentication(email: user_params[:email])
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
def user_params
|
def user_params
|
||||||
params.require(:user).permit(:email, :password, :otp_attempt, :sign_in_token_attempt, credential: {})
|
params.require(:user).permit(:email, :password, :otp_attempt, :sign_in_token_attempt, credential: {})
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,21 +16,24 @@ module SignInTokenAuthenticationConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_with_sign_in_token
|
def authenticate_with_sign_in_token
|
||||||
user = self.resource = find_user
|
if user_params[:email].present?
|
||||||
|
user = self.resource = find_user_from_params
|
||||||
|
prompt_for_sign_in_token(user) if user&.external_or_valid_password?(user_params[:password])
|
||||||
|
elsif session[:attempt_user_id]
|
||||||
|
user = self.resource = User.find_by(id: session[:attempt_user_id])
|
||||||
|
return if user.nil?
|
||||||
|
|
||||||
if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s
|
if session[:attempt_user_updated_at] != user.updated_at.to_s
|
||||||
restart_session
|
restart_session
|
||||||
elsif user_params.key?(:sign_in_token_attempt) && session[:attempt_user_id]
|
elsif user_params.key?(:sign_in_token_attempt)
|
||||||
authenticate_with_sign_in_token_attempt(user)
|
authenticate_with_sign_in_token_attempt(user)
|
||||||
elsif user.present? && user.external_or_valid_password?(user_params[:password])
|
end
|
||||||
prompt_for_sign_in_token(user)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_with_sign_in_token_attempt(user)
|
def authenticate_with_sign_in_token_attempt(user)
|
||||||
if valid_sign_in_token_attempt?(user)
|
if valid_sign_in_token_attempt?(user)
|
||||||
clear_attempt_from_session
|
clear_attempt_from_session
|
||||||
remember_me(user)
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
else
|
else
|
||||||
flash.now[:alert] = I18n.t('users.invalid_sign_in_token')
|
flash.now[:alert] = I18n.t('users.invalid_sign_in_token')
|
||||||
|
|
|
@ -35,16 +35,20 @@ module TwoFactorAuthenticationConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_with_two_factor
|
def authenticate_with_two_factor
|
||||||
user = self.resource = find_user
|
if user_params[:email].present?
|
||||||
|
user = self.resource = find_user_from_params
|
||||||
|
prompt_for_two_factor(user) if user&.external_or_valid_password?(user_params[:password])
|
||||||
|
elsif session[:attempt_user_id]
|
||||||
|
user = self.resource = User.find_by(id: session[:attempt_user_id])
|
||||||
|
return if user.nil?
|
||||||
|
|
||||||
if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s
|
if session[:attempt_user_updated_at] != user.updated_at.to_s
|
||||||
restart_session
|
restart_session
|
||||||
elsif user.webauthn_enabled? && user_params.key?(:credential) && session[:attempt_user_id]
|
elsif user.webauthn_enabled? && user_params.key?(:credential)
|
||||||
authenticate_with_two_factor_via_webauthn(user)
|
authenticate_with_two_factor_via_webauthn(user)
|
||||||
elsif user_params.key?(:otp_attempt) && session[:attempt_user_id]
|
elsif user_params.key?(:otp_attempt)
|
||||||
authenticate_with_two_factor_via_otp(user)
|
authenticate_with_two_factor_via_otp(user)
|
||||||
elsif user.present? && user.external_or_valid_password?(user_params[:password])
|
end
|
||||||
prompt_for_two_factor(user)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,7 +57,6 @@ module TwoFactorAuthenticationConcern
|
||||||
|
|
||||||
if valid_webauthn_credential?(user, webauthn_credential)
|
if valid_webauthn_credential?(user, webauthn_credential)
|
||||||
clear_attempt_from_session
|
clear_attempt_from_session
|
||||||
remember_me(user)
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
render json: { redirect_path: root_path }, status: :ok
|
render json: { redirect_path: root_path }, status: :ok
|
||||||
else
|
else
|
||||||
|
@ -64,7 +67,6 @@ module TwoFactorAuthenticationConcern
|
||||||
def authenticate_with_two_factor_via_otp(user)
|
def authenticate_with_two_factor_via_otp(user)
|
||||||
if valid_otp_attempt?(user)
|
if valid_otp_attempt?(user)
|
||||||
clear_attempt_from_session
|
clear_attempt_from_session
|
||||||
remember_me(user)
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
else
|
else
|
||||||
flash.now[:alert] = I18n.t('users.invalid_otp_token')
|
flash.now[:alert] = I18n.t('users.invalid_otp_token')
|
||||||
|
|
|
@ -3,11 +3,16 @@
|
||||||
class CustomCssController < ApplicationController
|
class CustomCssController < ApplicationController
|
||||||
skip_before_action :store_current_location
|
skip_before_action :store_current_location
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
skip_before_action :update_user_sign_in
|
||||||
|
skip_before_action :set_session_activity
|
||||||
|
|
||||||
|
skip_around_action :set_locale
|
||||||
|
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in 3.minutes, public: true
|
expires_in 3.minutes, public: true
|
||||||
|
request.session_options[:skip] = true
|
||||||
render plain: Setting.custom_css || '', content_type: 'text/css'
|
render plain: Setting.custom_css || '', content_type: 'text/css'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -85,7 +85,7 @@ class FollowerAccountsController < ApplicationController
|
||||||
if page_requested? || !@account.user_hides_network?
|
if page_requested? || !@account.user_hides_network?
|
||||||
# Return all fields
|
# Return all fields
|
||||||
else
|
else
|
||||||
%i(id type totalItems)
|
%i(id type total_items)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -85,7 +85,7 @@ class FollowingAccountsController < ApplicationController
|
||||||
if page_requested? || !@account.user_hides_network?
|
if page_requested? || !@account.user_hides_network?
|
||||||
# Return all fields
|
# Return all fields
|
||||||
else
|
else
|
||||||
%i(id type totalItems)
|
%i(id type total_items)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,7 +45,7 @@ class MediaProxyController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def lock_options
|
def lock_options
|
||||||
{ redis: Redis.current, key: "media_download:#{params[:id]}" }
|
{ redis: Redis.current, key: "media_download:#{params[:id]}", autorelease: 15.minutes.seconds }
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject_media?
|
def reject_media?
|
||||||
|
|
|
@ -42,7 +42,7 @@ class Settings::DeletesController < Settings::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_account!
|
def destroy_account!
|
||||||
current_account.suspend!(origin: :local)
|
current_account.suspend!(origin: :local, block_email: false)
|
||||||
AccountDeletionWorker.perform_async(current_user.account_id)
|
AccountDeletionWorker.perform_async(current_user.account_id)
|
||||||
sign_out
|
sign_out
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,6 @@ module WellKnown
|
||||||
class WebfingerController < ActionController::Base
|
class WebfingerController < ActionController::Base
|
||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
|
|
||||||
before_action { response.headers['Vary'] = 'Accept' }
|
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
before_action :check_account_suspension
|
before_action :check_account_suspension
|
||||||
|
|
||||||
|
@ -39,10 +38,12 @@ module WellKnown
|
||||||
end
|
end
|
||||||
|
|
||||||
def bad_request
|
def bad_request
|
||||||
|
expires_in(3.minutes, public: true)
|
||||||
head 400
|
head 400
|
||||||
end
|
end
|
||||||
|
|
||||||
def not_found
|
def not_found
|
||||||
|
expires_in(3.minutes, public: true)
|
||||||
head 404
|
head 404
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -80,17 +80,17 @@ module AccountsHelper
|
||||||
def account_description(account)
|
def account_description(account)
|
||||||
prepend_str = [
|
prepend_str = [
|
||||||
[
|
[
|
||||||
number_to_human(account.statuses_count, strip_insignificant_zeros: true),
|
number_to_human(account.statuses_count, precision: 3, strip_insignificant_zeros: true),
|
||||||
I18n.t('accounts.posts', count: account.statuses_count),
|
I18n.t('accounts.posts', count: account.statuses_count),
|
||||||
].join(' '),
|
].join(' '),
|
||||||
|
|
||||||
[
|
[
|
||||||
number_to_human(account.following_count, strip_insignificant_zeros: true),
|
number_to_human(account.following_count, precision: 3, strip_insignificant_zeros: true),
|
||||||
I18n.t('accounts.following', count: account.following_count),
|
I18n.t('accounts.following', count: account.following_count),
|
||||||
].join(' '),
|
].join(' '),
|
||||||
|
|
||||||
[
|
[
|
||||||
number_to_human(account.followers_count, strip_insignificant_zeros: true),
|
number_to_human(account.followers_count, precision: 3, strip_insignificant_zeros: true),
|
||||||
I18n.t('accounts.followers', count: account.followers_count),
|
I18n.t('accounts.followers', count: account.followers_count),
|
||||||
].join(' '),
|
].join(' '),
|
||||||
].join(', ')
|
].join(', ')
|
||||||
|
|
|
@ -14,6 +14,17 @@ module ApplicationHelper
|
||||||
ku
|
ku
|
||||||
).freeze
|
).freeze
|
||||||
|
|
||||||
|
def friendly_number_to_human(number, **options)
|
||||||
|
# By default, the number of precision digits used by number_to_human
|
||||||
|
# is looked up from the locales definition, and rails-i18n comes with
|
||||||
|
# values that don't seem to make much sense for many languages, so
|
||||||
|
# override these values with a default of 3 digits of precision.
|
||||||
|
options[:precision] = 3
|
||||||
|
options[:strip_insignificant_zeros] = true
|
||||||
|
|
||||||
|
number_to_human(number, **options)
|
||||||
|
end
|
||||||
|
|
||||||
def active_nav_class(*paths)
|
def active_nav_class(*paths)
|
||||||
paths.any? { |path| current_page?(path) } ? 'active' : ''
|
paths.any? { |path| current_page?(path) } ? 'active' : ''
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,7 @@ module SettingsHelper
|
||||||
en: 'English',
|
en: 'English',
|
||||||
eo: 'Esperanto',
|
eo: 'Esperanto',
|
||||||
'es-AR': 'Español (Argentina)',
|
'es-AR': 'Español (Argentina)',
|
||||||
|
'es-MX': 'Español (México)',
|
||||||
es: 'Español',
|
es: 'Español',
|
||||||
et: 'Eesti',
|
et: 'Eesti',
|
||||||
eu: 'Euskara',
|
eu: 'Euskara',
|
||||||
|
|
|
@ -22,13 +22,20 @@ export const PICTURE_IN_PICTURE_REMOVE = 'PICTURE_IN_PICTURE_REMOVE';
|
||||||
* @param {MediaProps} props
|
* @param {MediaProps} props
|
||||||
* @return {object}
|
* @return {object}
|
||||||
*/
|
*/
|
||||||
export const deployPictureInPicture = (statusId, accountId, playerType, props) => ({
|
export const deployPictureInPicture = (statusId, accountId, playerType, props) => {
|
||||||
type: PICTURE_IN_PICTURE_DEPLOY,
|
return (dispatch, getState) => {
|
||||||
statusId,
|
// Do not open a player for a toot that does not exist
|
||||||
accountId,
|
if (getState().hasIn(['statuses', statusId])) {
|
||||||
playerType,
|
dispatch({
|
||||||
props,
|
type: PICTURE_IN_PICTURE_DEPLOY,
|
||||||
});
|
statusId,
|
||||||
|
accountId,
|
||||||
|
playerType,
|
||||||
|
props,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @return {object}
|
* @return {object}
|
||||||
|
|
|
@ -93,7 +93,7 @@ export default class IntersectionObserverArticle extends React.Component {
|
||||||
// When the browser gets a chance, test if we're still not intersecting,
|
// When the browser gets a chance, test if we're still not intersecting,
|
||||||
// and if so, set our isHidden to true to trigger an unrender. The point of
|
// and if so, set our isHidden to true to trigger an unrender. The point of
|
||||||
// this is to save DOM nodes and avoid using up too much memory.
|
// this is to save DOM nodes and avoid using up too much memory.
|
||||||
// See: https://github.com/tootsuite/mastodon/issues/2900
|
// See: https://github.com/mastodon/mastodon/issues/2900
|
||||||
this.setState((prevState) => ({ isHidden: !prevState.isIntersecting }));
|
this.setState((prevState) => ({ isHidden: !prevState.isIntersecting }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import 'wicg-inert';
|
import 'wicg-inert';
|
||||||
|
import { createBrowserHistory } from 'history';
|
||||||
import { multiply } from 'color-blend';
|
import { multiply } from 'color-blend';
|
||||||
|
|
||||||
export default class ModalRoot extends React.PureComponent {
|
export default class ModalRoot extends React.PureComponent {
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
router: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
|
@ -48,6 +53,7 @@ export default class ModalRoot extends React.PureComponent {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
window.addEventListener('keyup', this.handleKeyUp, false);
|
window.addEventListener('keyup', this.handleKeyUp, false);
|
||||||
window.addEventListener('keydown', this.handleKeyDown, false);
|
window.addEventListener('keydown', this.handleKeyDown, false);
|
||||||
|
this.history = this.context.router ? this.context.router.history : createBrowserHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
|
@ -69,6 +75,14 @@ export default class ModalRoot extends React.PureComponent {
|
||||||
this.activeElement.focus({ preventScroll: true });
|
this.activeElement.focus({ preventScroll: true });
|
||||||
this.activeElement = null;
|
this.activeElement = null;
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
|
|
||||||
|
this._handleModalClose();
|
||||||
|
}
|
||||||
|
if (this.props.children && !prevProps.children) {
|
||||||
|
this._handleModalOpen();
|
||||||
|
}
|
||||||
|
if (this.props.children) {
|
||||||
|
this._ensureHistoryBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +91,32 @@ export default class ModalRoot extends React.PureComponent {
|
||||||
window.removeEventListener('keydown', this.handleKeyDown);
|
window.removeEventListener('keydown', this.handleKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleModalOpen () {
|
||||||
|
this._modalHistoryKey = Date.now();
|
||||||
|
this.unlistenHistory = this.history.listen((_, action) => {
|
||||||
|
if (action === 'POP') {
|
||||||
|
this.props.onClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleModalClose () {
|
||||||
|
if (this.unlistenHistory) {
|
||||||
|
this.unlistenHistory();
|
||||||
|
}
|
||||||
|
const { state } = this.history.location;
|
||||||
|
if (state && state.mastodonModalKey === this._modalHistoryKey) {
|
||||||
|
this.history.goBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ensureHistoryBuffer () {
|
||||||
|
const { pathname, state } = this.history.location;
|
||||||
|
if (!state || state.mastodonModalKey !== this._modalHistoryKey) {
|
||||||
|
this.history.push(pathname, { ...state, mastodonModalKey: this._modalHistoryKey });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getSiblings = () => {
|
getSiblings = () => {
|
||||||
return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
|
return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { ScrollContainer } from 'react-router-scroll-4';
|
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container';
|
import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container';
|
||||||
import LoadMore from './load_more';
|
import LoadMore from './load_more';
|
||||||
|
@ -34,7 +34,6 @@ class ScrollableList extends PureComponent {
|
||||||
onScrollToTop: PropTypes.func,
|
onScrollToTop: PropTypes.func,
|
||||||
onScroll: PropTypes.func,
|
onScroll: PropTypes.func,
|
||||||
trackScroll: PropTypes.bool,
|
trackScroll: PropTypes.bool,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
showLoading: PropTypes.bool,
|
showLoading: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
|
@ -290,7 +289,7 @@ class ScrollableList extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;
|
const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;
|
||||||
const { fullscreen } = this.state;
|
const { fullscreen } = this.state;
|
||||||
const childrenCount = React.Children.count(children);
|
const childrenCount = React.Children.count(children);
|
||||||
|
|
||||||
|
@ -356,7 +355,7 @@ class ScrollableList extends PureComponent {
|
||||||
|
|
||||||
if (trackScroll) {
|
if (trackScroll) {
|
||||||
return (
|
return (
|
||||||
<ScrollContainer scrollKey={scrollKey} shouldUpdateScroll={shouldUpdateScroll}>
|
<ScrollContainer scrollKey={scrollKey}>
|
||||||
{scrollableArea}
|
{scrollableArea}
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -309,8 +309,8 @@ class Status extends ImmutablePureComponent {
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={handlers}>
|
<HotKeys handlers={handlers}>
|
||||||
<div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex='0'>
|
<div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex='0'>
|
||||||
{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
|
<span>{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}</span>
|
||||||
{status.get('content')}
|
<span>{status.get('content')}</span>
|
||||||
</div>
|
</div>
|
||||||
</HotKeys>
|
</HotKeys>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,7 +18,6 @@ export default class StatusList extends ImmutablePureComponent {
|
||||||
onScrollToTop: PropTypes.func,
|
onScrollToTop: PropTypes.func,
|
||||||
onScroll: PropTypes.func,
|
onScroll: PropTypes.func,
|
||||||
trackScroll: PropTypes.bool,
|
trackScroll: PropTypes.bool,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
isPartial: PropTypes.bool,
|
isPartial: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
|
@ -77,7 +76,7 @@ export default class StatusList extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { statusIds, featuredStatusIds, shouldUpdateScroll, onLoadMore, timelineId, ...other } = this.props;
|
const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other } = this.props;
|
||||||
const { isLoading, isPartial } = other;
|
const { isLoading, isPartial } = other;
|
||||||
|
|
||||||
if (isPartial) {
|
if (isPartial) {
|
||||||
|
@ -120,7 +119,7 @@ export default class StatusList extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollableList {...other} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} shouldUpdateScroll={shouldUpdateScroll} ref={this.setRef}>
|
<ScrollableList {...other} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
|
||||||
{scrollableContent}
|
{scrollableContent}
|
||||||
</ScrollableList>
|
</ScrollableList>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,8 +10,6 @@ import { hydrateStore } from '../actions/store';
|
||||||
import { connectUserStream } from '../actions/streaming';
|
import { connectUserStream } from '../actions/streaming';
|
||||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||||
import { getLocale } from '../locales';
|
import { getLocale } from '../locales';
|
||||||
import { previewState as previewMediaState } from 'mastodon/features/ui/components/media_modal';
|
|
||||||
import { previewState as previewVideoState } from 'mastodon/features/ui/components/video_modal';
|
|
||||||
import initialState from '../initial_state';
|
import initialState from '../initial_state';
|
||||||
import ErrorBoundary from '../components/error_boundary';
|
import ErrorBoundary from '../components/error_boundary';
|
||||||
|
|
||||||
|
@ -41,8 +39,8 @@ export default class Mastodon extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldUpdateScroll (_, { location }) {
|
shouldUpdateScroll (prevRouterProps, { location }) {
|
||||||
return location.state !== previewMediaState && location.state !== previewVideoState;
|
return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { ScrollContainer as OriginalScrollContainer } from 'react-router-scroll-4';
|
||||||
|
|
||||||
|
// ScrollContainer is used to automatically scroll to the top when pushing a
|
||||||
|
// new history state and remembering the scroll position when going back.
|
||||||
|
// There are a few things we need to do differently, though.
|
||||||
|
const defaultShouldUpdateScroll = (prevRouterProps, { location }) => {
|
||||||
|
// If the change is caused by opening a modal, do not scroll to top
|
||||||
|
return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default
|
||||||
|
class ScrollContainer extends OriginalScrollContainer {
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
shouldUpdateScroll: defaultShouldUpdateScroll,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { getAccountGallery } from 'mastodon/selectors';
|
import { getAccountGallery } from 'mastodon/selectors';
|
||||||
import MediaItem from './components/media_item';
|
import MediaItem from './components/media_item';
|
||||||
import HeaderContainer from '../account_timeline/containers/header_container';
|
import HeaderContainer from '../account_timeline/containers/header_container';
|
||||||
import { ScrollContainer } from 'react-router-scroll-4';
|
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||||
import LoadMore from 'mastodon/components/load_more';
|
import LoadMore from 'mastodon/components/load_more';
|
||||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
|
@ -29,7 +29,6 @@ const mapStateToProps = (state, props) => ({
|
||||||
class LoadMoreMedia extends ImmutablePureComponent {
|
class LoadMoreMedia extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
maxId: PropTypes.string,
|
maxId: PropTypes.string,
|
||||||
onLoadMore: PropTypes.func.isRequired,
|
onLoadMore: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -127,7 +126,7 @@ class AccountGallery extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props;
|
const { attachments, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props;
|
||||||
const { width } = this.state;
|
const { width } = this.state;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
|
@ -164,7 +163,7 @@ class AccountGallery extends ImmutablePureComponent {
|
||||||
<Column>
|
<Column>
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnBackButton multiColumn={multiColumn} />
|
||||||
|
|
||||||
<ScrollContainer scrollKey='account_gallery' shouldUpdateScroll={shouldUpdateScroll}>
|
<ScrollContainer scrollKey='account_gallery'>
|
||||||
<div className='scrollable scrollable--flex' onScroll={this.handleScroll}>
|
<div className='scrollable scrollable--flex' onScroll={this.handleScroll}>
|
||||||
<HeaderContainer accountId={this.props.params.accountId} />
|
<HeaderContainer accountId={this.props.params.accountId} />
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
statusIds: ImmutablePropTypes.list,
|
statusIds: ImmutablePropTypes.list,
|
||||||
featuredStatusIds: ImmutablePropTypes.list,
|
featuredStatusIds: ImmutablePropTypes.list,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
|
@ -115,7 +114,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props;
|
const { statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
|
@ -162,7 +161,6 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
timelineId='account'
|
timelineId='account'
|
||||||
|
|
|
@ -29,7 +29,6 @@ class Blocks extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
|
@ -46,7 +45,7 @@ class Blocks extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, accountIds, shouldUpdateScroll, hasMore, multiColumn, isLoading } = this.props;
|
const { intl, accountIds, hasMore, multiColumn, isLoading } = this.props;
|
||||||
|
|
||||||
if (!accountIds) {
|
if (!accountIds) {
|
||||||
return (
|
return (
|
||||||
|
@ -66,7 +65,6 @@ class Blocks extends ImmutablePureComponent {
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
|
|
|
@ -27,7 +27,6 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
statusIds: ImmutablePropTypes.list.isRequired,
|
statusIds: ImmutablePropTypes.list.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
|
@ -68,7 +67,7 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true })
|
}, 300, { leading: true })
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked toots yet. When you bookmark one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked toots yet. When you bookmark one, it will show up here." />;
|
||||||
|
@ -93,7 +92,6 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -41,7 +41,6 @@ class CommunityTimeline extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
hasUnread: PropTypes.bool,
|
hasUnread: PropTypes.bool,
|
||||||
|
@ -103,7 +102,7 @@ class CommunityTimeline extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
|
const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -127,7 +126,6 @@ class CommunityTimeline extends React.PureComponent {
|
||||||
timelineId={`community${onlyMedia ? ':media' : ''}`}
|
timelineId={`community${onlyMedia ? ':media' : ''}`}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
|
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
||||||
import Overlay from 'react-overlays/lib/Overlay';
|
import Overlay from 'react-overlays/lib/Overlay';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -12,7 +12,6 @@ import { assetHost } from 'mastodon/utils/config';
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
|
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
|
||||||
emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search...' },
|
emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search...' },
|
||||||
emoji_not_found: { id: 'emoji_button.not_found', defaultMessage: 'No emojos!! (╯°□°)╯︵ ┻━┻' },
|
|
||||||
custom: { id: 'emoji_button.custom', defaultMessage: 'Custom' },
|
custom: { id: 'emoji_button.custom', defaultMessage: 'Custom' },
|
||||||
recent: { id: 'emoji_button.recent', defaultMessage: 'Frequently used' },
|
recent: { id: 'emoji_button.recent', defaultMessage: 'Frequently used' },
|
||||||
search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' },
|
search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' },
|
||||||
|
@ -28,9 +27,26 @@ const messages = defineMessages({
|
||||||
|
|
||||||
let EmojiPicker, Emoji; // load asynchronously
|
let EmojiPicker, Emoji; // load asynchronously
|
||||||
|
|
||||||
const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png`;
|
|
||||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||||
|
|
||||||
|
const backgroundImageFn = () => `${assetHost}/emoji/sheet_13.png`;
|
||||||
|
|
||||||
|
const notFoundFn = () => (
|
||||||
|
<div className='emoji-mart-no-results'>
|
||||||
|
<Emoji
|
||||||
|
emoji='sleuth_or_spy'
|
||||||
|
set='twitter'
|
||||||
|
size={32}
|
||||||
|
sheetSize={32}
|
||||||
|
backgroundImageFn={backgroundImageFn}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='emoji-mart-no-results-label'>
|
||||||
|
<FormattedMessage id='emoji_button.not_found' defaultMessage='No matching emojis found' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
class ModifierPickerMenu extends React.PureComponent {
|
class ModifierPickerMenu extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -182,7 +198,6 @@ class EmojiPickerMenu extends React.PureComponent {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
search: intl.formatMessage(messages.emoji_search),
|
search: intl.formatMessage(messages.emoji_search),
|
||||||
notfound: intl.formatMessage(messages.emoji_not_found),
|
|
||||||
categories: {
|
categories: {
|
||||||
search: intl.formatMessage(messages.search_results),
|
search: intl.formatMessage(messages.search_results),
|
||||||
recent: intl.formatMessage(messages.recent),
|
recent: intl.formatMessage(messages.recent),
|
||||||
|
@ -263,7 +278,9 @@ class EmojiPickerMenu extends React.PureComponent {
|
||||||
recent={frequentlyUsedEmojis}
|
recent={frequentlyUsedEmojis}
|
||||||
skin={skinTone}
|
skin={skinTone}
|
||||||
showPreview={false}
|
showPreview={false}
|
||||||
|
showSkinTones={false}
|
||||||
backgroundImageFn={backgroundImageFn}
|
backgroundImageFn={backgroundImageFn}
|
||||||
|
notFound={notFoundFn}
|
||||||
autoFocus
|
autoFocus
|
||||||
emojiTooltip
|
emojiTooltip
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -21,6 +21,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.logoutMessage),
|
message: intl.formatMessage(messages.logoutMessage),
|
||||||
confirm: intl.formatMessage(messages.logoutConfirm),
|
confirm: intl.formatMessage(messages.logoutConfirm),
|
||||||
|
closeWhenConfirm: false,
|
||||||
onConfirm: () => logOut(),
|
onConfirm: () => logOut(),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
|
@ -74,6 +74,7 @@ class Compose extends React.PureComponent {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.logoutMessage),
|
message: intl.formatMessage(messages.logoutMessage),
|
||||||
confirm: intl.formatMessage(messages.logoutConfirm),
|
confirm: intl.formatMessage(messages.logoutConfirm),
|
||||||
|
closeWhenConfirm: false,
|
||||||
onConfirm: () => logOut(),
|
onConfirm: () => logOut(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ export default class ConversationsList extends ImmutablePureComponent {
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
onLoadMore: PropTypes.func,
|
onLoadMore: PropTypes.func,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id)
|
getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id)
|
||||||
|
|
|
@ -19,7 +19,6 @@ class DirectTimeline extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
hasUnread: PropTypes.bool,
|
hasUnread: PropTypes.bool,
|
||||||
|
@ -71,7 +70,7 @@ class DirectTimeline extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, hasUnread, columnId, multiColumn, shouldUpdateScroll } = this.props;
|
const { intl, hasUnread, columnId, multiColumn } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -93,7 +92,6 @@ class DirectTimeline extends React.PureComponent {
|
||||||
timelineId='direct'
|
timelineId='direct'
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
|
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import AccountCard from './components/account_card';
|
||||||
import RadioButton from 'mastodon/components/radio_button';
|
import RadioButton from 'mastodon/components/radio_button';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import LoadMore from 'mastodon/components/load_more';
|
import LoadMore from 'mastodon/components/load_more';
|
||||||
import { ScrollContainer } from 'react-router-scroll-4';
|
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
title: { id: 'column.directory', defaultMessage: 'Browse profiles' },
|
title: { id: 'column.directory', defaultMessage: 'Browse profiles' },
|
||||||
|
@ -40,7 +40,6 @@ class Directory extends React.PureComponent {
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
accountIds: ImmutablePropTypes.list.isRequired,
|
accountIds: ImmutablePropTypes.list.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
|
@ -125,7 +124,7 @@ class Directory extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { isLoading, accountIds, intl, columnId, multiColumn, domain, shouldUpdateScroll } = this.props;
|
const { isLoading, accountIds, intl, columnId, multiColumn, domain } = this.props;
|
||||||
const { order, local } = this.getParams(this.props, this.state);
|
const { order, local } = this.getParams(this.props, this.state);
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
|
@ -163,7 +162,7 @@ class Directory extends React.PureComponent {
|
||||||
multiColumn={multiColumn}
|
multiColumn={multiColumn}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{multiColumn && !pinned ? <ScrollContainer scrollKey='directory' shouldUpdateScroll={shouldUpdateScroll}>{scrollableArea}</ScrollContainer> : scrollableArea}
|
{multiColumn && !pinned ? <ScrollContainer scrollKey='directory'>{scrollableArea}</ScrollContainer> : scrollableArea}
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ class Blocks extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
domains: ImmutablePropTypes.orderedSet,
|
domains: ImmutablePropTypes.orderedSet,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
|
@ -45,7 +44,7 @@ class Blocks extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, domains, shouldUpdateScroll, hasMore, multiColumn } = this.props;
|
const { intl, domains, hasMore, multiColumn } = this.props;
|
||||||
|
|
||||||
if (!domains) {
|
if (!domains) {
|
||||||
return (
|
return (
|
||||||
|
@ -64,7 +63,6 @@ class Blocks extends ImmutablePureComponent {
|
||||||
scrollKey='domain_blocks'
|
scrollKey='domain_blocks'
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
|
|
|
@ -7,29 +7,38 @@
|
||||||
|
|
||||||
const { unicodeToFilename } = require('./unicode_to_filename');
|
const { unicodeToFilename } = require('./unicode_to_filename');
|
||||||
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
|
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
|
||||||
const emojiMap = require('./emoji_map.json');
|
const emojiMap = require('./emoji_map.json');
|
||||||
const { emojiIndex } = require('emoji-mart');
|
const { emojiIndex } = require('emoji-mart');
|
||||||
const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data');
|
const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data');
|
||||||
|
|
||||||
let data = require('emoji-mart/data/all.json');
|
let data = require('emoji-mart/data/all.json');
|
||||||
|
|
||||||
if(data.compressed) {
|
if(data.compressed) {
|
||||||
data = emojiMartUncompress(data);
|
data = emojiMartUncompress(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const emojiMartData = data;
|
const emojiMartData = data;
|
||||||
|
|
||||||
const excluded = ['®', '©', '™'];
|
const excluded = ['®', '©', '™'];
|
||||||
const skins = ['🏻', '🏼', '🏽', '🏾', '🏿'];
|
const skinTones = ['🏻', '🏼', '🏽', '🏾', '🏿'];
|
||||||
const shortcodeMap = {};
|
const shortcodeMap = {};
|
||||||
|
|
||||||
const shortCodesToEmojiData = {};
|
const shortCodesToEmojiData = {};
|
||||||
const emojisWithoutShortCodes = [];
|
const emojisWithoutShortCodes = [];
|
||||||
|
|
||||||
Object.keys(emojiIndex.emojis).forEach(key => {
|
Object.keys(emojiIndex.emojis).forEach(key => {
|
||||||
shortcodeMap[emojiIndex.emojis[key].native] = emojiIndex.emojis[key].id;
|
let emoji = emojiIndex.emojis[key];
|
||||||
|
|
||||||
|
// Emojis with skin tone modifiers are stored like this
|
||||||
|
if (Object.prototype.hasOwnProperty.call(emoji, '1')) {
|
||||||
|
emoji = emoji['1'];
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcodeMap[emoji.native] = emoji.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
const stripModifiers = unicode => {
|
const stripModifiers = unicode => {
|
||||||
skins.forEach(tone => {
|
skinTones.forEach(tone => {
|
||||||
unicode = unicode.replace(tone, '');
|
unicode = unicode.replace(tone, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -64,13 +73,22 @@ Object.keys(emojiMap).forEach(key => {
|
||||||
if (!Array.isArray(shortCodesToEmojiData[shortcode])) {
|
if (!Array.isArray(shortCodesToEmojiData[shortcode])) {
|
||||||
shortCodesToEmojiData[shortcode] = [[]];
|
shortCodesToEmojiData[shortcode] = [[]];
|
||||||
}
|
}
|
||||||
|
|
||||||
shortCodesToEmojiData[shortcode][0].push(filenameData);
|
shortCodesToEmojiData[shortcode][0].push(filenameData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(emojiIndex.emojis).forEach(key => {
|
Object.keys(emojiIndex.emojis).forEach(key => {
|
||||||
const { native } = emojiIndex.emojis[key];
|
let emoji = emojiIndex.emojis[key];
|
||||||
|
|
||||||
|
// Emojis with skin tone modifiers are stored like this
|
||||||
|
if (Object.prototype.hasOwnProperty.call(emoji, '1')) {
|
||||||
|
emoji = emoji['1'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { native } = emoji;
|
||||||
let { short_names, search, unified } = emojiMartData.emojis[key];
|
let { short_names, search, unified } = emojiMartData.emojis[key];
|
||||||
|
|
||||||
if (short_names[0] !== key) {
|
if (short_names[0] !== key) {
|
||||||
throw new Error('The compresser expects the first short_code to be the ' +
|
throw new Error('The compresser expects the first short_code to be the ' +
|
||||||
'key. It may need to be rewritten if the emoji change such that this ' +
|
'key. It may need to be rewritten if the emoji change such that this ' +
|
||||||
|
@ -80,11 +98,16 @@ Object.keys(emojiIndex.emojis).forEach(key => {
|
||||||
short_names = short_names.slice(1); // first short name can be inferred from the key
|
short_names = short_names.slice(1); // first short name can be inferred from the key
|
||||||
|
|
||||||
const searchData = [native, short_names, search];
|
const searchData = [native, short_names, search];
|
||||||
|
|
||||||
if (unicodeToUnifiedName(native) !== unified) {
|
if (unicodeToUnifiedName(native) !== unified) {
|
||||||
// unified name can't be derived from unicodeToUnifiedName
|
// unified name can't be derived from unicodeToUnifiedName
|
||||||
searchData.push(unified);
|
searchData.push(unified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(shortCodesToEmojiData[key])) {
|
||||||
|
shortCodesToEmojiData[key] = [[]];
|
||||||
|
}
|
||||||
|
|
||||||
shortCodesToEmojiData[key].push(searchData);
|
shortCodesToEmojiData[key].push(searchData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,20 @@ function padLeft(str, num) {
|
||||||
while (str.length < num) {
|
while (str.length < num) {
|
||||||
str = '0' + str;
|
str = '0' + str;
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.unicodeToUnifiedName = (str) => {
|
exports.unicodeToUnifiedName = (str) => {
|
||||||
let output = '';
|
let output = '';
|
||||||
|
|
||||||
for (let i = 0; i < str.length; i += 2) {
|
for (let i = 0; i < str.length; i += 2) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
output += '-';
|
output += '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
output += padLeft(str.codePointAt(i).toString(16).toUpperCase(), 4);
|
output += padLeft(str.codePointAt(i).toString(16).toUpperCase(), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,7 +27,6 @@ class Favourites extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
statusIds: ImmutablePropTypes.list.isRequired,
|
statusIds: ImmutablePropTypes.list.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
|
@ -68,7 +67,7 @@ class Favourites extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true })
|
}, 300, { leading: true })
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite toots yet. When you favourite one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite toots yet. When you favourite one, it will show up here." />;
|
||||||
|
@ -93,7 +92,6 @@ class Favourites extends ImmutablePureComponent {
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -27,7 +27,6 @@ class Favourites extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
|
@ -50,7 +49,7 @@ class Favourites extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
|
const { intl, accountIds, multiColumn } = this.props;
|
||||||
|
|
||||||
if (!accountIds) {
|
if (!accountIds) {
|
||||||
return (
|
return (
|
||||||
|
@ -74,7 +73,6 @@ class Favourites extends ImmutablePureComponent {
|
||||||
|
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='favourites'
|
scrollKey='favourites'
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
|
|
|
@ -32,7 +32,6 @@ class FollowRequests extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
|
@ -51,7 +50,7 @@ class FollowRequests extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props;
|
const { intl, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props;
|
||||||
|
|
||||||
if (!accountIds) {
|
if (!accountIds) {
|
||||||
return (
|
return (
|
||||||
|
@ -80,7 +79,6 @@ class FollowRequests extends ImmutablePureComponent {
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
prepend={unlockedPrependMessage}
|
prepend={unlockedPrependMessage}
|
||||||
|
|
|
@ -43,7 +43,6 @@ class Followers extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
|
@ -73,7 +72,7 @@ class Followers extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
|
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
|
@ -112,7 +111,6 @@ class Followers extends ImmutablePureComponent {
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
||||||
alwaysPrepend
|
alwaysPrepend
|
||||||
append={remoteMessage}
|
append={remoteMessage}
|
||||||
|
|
|
@ -43,7 +43,6 @@ class Following extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
|
@ -73,7 +72,7 @@ class Following extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
|
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
|
@ -112,7 +111,6 @@ class Following extends ImmutablePureComponent {
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
||||||
alwaysPrepend
|
alwaysPrepend
|
||||||
append={remoteMessage}
|
append={remoteMessage}
|
||||||
|
|
|
@ -24,7 +24,6 @@ class HashtagTimeline extends React.PureComponent {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
hasUnread: PropTypes.bool,
|
hasUnread: PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
@ -130,7 +129,7 @@ class HashtagTimeline extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { shouldUpdateScroll, hasUnread, columnId, multiColumn } = this.props;
|
const { hasUnread, columnId, multiColumn } = this.props;
|
||||||
const { id, local } = this.props.params;
|
const { id, local } = this.props.params;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
|
@ -156,7 +155,6 @@ class HashtagTimeline extends React.PureComponent {
|
||||||
timelineId={`hashtag:${id}${local ? ':local' : ''}`}
|
timelineId={`hashtag:${id}${local ? ':local' : ''}`}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
|
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -34,7 +34,6 @@ class HomeTimeline extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
hasUnread: PropTypes.bool,
|
hasUnread: PropTypes.bool,
|
||||||
isPartial: PropTypes.bool,
|
isPartial: PropTypes.bool,
|
||||||
|
@ -112,7 +111,7 @@ class HomeTimeline extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
|
const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
let announcementsButton = null;
|
let announcementsButton = null;
|
||||||
|
@ -154,7 +153,6 @@ class HomeTimeline extends React.PureComponent {
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
timelineId='home'
|
timelineId='home'
|
||||||
emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty! Follow more people to fill it up. {suggestions}' values={{ suggestions: <Link to='/start'><FormattedMessage id='empty_column.home.suggestions' defaultMessage='See some suggestions' /></Link> }} />}
|
emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty! Follow more people to fill it up. {suggestions}' values={{ suggestions: <Link to='/start'><FormattedMessage id='empty_column.home.suggestions' defaultMessage='See some suggestions' /></Link> }} />}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -41,7 +41,6 @@ class ListTimeline extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
hasUnread: PropTypes.bool,
|
hasUnread: PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
|
@ -142,7 +141,7 @@ class ListTimeline extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { shouldUpdateScroll, hasUnread, columnId, multiColumn, list, intl } = this.props;
|
const { hasUnread, columnId, multiColumn, list, intl } = this.props;
|
||||||
const { id } = this.props.params;
|
const { id } = this.props.params;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
const title = list ? list.get('title') : id;
|
const title = list ? list.get('title') : id;
|
||||||
|
@ -207,7 +206,6 @@ class ListTimeline extends React.PureComponent {
|
||||||
timelineId={`list:${id}`}
|
timelineId={`list:${id}`}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />}
|
emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -48,7 +48,7 @@ class Lists extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, lists, multiColumn } = this.props;
|
const { intl, lists, multiColumn } = this.props;
|
||||||
|
|
||||||
if (!lists) {
|
if (!lists) {
|
||||||
return (
|
return (
|
||||||
|
@ -68,7 +68,6 @@ class Lists extends ImmutablePureComponent {
|
||||||
|
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='lists'
|
scrollKey='lists'
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
prepend={<ColumnSubheading text={intl.formatMessage(messages.subheading)} />}
|
prepend={<ColumnSubheading text={intl.formatMessage(messages.subheading)} />}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
|
|
|
@ -29,7 +29,6 @@ class Mutes extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
|
@ -46,7 +45,7 @@ class Mutes extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, hasMore, accountIds, multiColumn, isLoading } = this.props;
|
const { intl, hasMore, accountIds, multiColumn, isLoading } = this.props;
|
||||||
|
|
||||||
if (!accountIds) {
|
if (!accountIds) {
|
||||||
return (
|
return (
|
||||||
|
@ -66,7 +65,6 @@ class Mutes extends ImmutablePureComponent {
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
|
|
|
@ -74,7 +74,6 @@ class Notifications extends React.PureComponent {
|
||||||
notifications: ImmutablePropTypes.list.isRequired,
|
notifications: ImmutablePropTypes.list.isRequired,
|
||||||
showFilterBar: PropTypes.bool.isRequired,
|
showFilterBar: PropTypes.bool.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
isUnread: PropTypes.bool,
|
isUnread: PropTypes.bool,
|
||||||
|
@ -176,7 +175,7 @@ class Notifications extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
|
const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />;
|
||||||
|
|
||||||
|
@ -227,7 +226,6 @@ class Notifications extends React.PureComponent {
|
||||||
onLoadPending={this.handleLoadPending}
|
onLoadPending={this.handleLoadPending}
|
||||||
onScrollToTop={this.handleScrollToTop}
|
onScrollToTop={this.handleScrollToTop}
|
||||||
onScroll={this.handleScroll}
|
onScroll={this.handleScroll}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
{scrollableContent}
|
{scrollableContent}
|
||||||
|
|
|
@ -114,7 +114,11 @@ class Footer extends ImmutablePureComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { status } = this.props;
|
const { status, onClose } = this.props;
|
||||||
|
|
||||||
|
if (onClose) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
router.history.push(`/statuses/${status.get('id')}`);
|
router.history.push(`/statuses/${status.get('id')}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ class PinnedStatuses extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
statusIds: ImmutablePropTypes.list.isRequired,
|
statusIds: ImmutablePropTypes.list.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
hasMore: PropTypes.bool.isRequired,
|
hasMore: PropTypes.bool.isRequired,
|
||||||
|
@ -44,7 +43,7 @@ class PinnedStatuses extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, statusIds, hasMore, multiColumn } = this.props;
|
const { intl, statusIds, hasMore, multiColumn } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}>
|
<Column bindToDocument={!multiColumn} icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}>
|
||||||
|
@ -53,7 +52,6 @@ class PinnedStatuses extends ImmutablePureComponent {
|
||||||
statusIds={statusIds}
|
statusIds={statusIds}
|
||||||
scrollKey='pinned_statuses'
|
scrollKey='pinned_statuses'
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -43,7 +43,6 @@ class PublicTimeline extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
|
@ -106,7 +105,7 @@ class PublicTimeline extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
|
const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -130,7 +129,6 @@ class PublicTimeline extends React.PureComponent {
|
||||||
trackScroll={!pinned}
|
trackScroll={!pinned}
|
||||||
scrollKey={`public_timeline-${columnId}`}
|
scrollKey={`public_timeline-${columnId}`}
|
||||||
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />}
|
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -27,7 +27,6 @@ class Reblogs extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
|
@ -50,7 +49,7 @@ class Reblogs extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
|
const { intl, accountIds, multiColumn } = this.props;
|
||||||
|
|
||||||
if (!accountIds) {
|
if (!accountIds) {
|
||||||
return (
|
return (
|
||||||
|
@ -74,7 +73,6 @@ class Reblogs extends ImmutablePureComponent {
|
||||||
|
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='reblogs'
|
scrollKey='reblogs'
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
|
|
|
@ -45,7 +45,7 @@ import { initBlockModal } from '../../actions/blocks';
|
||||||
import { initBoostModal } from '../../actions/boosts';
|
import { initBoostModal } from '../../actions/boosts';
|
||||||
import { initReport } from '../../actions/reports';
|
import { initReport } from '../../actions/reports';
|
||||||
import { makeGetStatus, makeGetPictureInPicture } from '../../selectors';
|
import { makeGetStatus, makeGetPictureInPicture } from '../../selectors';
|
||||||
import { ScrollContainer } from 'react-router-scroll-4';
|
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||||
import ColumnBackButton from '../../components/column_back_button';
|
import ColumnBackButton from '../../components/column_back_button';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
import StatusContainer from '../../containers/status_container';
|
import StatusContainer from '../../containers/status_container';
|
||||||
|
@ -83,7 +83,7 @@ const makeMapStateToProps = () => {
|
||||||
ancestorsIds = ancestorsIds.withMutations(mutable => {
|
ancestorsIds = ancestorsIds.withMutations(mutable => {
|
||||||
let id = statusId;
|
let id = statusId;
|
||||||
|
|
||||||
while (id) {
|
while (id && !mutable.includes(id)) {
|
||||||
mutable.unshift(id);
|
mutable.unshift(id);
|
||||||
id = inReplyTos.get(id);
|
id = inReplyTos.get(id);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ const makeMapStateToProps = () => {
|
||||||
const ids = [statusId];
|
const ids = [statusId];
|
||||||
|
|
||||||
while (ids.length > 0) {
|
while (ids.length > 0) {
|
||||||
let id = ids.shift();
|
let id = ids.pop();
|
||||||
const replies = contextReplies.get(id);
|
const replies = contextReplies.get(id);
|
||||||
|
|
||||||
if (statusId !== id) {
|
if (statusId !== id) {
|
||||||
|
@ -110,7 +110,7 @@ const makeMapStateToProps = () => {
|
||||||
|
|
||||||
if (replies) {
|
if (replies) {
|
||||||
replies.reverse().forEach(reply => {
|
replies.reverse().forEach(reply => {
|
||||||
ids.unshift(reply);
|
if (!ids.includes(reply) && !descendantsIds.includes(reply) && statusId !== reply) ids.push(reply);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -498,7 +498,7 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let ancestors, descendants;
|
let ancestors, descendants;
|
||||||
const { shouldUpdateScroll, status, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props;
|
const { status, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props;
|
||||||
const { fullscreen } = this.state;
|
const { fullscreen } = this.state;
|
||||||
|
|
||||||
if (status === null) {
|
if (status === null) {
|
||||||
|
@ -541,7 +541,7 @@ class Status extends ImmutablePureComponent {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ScrollContainer scrollKey='thread' shouldUpdateScroll={shouldUpdateScroll}>
|
<ScrollContainer scrollKey='thread'>
|
||||||
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
|
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
|
||||||
{ancestors}
|
{ancestors}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
||||||
import Audio from 'mastodon/features/audio';
|
import Audio from 'mastodon/features/audio';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { previewState } from './video_modal';
|
|
||||||
import Footer from 'mastodon/features/picture_in_picture/components/footer';
|
import Footer from 'mastodon/features/picture_in_picture/components/footer';
|
||||||
|
|
||||||
const mapStateToProps = (state, { statusId }) => ({
|
const mapStateToProps = (state, { statusId }) => ({
|
||||||
|
@ -25,32 +24,6 @@ class AudioModal extends ImmutablePureComponent {
|
||||||
onChangeBackgroundColor: PropTypes.func.isRequired,
|
onChangeBackgroundColor: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
if (this.context.router) {
|
|
||||||
const history = this.context.router.history;
|
|
||||||
|
|
||||||
history.push(history.location.pathname, previewState);
|
|
||||||
|
|
||||||
this.unlistenHistory = history.listen(() => {
|
|
||||||
this.props.onClose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
if (this.context.router) {
|
|
||||||
this.unlistenHistory();
|
|
||||||
|
|
||||||
if (this.context.router.history.location.state === previewState) {
|
|
||||||
this.context.router.history.goBack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { media, accountStaticAvatar, statusId, onClose } = this.props;
|
const { media, accountStaticAvatar, statusId, onClose } = this.props;
|
||||||
const options = this.props.options || {};
|
const options = this.props.options || {};
|
||||||
|
|
|
@ -13,15 +13,22 @@ class ConfirmationModal extends React.PureComponent {
|
||||||
onConfirm: PropTypes.func.isRequired,
|
onConfirm: PropTypes.func.isRequired,
|
||||||
secondary: PropTypes.string,
|
secondary: PropTypes.string,
|
||||||
onSecondary: PropTypes.func,
|
onSecondary: PropTypes.func,
|
||||||
|
closeWhenConfirm: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
closeWhenConfirm: true,
|
||||||
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.button.focus();
|
this.button.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
this.props.onClose();
|
if (this.props.closeWhenConfirm) {
|
||||||
|
this.props.onClose();
|
||||||
|
}
|
||||||
this.props.onConfirm();
|
this.props.onConfirm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: intl.formatMessage(messages.logoutMessage),
|
message: intl.formatMessage(messages.logoutMessage),
|
||||||
confirm: intl.formatMessage(messages.logoutConfirm),
|
confirm: intl.formatMessage(messages.logoutConfirm),
|
||||||
|
closeWhenConfirm: false,
|
||||||
onConfirm: () => logOut(),
|
onConfirm: () => logOut(),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,8 +20,6 @@ const messages = defineMessages({
|
||||||
next: { id: 'lightbox.next', defaultMessage: 'Next' },
|
next: { id: 'lightbox.next', defaultMessage: 'Next' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export const previewState = 'previewMediaModal';
|
|
||||||
|
|
||||||
export default @injectIntl
|
export default @injectIntl
|
||||||
class MediaModal extends ImmutablePureComponent {
|
class MediaModal extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
@ -37,10 +35,6 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
volume: PropTypes.number,
|
volume: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
index: null,
|
index: null,
|
||||||
navigationHidden: false,
|
navigationHidden: false,
|
||||||
|
@ -98,16 +92,6 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
window.addEventListener('keydown', this.handleKeyDown, false);
|
window.addEventListener('keydown', this.handleKeyDown, false);
|
||||||
|
|
||||||
if (this.context.router) {
|
|
||||||
const history = this.context.router.history;
|
|
||||||
|
|
||||||
history.push(history.location.pathname, previewState);
|
|
||||||
|
|
||||||
this.unlistenHistory = history.listen(() => {
|
|
||||||
this.props.onClose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._sendBackgroundColor();
|
this._sendBackgroundColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,14 +115,6 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
window.removeEventListener('keydown', this.handleKeyDown);
|
window.removeEventListener('keydown', this.handleKeyDown);
|
||||||
|
|
||||||
if (this.context.router) {
|
|
||||||
this.unlistenHistory();
|
|
||||||
|
|
||||||
if (this.context.router.history.location.state === previewState) {
|
|
||||||
this.context.router.history.goBack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.onChangeBackgroundColor(null);
|
this.props.onChangeBackgroundColor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import Footer from 'mastodon/features/picture_in_picture/components/footer';
|
import Footer from 'mastodon/features/picture_in_picture/components/footer';
|
||||||
import { getAverageFromBlurhash } from 'mastodon/blurhash';
|
import { getAverageFromBlurhash } from 'mastodon/blurhash';
|
||||||
|
|
||||||
export const previewState = 'previewVideoModal';
|
|
||||||
|
|
||||||
export default class VideoModal extends ImmutablePureComponent {
|
export default class VideoModal extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -22,19 +20,9 @@ export default class VideoModal extends ImmutablePureComponent {
|
||||||
onChangeBackgroundColor: PropTypes.func.isRequired,
|
onChangeBackgroundColor: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const { router } = this.context;
|
|
||||||
const { media, onChangeBackgroundColor, onClose } = this.props;
|
const { media, onChangeBackgroundColor, onClose } = this.props;
|
||||||
|
|
||||||
if (router) {
|
|
||||||
router.history.push(router.history.location.pathname, previewState);
|
|
||||||
this.unlistenHistory = router.history.listen(() => onClose());
|
|
||||||
}
|
|
||||||
|
|
||||||
const backgroundColor = getAverageFromBlurhash(media.get('blurhash'));
|
const backgroundColor = getAverageFromBlurhash(media.get('blurhash'));
|
||||||
|
|
||||||
if (backgroundColor) {
|
if (backgroundColor) {
|
||||||
|
@ -42,18 +30,6 @@ export default class VideoModal extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
const { router } = this.context;
|
|
||||||
|
|
||||||
if (router) {
|
|
||||||
this.unlistenHistory();
|
|
||||||
|
|
||||||
if (router.history.location.state === previewState) {
|
|
||||||
router.history.goBack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { media, statusId, onClose } = this.props;
|
const { media, statusId, onClose } = this.props;
|
||||||
const options = this.props.options || {};
|
const options = this.props.options || {};
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { closeModal } from '../../../actions/modal';
|
||||||
import ModalRoot from '../components/modal_root';
|
import ModalRoot from '../components/modal_root';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
type: state.get('modal').modalType,
|
type: state.getIn(['modal', 0, 'modalType'], null),
|
||||||
props: state.get('modal').modalProps,
|
props: state.getIn(['modal', 0, 'modalProps'], {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
|
@ -54,8 +54,6 @@ import {
|
||||||
FollowRecommendations,
|
FollowRecommendations,
|
||||||
} from './util/async-components';
|
} from './util/async-components';
|
||||||
import { me } from '../../initial_state';
|
import { me } from '../../initial_state';
|
||||||
import { previewState as previewMediaState } from './components/media_modal';
|
|
||||||
import { previewState as previewVideoState } from './components/video_modal';
|
|
||||||
import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
|
import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
|
||||||
|
|
||||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||||
|
@ -138,10 +136,6 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldUpdateScroll (_, { location }) {
|
|
||||||
return location.state !== previewMediaState && location.state !== previewVideoState;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRef = c => {
|
setRef = c => {
|
||||||
if (c) {
|
if (c) {
|
||||||
this.node = c.getWrappedInstance();
|
this.node = c.getWrappedInstance();
|
||||||
|
@ -158,38 +152,38 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
{redirect}
|
{redirect}
|
||||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
||||||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
||||||
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
|
||||||
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
|
||||||
<WrappedRoute path='/timelines/public/local' exact component={CommunityTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/timelines/public/local' exact component={CommunityTimeline} content={children} />
|
||||||
<WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
|
||||||
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
|
||||||
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/notifications' component={Notifications} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/notifications' component={Notifications} content={children} />
|
||||||
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
|
||||||
<WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
|
<WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
|
||||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/start' component={FollowRecommendations} content={children} />
|
<WrappedRoute path='/start' component={FollowRecommendations} content={children} />
|
||||||
<WrappedRoute path='/search' component={Search} content={children} />
|
<WrappedRoute path='/search' component={Search} content={children} />
|
||||||
<WrappedRoute path='/directory' component={Directory} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/directory' component={Directory} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
|
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
|
||||||
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
|
||||||
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
|
||||||
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll, withReplies: true }} />
|
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
|
||||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
||||||
<WrappedRoute path='/blocks' component={Blocks} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
||||||
<WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} />
|
||||||
<WrappedRoute path='/mutes' component={Mutes} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/mutes' component={Mutes} content={children} />
|
||||||
<WrappedRoute path='/lists' component={Lists} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
<WrappedRoute path='/lists' component={Lists} content={children} />
|
||||||
|
|
||||||
<WrappedRoute component={GenericNotFound} content={children} />
|
<WrappedRoute component={GenericNotFound} content={children} />
|
||||||
</WrappedSwitch>
|
</WrappedSwitch>
|
||||||
|
@ -361,9 +355,9 @@ class UI extends React.PureComponent {
|
||||||
this.props.dispatch(closeOnboarding());
|
this.props.dispatch(closeOnboarding());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.props.dispatch(fetchMarkers());
|
||||||
this.props.dispatch(expandHomeTimeline());
|
this.props.dispatch(expandHomeTimeline());
|
||||||
this.props.dispatch(expandNotifications());
|
this.props.dispatch(expandNotifications());
|
||||||
setTimeout(() => this.props.dispatch(fetchMarkers()), 500);
|
|
||||||
setTimeout(() => this.props.dispatch(fetchFilters()), 500);
|
setTimeout(() => this.props.dispatch(fetchFilters()), 500);
|
||||||
|
|
||||||
this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
|
this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
"account.moved_to": "Uživatel {name} se přesunul na:",
|
"account.moved_to": "Uživatel {name} se přesunul na:",
|
||||||
"account.mute": "Skrýt @{name}",
|
"account.mute": "Skrýt @{name}",
|
||||||
"account.mute_notifications": "Skrýt oznámení od @{name}",
|
"account.mute_notifications": "Skrýt oznámení od @{name}",
|
||||||
"account.muted": "Účet skryt",
|
"account.muted": "Skryt",
|
||||||
"account.never_active": "Nikdy",
|
"account.never_active": "Nikdy",
|
||||||
"account.posts": "Příspěvky",
|
"account.posts": "Příspěvky",
|
||||||
"account.posts_with_replies": "Příspěvky a odpovědi",
|
"account.posts_with_replies": "Příspěvky a odpovědi",
|
||||||
|
|
|
@ -1074,10 +1074,6 @@
|
||||||
"defaultMessage": "Search...",
|
"defaultMessage": "Search...",
|
||||||
"id": "emoji_button.search"
|
"id": "emoji_button.search"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"defaultMessage": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
|
||||||
"id": "emoji_button.not_found"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"defaultMessage": "Custom",
|
"defaultMessage": "Custom",
|
||||||
"id": "emoji_button.custom"
|
"id": "emoji_button.custom"
|
||||||
|
@ -1121,6 +1117,10 @@
|
||||||
{
|
{
|
||||||
"defaultMessage": "Flags",
|
"defaultMessage": "Flags",
|
||||||
"id": "emoji_button.flags"
|
"id": "emoji_button.flags"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "No matching emojis found",
|
||||||
|
"id": "emoji_button.not_found"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"path": "app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.json"
|
"path": "app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.json"
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Comida y bebida",
|
"emoji_button.food": "Comida y bebida",
|
||||||
"emoji_button.label": "Insertar emoji",
|
"emoji_button.label": "Insertar emoji",
|
||||||
"emoji_button.nature": "Naturaleza",
|
"emoji_button.nature": "Naturaleza",
|
||||||
"emoji_button.not_found": "¡¡No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No se encontraron emojis coincidentes",
|
||||||
"emoji_button.objects": "Objetos",
|
"emoji_button.objects": "Objetos",
|
||||||
"emoji_button.people": "Gente",
|
"emoji_button.people": "Gente",
|
||||||
"emoji_button.recent": "Usados frecuentemente",
|
"emoji_button.recent": "Usados frecuentemente",
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"account.follows.empty": "Este usuario todavía no sigue a nadie.",
|
"account.follows.empty": "Este usuario todavía no sigue a nadie.",
|
||||||
"account.follows_you": "Te sigue",
|
"account.follows_you": "Te sigue",
|
||||||
"account.hide_reblogs": "Ocultar retoots de @{name}",
|
"account.hide_reblogs": "Ocultar retoots de @{name}",
|
||||||
"account.joined": "Joined {date}",
|
"account.joined": "Se unió el {date}",
|
||||||
"account.last_status": "Última actividad",
|
"account.last_status": "Última actividad",
|
||||||
"account.link_verified_on": "El proprietario de este link fue comprobado el {date}",
|
"account.link_verified_on": "El proprietario de este link fue comprobado el {date}",
|
||||||
"account.locked_info": "El estado de privacidad de esta cuenta està configurado como bloqueado. El proprietario debe revisar manualmente quien puede seguirle.",
|
"account.locked_info": "El estado de privacidad de esta cuenta està configurado como bloqueado. El proprietario debe revisar manualmente quien puede seguirle.",
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
"compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
|
"compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
|
||||||
"compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
|
"compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
|
||||||
"compose_form.publish": "Tootear",
|
"compose_form.publish": "Tootear",
|
||||||
"compose_form.publish_loud": "{publish}!",
|
"compose_form.publish_loud": "¡{publish}!",
|
||||||
"compose_form.sensitive.hide": "Marcar multimedia como sensible",
|
"compose_form.sensitive.hide": "Marcar multimedia como sensible",
|
||||||
"compose_form.sensitive.marked": "Material marcado como sensible",
|
"compose_form.sensitive.marked": "Material marcado como sensible",
|
||||||
"compose_form.sensitive.unmarked": "Material no marcado como sensible",
|
"compose_form.sensitive.unmarked": "Material no marcado como sensible",
|
||||||
|
@ -160,11 +160,11 @@
|
||||||
"empty_column.domain_blocks": "Todavía no hay dominios ocultos.",
|
"empty_column.domain_blocks": "Todavía no hay dominios ocultos.",
|
||||||
"empty_column.favourited_statuses": "Aún no tienes toots preferidos. Cuando marques uno como favorito, aparecerá aquí.",
|
"empty_column.favourited_statuses": "Aún no tienes toots preferidos. Cuando marques uno como favorito, aparecerá aquí.",
|
||||||
"empty_column.favourites": "Nadie ha marcado este toot como preferido. Cuando alguien lo haga, aparecerá aquí.",
|
"empty_column.favourites": "Nadie ha marcado este toot como preferido. Cuando alguien lo haga, aparecerá aquí.",
|
||||||
"empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
|
"empty_column.follow_recommendations": "Parece que no se ha podido generar ninguna sugerencia para ti. Puedes probar a buscar a gente que quizá conozcas o explorar los hashtags que están en tendencia.",
|
||||||
"empty_column.follow_requests": "No tienes ninguna petición de seguidor. Cuando recibas una, se mostrará aquí.",
|
"empty_column.follow_requests": "No tienes ninguna petición de seguidor. Cuando recibas una, se mostrará aquí.",
|
||||||
"empty_column.hashtag": "No hay nada en este hashtag aún.",
|
"empty_column.hashtag": "No hay nada en este hashtag aún.",
|
||||||
"empty_column.home": "No estás siguiendo a nadie aún. Visita {public} o haz búsquedas para empezar y conocer gente nueva.",
|
"empty_column.home": "No estás siguiendo a nadie aún. Visita {public} o haz búsquedas para empezar y conocer gente nueva.",
|
||||||
"empty_column.home.suggestions": "See some suggestions",
|
"empty_column.home.suggestions": "Ver algunas sugerencias",
|
||||||
"empty_column.list": "No hay nada en esta lista aún. Cuando miembros de esta lista publiquen nuevos estatus, estos aparecerán qui.",
|
"empty_column.list": "No hay nada en esta lista aún. Cuando miembros de esta lista publiquen nuevos estatus, estos aparecerán qui.",
|
||||||
"empty_column.lists": "No tienes ninguna lista. cuando crees una, se mostrará aquí.",
|
"empty_column.lists": "No tienes ninguna lista. cuando crees una, se mostrará aquí.",
|
||||||
"empty_column.mutes": "Aún no has silenciado a ningún usuario.",
|
"empty_column.mutes": "Aún no has silenciado a ningún usuario.",
|
||||||
|
@ -177,8 +177,8 @@
|
||||||
"errors.unexpected_crash.copy_stacktrace": "Copiar el seguimiento de pila en el portapapeles",
|
"errors.unexpected_crash.copy_stacktrace": "Copiar el seguimiento de pila en el portapapeles",
|
||||||
"errors.unexpected_crash.report_issue": "Informar de un problema/error",
|
"errors.unexpected_crash.report_issue": "Informar de un problema/error",
|
||||||
"follow_recommendations.done": "Hecho",
|
"follow_recommendations.done": "Hecho",
|
||||||
"follow_recommendations.heading": "¡Sigue a la gente cuyas publicaciones te gustaría ver! Aquí tienes algunas sugerencias.",
|
"follow_recommendations.heading": "¡Sigue a gente que publique cosas que te gusten! Aquí tienes algunas sugerencias.",
|
||||||
"follow_recommendations.lead": "Los mensajes de las personas que sigues aparecerán en orden cronológico en el Inicio. No tengas miedo de cometer errores, ¡puedes dejar de seguir a la gente fácilmente en cualquier momento!",
|
"follow_recommendations.lead": "Las publicaciones de la gente a la que sigas aparecerán ordenadas cronológicamente en Inicio. No tengas miedo de cometer errores, ¡puedes dejarles de seguir en cualquier momento con la misma facilidad!",
|
||||||
"follow_request.authorize": "Autorizar",
|
"follow_request.authorize": "Autorizar",
|
||||||
"follow_request.reject": "Rechazar",
|
"follow_request.reject": "Rechazar",
|
||||||
"follow_requests.unlocked_explanation": "A pesar de que tu cuenta no es privada, el personal de {domain} ha pensado que quizás deberías revisar manualmente las solicitudes de seguimiento de estas cuentas.",
|
"follow_requests.unlocked_explanation": "A pesar de que tu cuenta no es privada, el personal de {domain} ha pensado que quizás deberías revisar manualmente las solicitudes de seguimiento de estas cuentas.",
|
||||||
|
@ -335,7 +335,7 @@
|
||||||
"picture_in_picture.restore": "Restaurar",
|
"picture_in_picture.restore": "Restaurar",
|
||||||
"poll.closed": "Cerrada",
|
"poll.closed": "Cerrada",
|
||||||
"poll.refresh": "Actualizar",
|
"poll.refresh": "Actualizar",
|
||||||
"poll.total_people": "{count, plural, one {# person} other {# people}}",
|
"poll.total_people": "{count, plural, one {# persona} other {# personas}}",
|
||||||
"poll.total_votes": "{count, plural, one {# voto} other {# votos}}",
|
"poll.total_votes": "{count, plural, one {# voto} other {# votos}}",
|
||||||
"poll.vote": "Votar",
|
"poll.vote": "Votar",
|
||||||
"poll.voted": "Has votado a favor de esta respuesta",
|
"poll.voted": "Has votado a favor de esta respuesta",
|
||||||
|
@ -353,11 +353,11 @@
|
||||||
"refresh": "Actualizar",
|
"refresh": "Actualizar",
|
||||||
"regeneration_indicator.label": "Cargando…",
|
"regeneration_indicator.label": "Cargando…",
|
||||||
"regeneration_indicator.sublabel": "¡Tu historia de inicio se está preparando!",
|
"regeneration_indicator.sublabel": "¡Tu historia de inicio se está preparando!",
|
||||||
"relative_time.days": "{number}d",
|
"relative_time.days": "{number} d",
|
||||||
"relative_time.hours": "{number}h",
|
"relative_time.hours": "{number} h",
|
||||||
"relative_time.just_now": "ahora",
|
"relative_time.just_now": "ahora",
|
||||||
"relative_time.minutes": "{number}m",
|
"relative_time.minutes": "{number} m",
|
||||||
"relative_time.seconds": "{number}s",
|
"relative_time.seconds": "{number} s",
|
||||||
"relative_time.today": "hoy",
|
"relative_time.today": "hoy",
|
||||||
"reply_indicator.cancel": "Cancelar",
|
"reply_indicator.cancel": "Cancelar",
|
||||||
"report.forward": "Reenviar a {target}",
|
"report.forward": "Reenviar a {target}",
|
||||||
|
@ -439,9 +439,9 @@
|
||||||
"trends.counter_by_accounts": "{count, plural, one {{counter} persona} other {{counter} personas}} hablando",
|
"trends.counter_by_accounts": "{count, plural, one {{counter} persona} other {{counter} personas}} hablando",
|
||||||
"trends.trending_now": "Tendencia ahora",
|
"trends.trending_now": "Tendencia ahora",
|
||||||
"ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
|
"ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
|
||||||
"units.short.billion": "{count}B",
|
"units.short.billion": "{count} MM",
|
||||||
"units.short.million": "{count}M",
|
"units.short.million": "{count} M",
|
||||||
"units.short.thousand": "{count}K",
|
"units.short.thousand": "{count} K",
|
||||||
"upload_area.title": "Arrastra y suelta para subir",
|
"upload_area.title": "Arrastra y suelta para subir",
|
||||||
"upload_button.label": "Subir multimedia (JPEG, PNG, GIF, WebM, MP4, MOV)",
|
"upload_button.label": "Subir multimedia (JPEG, PNG, GIF, WebM, MP4, MOV)",
|
||||||
"upload_error.limit": "Límite de subida de archivos excedido.",
|
"upload_error.limit": "Límite de subida de archivos excedido.",
|
||||||
|
|
|
@ -98,7 +98,7 @@
|
||||||
"compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
|
"compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
|
||||||
"compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
|
"compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
|
||||||
"compose_form.publish": "Tootear",
|
"compose_form.publish": "Tootear",
|
||||||
"compose_form.publish_loud": "{publish}!",
|
"compose_form.publish_loud": "¡{publish}!",
|
||||||
"compose_form.sensitive.hide": "Marcar multimedia como sensible",
|
"compose_form.sensitive.hide": "Marcar multimedia como sensible",
|
||||||
"compose_form.sensitive.marked": "Material marcado como sensible",
|
"compose_form.sensitive.marked": "Material marcado como sensible",
|
||||||
"compose_form.sensitive.unmarked": "Material no marcado como sensible",
|
"compose_form.sensitive.unmarked": "Material no marcado como sensible",
|
||||||
|
@ -335,7 +335,7 @@
|
||||||
"picture_in_picture.restore": "Restaurar",
|
"picture_in_picture.restore": "Restaurar",
|
||||||
"poll.closed": "Cerrada",
|
"poll.closed": "Cerrada",
|
||||||
"poll.refresh": "Actualizar",
|
"poll.refresh": "Actualizar",
|
||||||
"poll.total_people": "{count, plural, one {# person} other {# people}}",
|
"poll.total_people": "{count, plural, one {# persona} other {# personas}}",
|
||||||
"poll.total_votes": "{count, plural, one {# voto} other {# votos}}",
|
"poll.total_votes": "{count, plural, one {# voto} other {# votos}}",
|
||||||
"poll.vote": "Votar",
|
"poll.vote": "Votar",
|
||||||
"poll.voted": "Has votado a favor de esta respuesta",
|
"poll.voted": "Has votado a favor de esta respuesta",
|
||||||
|
@ -353,11 +353,11 @@
|
||||||
"refresh": "Actualizar",
|
"refresh": "Actualizar",
|
||||||
"regeneration_indicator.label": "Cargando…",
|
"regeneration_indicator.label": "Cargando…",
|
||||||
"regeneration_indicator.sublabel": "¡Tu historia de inicio se está preparando!",
|
"regeneration_indicator.sublabel": "¡Tu historia de inicio se está preparando!",
|
||||||
"relative_time.days": "{number}d",
|
"relative_time.days": "{number} d",
|
||||||
"relative_time.hours": "{number}h",
|
"relative_time.hours": "{number} h",
|
||||||
"relative_time.just_now": "ahora",
|
"relative_time.just_now": "ahora",
|
||||||
"relative_time.minutes": "{number}m",
|
"relative_time.minutes": "{number} m",
|
||||||
"relative_time.seconds": "{number}s",
|
"relative_time.seconds": "{number} s",
|
||||||
"relative_time.today": "hoy",
|
"relative_time.today": "hoy",
|
||||||
"reply_indicator.cancel": "Cancelar",
|
"reply_indicator.cancel": "Cancelar",
|
||||||
"report.forward": "Reenviar a {target}",
|
"report.forward": "Reenviar a {target}",
|
||||||
|
@ -439,9 +439,9 @@
|
||||||
"trends.counter_by_accounts": "{count, plural, one {{counter} persona} other {{counter} personas}} hablando",
|
"trends.counter_by_accounts": "{count, plural, one {{counter} persona} other {{counter} personas}} hablando",
|
||||||
"trends.trending_now": "Tendencia ahora",
|
"trends.trending_now": "Tendencia ahora",
|
||||||
"ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
|
"ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
|
||||||
"units.short.billion": "{count}B",
|
"units.short.billion": "{count} MM",
|
||||||
"units.short.million": "{count}M",
|
"units.short.million": "{count} M",
|
||||||
"units.short.thousand": "{count}K",
|
"units.short.thousand": "{count} K",
|
||||||
"upload_area.title": "Arrastra y suelta para subir",
|
"upload_area.title": "Arrastra y suelta para subir",
|
||||||
"upload_button.label": "Subir multimedia (JPEG, PNG, GIF, WebM, MP4, MOV)",
|
"upload_button.label": "Subir multimedia (JPEG, PNG, GIF, WebM, MP4, MOV)",
|
||||||
"upload_error.limit": "Límite de subida de archivos excedido.",
|
"upload_error.limit": "Límite de subida de archivos excedido.",
|
||||||
|
|
|
@ -102,8 +102,8 @@
|
||||||
"compose_form.sensitive.hide": "{count, plural, one {Cuir comharra gu bheil am meadhan frionasach} two {Cuir comharra gu bheil na meadhanan frionasach} few {Cuir comharra gu bheil na meadhanan frionasach} other {Cuir comharra gu bheil na meadhanan frionasach}}",
|
"compose_form.sensitive.hide": "{count, plural, one {Cuir comharra gu bheil am meadhan frionasach} two {Cuir comharra gu bheil na meadhanan frionasach} few {Cuir comharra gu bheil na meadhanan frionasach} other {Cuir comharra gu bheil na meadhanan frionasach}}",
|
||||||
"compose_form.sensitive.marked": "{count, plural, one {Tha comharra ris a’ mheadhan gu bheil e frionasach} two {Tha comharra ris na meadhanan gu bheil iad frionasach} few {Tha comharra ris na meadhanan gu bheil iad frionasach} other {Tha comharra ris na meadhanan gu bheil iad frionasach}}",
|
"compose_form.sensitive.marked": "{count, plural, one {Tha comharra ris a’ mheadhan gu bheil e frionasach} two {Tha comharra ris na meadhanan gu bheil iad frionasach} few {Tha comharra ris na meadhanan gu bheil iad frionasach} other {Tha comharra ris na meadhanan gu bheil iad frionasach}}",
|
||||||
"compose_form.sensitive.unmarked": "{count, plural, one {Chan eil comharra ris a’ mheadhan gun robh e frionasach} two {Chan eil comharra ris na meadhanan gun robh iad frionasach} few {Chan eil comharra ris na meadhanan gun robh iad frionasach} other {Chan eil comharra ris na meadhanan gun robh iad frionasach}}",
|
"compose_form.sensitive.unmarked": "{count, plural, one {Chan eil comharra ris a’ mheadhan gun robh e frionasach} two {Chan eil comharra ris na meadhanan gun robh iad frionasach} few {Chan eil comharra ris na meadhanan gun robh iad frionasach} other {Chan eil comharra ris na meadhanan gun robh iad frionasach}}",
|
||||||
"compose_form.spoiler.marked": "Tha an teacsa falaichte air cùlaibh rabhaidh",
|
"compose_form.spoiler.marked": "Thoir air falbh an rabhadh susbainte",
|
||||||
"compose_form.spoiler.unmarked": "Chan eil an teacsa flaichte",
|
"compose_form.spoiler.unmarked": "Cuir rabhadh susbainte ris",
|
||||||
"compose_form.spoiler_placeholder": "Sgrìobh an rabhadh agad an-seo",
|
"compose_form.spoiler_placeholder": "Sgrìobh an rabhadh agad an-seo",
|
||||||
"confirmation_modal.cancel": "Sguir dheth",
|
"confirmation_modal.cancel": "Sguir dheth",
|
||||||
"confirmations.block.block_and_report": "Bac ⁊ dèan gearan",
|
"confirmations.block.block_and_report": "Bac ⁊ dèan gearan",
|
||||||
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Biadh ⁊ deoch",
|
"emoji_button.food": "Biadh ⁊ deoch",
|
||||||
"emoji_button.label": "Cuir a-steach Emoji",
|
"emoji_button.label": "Cuir a-steach Emoji",
|
||||||
"emoji_button.nature": "An nàdar",
|
"emoji_button.nature": "An nàdar",
|
||||||
"emoji_button.not_found": "Chan eil Emoji gnàthaichte ann!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "Cha deach Emoji iomchaidh a lorg",
|
||||||
"emoji_button.objects": "Nithean",
|
"emoji_button.objects": "Nithean",
|
||||||
"emoji_button.people": "Daoine",
|
"emoji_button.people": "Daoine",
|
||||||
"emoji_button.recent": "Air a chleachdadh o chionn ghoirid",
|
"emoji_button.recent": "Air a chleachdadh o chionn ghoirid",
|
||||||
|
@ -163,12 +163,12 @@
|
||||||
"empty_column.follow_recommendations": "Chan urrainn dhuinn dad a mholadh dhut. Cleachd gleus an luirg feuch an lorg thu daoine air a bheil thu eòlach no rùraich na tagaichean-hais a tha a’ treandadh.",
|
"empty_column.follow_recommendations": "Chan urrainn dhuinn dad a mholadh dhut. Cleachd gleus an luirg feuch an lorg thu daoine air a bheil thu eòlach no rùraich na tagaichean-hais a tha a’ treandadh.",
|
||||||
"empty_column.follow_requests": "Chan eil iarrtas air leantainn agad fhathast. Nuair gheibh thu fear, nochdaidh e an-seo.",
|
"empty_column.follow_requests": "Chan eil iarrtas air leantainn agad fhathast. Nuair gheibh thu fear, nochdaidh e an-seo.",
|
||||||
"empty_column.hashtag": "Chan eil dad san taga hais seo fhathast.",
|
"empty_column.hashtag": "Chan eil dad san taga hais seo fhathast.",
|
||||||
"empty_column.home": "Tha an loidhne-ama dachaigh agad falamh! Tadhail air {public} no dèan lorg airson toiseach-tòiseachaidh a dhèanamh is tachairt ri càch.",
|
"empty_column.home": "Tha an loidhne-ama dachaigh agad falamh! Lean air barrachd dhaoine gus a lìonadh. {suggestions}",
|
||||||
"empty_column.home.suggestions": "Faic moladh no dhà",
|
"empty_column.home.suggestions": "Faic moladh no dhà",
|
||||||
"empty_column.list": "Chan eil dad air an liosta seo fhathast. Nuair a phostaicheas buill a tha air an liosta seo postaichean ùra, nochdaidh iad an-seo.",
|
"empty_column.list": "Chan eil dad air an liosta seo fhathast. Nuair a phostaicheas buill a tha air an liosta seo postaichean ùra, nochdaidh iad an-seo.",
|
||||||
"empty_column.lists": "Chan eil liosta agad fhathast. Nuair chruthaicheas tu tè, nochdaidh i an-seo.",
|
"empty_column.lists": "Chan eil liosta agad fhathast. Nuair chruthaicheas tu tè, nochdaidh i an-seo.",
|
||||||
"empty_column.mutes": "Cha do mhùch thu cleachdaiche sam bith fhathast.",
|
"empty_column.mutes": "Cha do mhùch thu cleachdaiche sam bith fhathast.",
|
||||||
"empty_column.notifications": "Cha d’ fhuair thu brath sam bith fhathast. Dèan eadar-ghnìomh le càch airson tòiseachadh air còmhradh.",
|
"empty_column.notifications": "Cha d’ fhuair thu brath sam bith fhathast. Nuair a ghabhas càch eadar-ghnìomh leat, chì thu an-seo e.",
|
||||||
"empty_column.public": "Chan eil dad an-seo! Sgrìobh rudeigin gu poblach no lean air càch o fhrithealaichean eile a làimh airson seo a lìonadh",
|
"empty_column.public": "Chan eil dad an-seo! Sgrìobh rudeigin gu poblach no lean air càch o fhrithealaichean eile a làimh airson seo a lìonadh",
|
||||||
"error.unexpected_crash.explanation": "Air sàilleibh buga sa chòd againn no duilgheadas co-chòrdalachd leis a’ bhrabhsair, chan urrainn dhuinn an duilleag seo a shealltainn mar bu chòir.",
|
"error.unexpected_crash.explanation": "Air sàilleibh buga sa chòd againn no duilgheadas co-chòrdalachd leis a’ bhrabhsair, chan urrainn dhuinn an duilleag seo a shealltainn mar bu chòir.",
|
||||||
"error.unexpected_crash.explanation_addons": "Cha b’ urrainn dhuinn an duilleag seo a shealltainn mar bu chòir. Tha sinn an dùil gu do dh’adhbharaich tuilleadan a’ bhrabhsair no inneal eadar-theangachaidh fèin-obrachail a’ mhearachd.",
|
"error.unexpected_crash.explanation_addons": "Cha b’ urrainn dhuinn an duilleag seo a shealltainn mar bu chòir. Tha sinn an dùil gu do dh’adhbharaich tuilleadan a’ bhrabhsair no inneal eadar-theangachaidh fèin-obrachail a’ mhearachd.",
|
||||||
|
@ -208,40 +208,40 @@
|
||||||
"intervals.full.days": "{number, plural, one {# latha} two {# latha} few {# làithean} other {# latha}}",
|
"intervals.full.days": "{number, plural, one {# latha} two {# latha} few {# làithean} other {# latha}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# uair a thìde} two {# uair a thìde} few {# uairean a thìde} other {# uair a thìde}}",
|
"intervals.full.hours": "{number, plural, one {# uair a thìde} two {# uair a thìde} few {# uairean a thìde} other {# uair a thìde}}",
|
||||||
"intervals.full.minutes": "{number, plural, one {# mhionaid} two {# mhionaid} few {# mionaidean} other {# mionaid}}",
|
"intervals.full.minutes": "{number, plural, one {# mhionaid} two {# mhionaid} few {# mionaidean} other {# mionaid}}",
|
||||||
"keyboard_shortcuts.back": "a sheòladh air ais",
|
"keyboard_shortcuts.back": "Seòl air ais",
|
||||||
"keyboard_shortcuts.blocked": "a dh’fhosgladh liosta nan cleachdaichean bacte",
|
"keyboard_shortcuts.blocked": "Fosgail liosta nan cleachdaichean bacte",
|
||||||
"keyboard_shortcuts.boost": "’ga bhrosnachadh",
|
"keyboard_shortcuts.boost": "Brosnaich post",
|
||||||
"keyboard_shortcuts.column": "a chur am fòcas air post air fear dhe na colbhan",
|
"keyboard_shortcuts.column": "Cuir am fòcas air colbh",
|
||||||
"keyboard_shortcuts.compose": "a chur am fòcas air raon teacsa an sgrìobhaidh",
|
"keyboard_shortcuts.compose": "Cuir am fòcas air raon teacsa an sgrìobhaidh",
|
||||||
"keyboard_shortcuts.description": "Tuairisgeul",
|
"keyboard_shortcuts.description": "Tuairisgeul",
|
||||||
"keyboard_shortcuts.direct": "a dh’fhosgladh colbh nan teachdaireachdan dìreach",
|
"keyboard_shortcuts.direct": "Fosgail colbh nan teachdaireachdan dìreach",
|
||||||
"keyboard_shortcuts.down": "a ghluasad sìos air an liosta",
|
"keyboard_shortcuts.down": "Gluais sìos air an liosta",
|
||||||
"keyboard_shortcuts.enter": "a dh’fhosgladh a’ phuist",
|
"keyboard_shortcuts.enter": "Fosgail post",
|
||||||
"keyboard_shortcuts.favourite": "’ga chur ris na h-annsachdan",
|
"keyboard_shortcuts.favourite": "Cuir post ris na h-annsachdan",
|
||||||
"keyboard_shortcuts.favourites": "a dh’fhosgladh liosta nan annsachdan",
|
"keyboard_shortcuts.favourites": "Fosgail liosta nan annsachdan",
|
||||||
"keyboard_shortcuts.federated": "a dh’fhosgladh na loidhne-ama co-naisgte",
|
"keyboard_shortcuts.federated": "Fosgail an loidhne-ama cho-naisgte",
|
||||||
"keyboard_shortcuts.heading": "Ath-ghoiridean a’ mheur-chlàir",
|
"keyboard_shortcuts.heading": "Ath-ghoiridean a’ mheur-chlàir",
|
||||||
"keyboard_shortcuts.home": "a dh’fhosgladh loidhne-ama na dachaigh",
|
"keyboard_shortcuts.home": "Fosgail loidhne-ama na dachaigh",
|
||||||
"keyboard_shortcuts.hotkey": "Grad-iuchair",
|
"keyboard_shortcuts.hotkey": "Grad-iuchair",
|
||||||
"keyboard_shortcuts.legend": "a shealltainn a’ chlàir-mhìneachaidh seo",
|
"keyboard_shortcuts.legend": "Seall an clàr-mìneachaidh seo",
|
||||||
"keyboard_shortcuts.local": "a dh’fhosgladh na loidhne-ama ionadail",
|
"keyboard_shortcuts.local": "Fosgail an loidhne-ama ionadail",
|
||||||
"keyboard_shortcuts.mention": "a thoirt iomradh dhan ùghdar",
|
"keyboard_shortcuts.mention": "Thoir iomradh dhan ùghdar",
|
||||||
"keyboard_shortcuts.muted": "a dh’fhosgladh liosta nan cleachdaichean mùchte",
|
"keyboard_shortcuts.muted": "Fosgail liosta nan cleachdaichean mùchte",
|
||||||
"keyboard_shortcuts.my_profile": "a dh’fhosgladh na pròifil agad",
|
"keyboard_shortcuts.my_profile": "Fosgail a’ phròifil agad",
|
||||||
"keyboard_shortcuts.notifications": "a dh’fhosgladh colbh nam brathan",
|
"keyboard_shortcuts.notifications": "Fosgail colbh nam brathan",
|
||||||
"keyboard_shortcuts.open_media": "a dh’fhosgladh nam meadhanan",
|
"keyboard_shortcuts.open_media": "Fosgail meadhan",
|
||||||
"keyboard_shortcuts.pinned": "a dh’fhosgladh liosta na postaichean prìnichte",
|
"keyboard_shortcuts.pinned": "Fosgail liosta nam postaichean prìnichte",
|
||||||
"keyboard_shortcuts.profile": "a dh’fhosgladh pròifil an ùghdair",
|
"keyboard_shortcuts.profile": "Fosgail pròifil an ùghdair",
|
||||||
"keyboard_shortcuts.reply": "a fhreagairt",
|
"keyboard_shortcuts.reply": "Freagair do phost",
|
||||||
"keyboard_shortcuts.requests": "a dh’fhosgladh liosta nan iarrtasan leantainn",
|
"keyboard_shortcuts.requests": "Fosgail liosta nan iarrtasan leantainn",
|
||||||
"keyboard_shortcuts.search": "a chur am fòcas air a lorg",
|
"keyboard_shortcuts.search": "Cuir am fòcas air a’ bhàr-luirg",
|
||||||
"keyboard_shortcuts.spoilers": "a shealltainn/fhalach raon an rabhaidh susbainte",
|
"keyboard_shortcuts.spoilers": "Seall/Falaich raon an rabhaidh susbainte",
|
||||||
"keyboard_shortcuts.start": "a dh’fhosgladh colbh “dèan toiseach-tòiseachaidh”",
|
"keyboard_shortcuts.start": "Fosgail an colbh “dèan toiseach-tòiseachaidh”",
|
||||||
"keyboard_shortcuts.toggle_hidden": "a shealltainn/fhalach teacsa fo rabhadh susbainte",
|
"keyboard_shortcuts.toggle_hidden": "Seall/Falaich an teacsa fo rabhadh susbainte",
|
||||||
"keyboard_shortcuts.toggle_sensitivity": "a shealltainn/fhalach meadhanan",
|
"keyboard_shortcuts.toggle_sensitivity": "Seall/Falaich na meadhanan",
|
||||||
"keyboard_shortcuts.toot": "a thòiseachadh air post ùr",
|
"keyboard_shortcuts.toot": "Tòisich air post ùr",
|
||||||
"keyboard_shortcuts.unfocus": "a thoirt am fòcas far raon teacsa an sgrìobhaidh/an luirg",
|
"keyboard_shortcuts.unfocus": "Thoir am fòcas far raon teacsa an sgrìobhaidh/an luirg",
|
||||||
"keyboard_shortcuts.up": "a ghluasad suas air an liosta",
|
"keyboard_shortcuts.up": "Gluais suas air an liosta",
|
||||||
"lightbox.close": "Dùin",
|
"lightbox.close": "Dùin",
|
||||||
"lightbox.compress": "Co-theannaich bogsa sealladh an deilbh",
|
"lightbox.compress": "Co-theannaich bogsa sealladh an deilbh",
|
||||||
"lightbox.expand": "Leudaich bogsa sealladh an deilbh",
|
"lightbox.expand": "Leudaich bogsa sealladh an deilbh",
|
||||||
|
@ -262,7 +262,7 @@
|
||||||
"lists.subheading": "Na liostaichean agad",
|
"lists.subheading": "Na liostaichean agad",
|
||||||
"load_pending": "{count, plural, one {# nì ùr} two {# nì ùr} few {# nithean ùra} other {# nì ùr}}",
|
"load_pending": "{count, plural, one {# nì ùr} two {# nì ùr} few {# nithean ùra} other {# nì ùr}}",
|
||||||
"loading_indicator.label": "’Ga luchdadh…",
|
"loading_indicator.label": "’Ga luchdadh…",
|
||||||
"media_gallery.toggle_visible": "Falaich {number, plural, 1 {an dealbh} one {na dealbhan} two {na dealbhan} few {na dealbhan} other {na dealbhan}}",
|
"media_gallery.toggle_visible": "{number, plural, 1 {Falaich an dealbh} one {Falaich na dealbhan} two {Falaich na dealbhan} few {Falaich na dealbhan} other {Falaich na dealbhan}}",
|
||||||
"missing_indicator.label": "Cha deach càil a lorg",
|
"missing_indicator.label": "Cha deach càil a lorg",
|
||||||
"missing_indicator.sublabel": "Cha deach an goireas a lorg",
|
"missing_indicator.sublabel": "Cha deach an goireas a lorg",
|
||||||
"mute_modal.duration": "Faide",
|
"mute_modal.duration": "Faide",
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
"alert.rate_limited.title": "Forgalomkorlátozás",
|
"alert.rate_limited.title": "Forgalomkorlátozás",
|
||||||
"alert.unexpected.message": "Váratlan hiba történt.",
|
"alert.unexpected.message": "Váratlan hiba történt.",
|
||||||
"alert.unexpected.title": "Hoppá!",
|
"alert.unexpected.title": "Hoppá!",
|
||||||
"announcement.announcement": "Bejelentés",
|
"announcement.announcement": "Közlemény",
|
||||||
"autosuggest_hashtag.per_week": "{count} hetente",
|
"autosuggest_hashtag.per_week": "{count} hetente",
|
||||||
"boost_modal.combo": "Hogy átugord ezt következő alkalommal, használd {combo}",
|
"boost_modal.combo": "Hogy átugord ezt következő alkalommal, használd {combo}",
|
||||||
"bundle_column_error.body": "Valami hiba történt a komponens betöltése közben.",
|
"bundle_column_error.body": "Valami hiba történt a komponens betöltése közben.",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insertar emoji",
|
"emoji_button.label": "Insertar emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"account.badges.group": "Gruppo",
|
"account.badges.group": "Gruppo",
|
||||||
"account.block": "Blocca @{name}",
|
"account.block": "Blocca @{name}",
|
||||||
"account.block_domain": "Blocca dominio {domain}",
|
"account.block_domain": "Blocca dominio {domain}",
|
||||||
"account.blocked": "Bloccat*",
|
"account.blocked": "Bloccato",
|
||||||
"account.browse_more_on_origin_server": "Sfoglia ulteriormente sul profilo originale",
|
"account.browse_more_on_origin_server": "Sfoglia ulteriormente sul profilo originale",
|
||||||
"account.cancel_follow_request": "Annulla richiesta di seguire",
|
"account.cancel_follow_request": "Annulla richiesta di seguire",
|
||||||
"account.direct": "Messaggio diretto a @{name}",
|
"account.direct": "Messaggio diretto a @{name}",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
|
@ -85,9 +85,9 @@
|
||||||
"community.column_settings.local_only": "로컬만",
|
"community.column_settings.local_only": "로컬만",
|
||||||
"community.column_settings.media_only": "미디어만",
|
"community.column_settings.media_only": "미디어만",
|
||||||
"community.column_settings.remote_only": "원격만",
|
"community.column_settings.remote_only": "원격만",
|
||||||
"compose_form.direct_message_warning": "이 툿은 멘션 된 유저들에게만 보여집니다.",
|
"compose_form.direct_message_warning": "이 게시물은 멘션 된 유저들에게만 보여집니다.",
|
||||||
"compose_form.direct_message_warning_learn_more": "더 알아보기",
|
"compose_form.direct_message_warning_learn_more": "더 알아보기",
|
||||||
"compose_form.hashtag_warning": "이 툿은 어떤 해시태그로도 검색 되지 않습니다. 전체공개로 게시 된 툿만이 해시태그로 검색 될 수 있습니다.",
|
"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": "지금 무엇을 하고 있나요?",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"account.account_note_header": "Note",
|
"account.account_note_header": "Pastaba",
|
||||||
"account.add_or_remove_from_list": "Add or Remove from lists",
|
"account.add_or_remove_from_list": "Add or Remove from lists",
|
||||||
"account.badges.bot": "Bot",
|
"account.badges.bot": "Bot",
|
||||||
"account.badges.group": "Group",
|
"account.badges.group": "Grupė",
|
||||||
"account.block": "Block @{name}",
|
"account.block": "Block @{name}",
|
||||||
"account.block_domain": "Hide everything from {domain}",
|
"account.block_domain": "Hide everything from {domain}",
|
||||||
"account.blocked": "Blocked",
|
"account.blocked": "Blocked",
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
"account.edit_profile": "Edit profile",
|
"account.edit_profile": "Edit profile",
|
||||||
"account.enable_notifications": "Notify me when @{name} posts",
|
"account.enable_notifications": "Notify me when @{name} posts",
|
||||||
"account.endorse": "Feature on profile",
|
"account.endorse": "Feature on profile",
|
||||||
"account.follow": "Follow",
|
"account.follow": "Sekti",
|
||||||
"account.followers": "Followers",
|
"account.followers": "Followers",
|
||||||
"account.followers.empty": "No one follows this user yet.",
|
"account.followers.empty": "No one follows this user yet.",
|
||||||
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
|
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
|
||||||
|
@ -31,8 +31,8 @@
|
||||||
"account.moved_to": "{name} has moved to:",
|
"account.moved_to": "{name} has moved to:",
|
||||||
"account.mute": "Mute @{name}",
|
"account.mute": "Mute @{name}",
|
||||||
"account.mute_notifications": "Mute notifications from @{name}",
|
"account.mute_notifications": "Mute notifications from @{name}",
|
||||||
"account.muted": "Muted",
|
"account.muted": "Užtildytas",
|
||||||
"account.never_active": "Never",
|
"account.never_active": "Niekada",
|
||||||
"account.posts": "Toots",
|
"account.posts": "Toots",
|
||||||
"account.posts_with_replies": "Toots and replies",
|
"account.posts_with_replies": "Toots and replies",
|
||||||
"account.report": "Report @{name}",
|
"account.report": "Report @{name}",
|
||||||
|
@ -43,20 +43,20 @@
|
||||||
"account.unblock": "Unblock @{name}",
|
"account.unblock": "Unblock @{name}",
|
||||||
"account.unblock_domain": "Unhide {domain}",
|
"account.unblock_domain": "Unhide {domain}",
|
||||||
"account.unendorse": "Don't feature on profile",
|
"account.unendorse": "Don't feature on profile",
|
||||||
"account.unfollow": "Unfollow",
|
"account.unfollow": "Nebesekti",
|
||||||
"account.unmute": "Unmute @{name}",
|
"account.unmute": "Unmute @{name}",
|
||||||
"account.unmute_notifications": "Unmute notifications from @{name}",
|
"account.unmute_notifications": "Unmute notifications from @{name}",
|
||||||
"account_note.placeholder": "Click to add a note",
|
"account_note.placeholder": "Click to add a note",
|
||||||
"alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
|
"alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
|
||||||
"alert.rate_limited.title": "Rate limited",
|
"alert.rate_limited.title": "Rate limited",
|
||||||
"alert.unexpected.message": "An unexpected error occurred.",
|
"alert.unexpected.message": "An unexpected error occurred.",
|
||||||
"alert.unexpected.title": "Oops!",
|
"alert.unexpected.title": "Oi!",
|
||||||
"announcement.announcement": "Announcement",
|
"announcement.announcement": "Announcement",
|
||||||
"autosuggest_hashtag.per_week": "{count} per week",
|
"autosuggest_hashtag.per_week": "{count} per week",
|
||||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||||
"bundle_column_error.retry": "Try again",
|
"bundle_column_error.retry": "Try again",
|
||||||
"bundle_column_error.title": "Network error",
|
"bundle_column_error.title": "Tinklo klaida",
|
||||||
"bundle_modal_error.close": "Close",
|
"bundle_modal_error.close": "Close",
|
||||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||||
"bundle_modal_error.retry": "Try again",
|
"bundle_modal_error.retry": "Try again",
|
||||||
|
@ -66,22 +66,22 @@
|
||||||
"column.direct": "Direct messages",
|
"column.direct": "Direct messages",
|
||||||
"column.directory": "Browse profiles",
|
"column.directory": "Browse profiles",
|
||||||
"column.domain_blocks": "Hidden domains",
|
"column.domain_blocks": "Hidden domains",
|
||||||
"column.favourites": "Favourites",
|
"column.favourites": "Mėgstamiausi",
|
||||||
"column.follow_requests": "Follow requests",
|
"column.follow_requests": "Follow requests",
|
||||||
"column.home": "Home",
|
"column.home": "Home",
|
||||||
"column.lists": "Lists",
|
"column.lists": "Sąrašai",
|
||||||
"column.mutes": "Muted users",
|
"column.mutes": "Užtildyti vartotojai",
|
||||||
"column.notifications": "Notifications",
|
"column.notifications": "Notifications",
|
||||||
"column.pins": "Pinned toot",
|
"column.pins": "Pinned toot",
|
||||||
"column.public": "Federated timeline",
|
"column.public": "Federated timeline",
|
||||||
"column_back_button.label": "Back",
|
"column_back_button.label": "Atgal",
|
||||||
"column_header.hide_settings": "Hide settings",
|
"column_header.hide_settings": "Slėpti nustatymus",
|
||||||
"column_header.moveLeft_settings": "Move column to the left",
|
"column_header.moveLeft_settings": "Move column to the left",
|
||||||
"column_header.moveRight_settings": "Move column to the right",
|
"column_header.moveRight_settings": "Move column to the right",
|
||||||
"column_header.pin": "Pin",
|
"column_header.pin": "Prisegti",
|
||||||
"column_header.show_settings": "Show settings",
|
"column_header.show_settings": "Rodyti nustatymus",
|
||||||
"column_header.unpin": "Unpin",
|
"column_header.unpin": "Atsegti",
|
||||||
"column_subheading.settings": "Settings",
|
"column_subheading.settings": "Nustatymai",
|
||||||
"community.column_settings.local_only": "Local only",
|
"community.column_settings.local_only": "Local only",
|
||||||
"community.column_settings.media_only": "Media only",
|
"community.column_settings.media_only": "Media only",
|
||||||
"community.column_settings.remote_only": "Remote only",
|
"community.column_settings.remote_only": "Remote only",
|
||||||
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Храна & Пијалаци",
|
"emoji_button.food": "Храна & Пијалаци",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Природа",
|
"emoji_button.nature": "Природа",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Објекти",
|
"emoji_button.objects": "Објекти",
|
||||||
"emoji_button.people": "Луѓе",
|
"emoji_button.people": "Луѓе",
|
||||||
"emoji_button.recent": "Најчесто користени",
|
"emoji_button.recent": "Најчесто користени",
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"account.follows.empty": "ഈ ഉപയോക്താവ് ആരേയും ഇതുവരെ പിന്തുടരുന്നില്ല.",
|
"account.follows.empty": "ഈ ഉപയോക്താവ് ആരേയും ഇതുവരെ പിന്തുടരുന്നില്ല.",
|
||||||
"account.follows_you": "നിങ്ങളെ പിന്തുടരുന്നു",
|
"account.follows_you": "നിങ്ങളെ പിന്തുടരുന്നു",
|
||||||
"account.hide_reblogs": "@{name} ബൂസ്റ്റ് ചെയ്തവ മറയ്കുക",
|
"account.hide_reblogs": "@{name} ബൂസ്റ്റ് ചെയ്തവ മറയ്കുക",
|
||||||
"account.joined": "Joined {date}",
|
"account.joined": "{date} ൽ ചേർന്നു",
|
||||||
"account.last_status": "അവസാനം കണ്ടത്",
|
"account.last_status": "അവസാനം കണ്ടത്",
|
||||||
"account.link_verified_on": "ഈ ലിങ്കിന്റെ ഉടമസ്തത {date} ഇൽ ഉറപ്പാക്കിയതാണ്",
|
"account.link_verified_on": "ഈ ലിങ്കിന്റെ ഉടമസ്തത {date} ഇൽ ഉറപ്പാക്കിയതാണ്",
|
||||||
"account.locked_info": "ഈ അംഗത്വത്തിന്റെ സ്വകാര്യതാ നിലപാട് അനുസരിച്ച് പിന്തുടരുന്നവരെ തിരഞ്ഞെടുക്കാനുള്ള വിവേചനാധികാരം ഉടമസ്ഥനിൽ നിഷിപ്തമായിരിക്കുന്നു.",
|
"account.locked_info": "ഈ അംഗത്വത്തിന്റെ സ്വകാര്യതാ നിലപാട് അനുസരിച്ച് പിന്തുടരുന്നവരെ തിരഞ്ഞെടുക്കാനുള്ള വിവേചനാധികാരം ഉടമസ്ഥനിൽ നിഷിപ്തമായിരിക്കുന്നു.",
|
||||||
|
@ -33,8 +33,8 @@
|
||||||
"account.mute_notifications": "@{name} യിൽ നിന്നുള്ള അറിയിപ്പുകൾ നിശബ്ദമാക്കുക",
|
"account.mute_notifications": "@{name} യിൽ നിന്നുള്ള അറിയിപ്പുകൾ നിശബ്ദമാക്കുക",
|
||||||
"account.muted": "നിശ്ശബ്ദമാക്കിയിരിക്കുന്നു",
|
"account.muted": "നിശ്ശബ്ദമാക്കിയിരിക്കുന്നു",
|
||||||
"account.never_active": "ഒരിക്കലും ഇല്ല",
|
"account.never_active": "ഒരിക്കലും ഇല്ല",
|
||||||
"account.posts": "ടൂട്ടുകൾ",
|
"account.posts": "പോസ്റ്റുകൾ",
|
||||||
"account.posts_with_replies": "ടൂട്ടുകളും മറുപടികളും",
|
"account.posts_with_replies": "പോസ്റ്റുകളും മറുപടികളും",
|
||||||
"account.report": "റിപ്പോർട്ട് ചെയ്യുക @{name}",
|
"account.report": "റിപ്പോർട്ട് ചെയ്യുക @{name}",
|
||||||
"account.requested": "അനുവാദത്തിനായി കാത്തിരിക്കുന്നു. പിന്തുടരാനുള്ള അപേക്ഷ റദ്ദാക്കുവാൻ ഞെക്കുക",
|
"account.requested": "അനുവാദത്തിനായി കാത്തിരിക്കുന്നു. പിന്തുടരാനുള്ള അപേക്ഷ റദ്ദാക്കുവാൻ ഞെക്കുക",
|
||||||
"account.share": "@{name} ന്റെ പ്രൊഫൈൽ പങ്കിടുക",
|
"account.share": "@{name} ന്റെ പ്രൊഫൈൽ പങ്കിടുക",
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
"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": "എഴുത്തുപംക്തി ഇടത്തോട്ട് മാറ്റുക",
|
||||||
"column_header.moveRight_settings": "എഴുത്തുപംക്തി വലത്തോട്ട് മാറ്റുക",
|
"column_header.moveRight_settings": "എഴുത്തുപംക്തി വലത്തോട്ട് മാറ്റുക",
|
||||||
"column_header.pin": "ഉറപ്പിച്ചു നിറുത്തുക",
|
"column_header.pin": "ഉറപ്പിച്ചു നിറുത്തുക",
|
||||||
|
@ -398,7 +398,7 @@
|
||||||
"status.mute": "@{name}-നെ നിശ്ശബ്ദമാക്കുക",
|
"status.mute": "@{name}-നെ നിശ്ശബ്ദമാക്കുക",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
"status.pin": "Pin on profile",
|
"status.pin": "പ്രൊഫൈലിൽ പിൻ ചെയ്യൂ",
|
||||||
"status.pinned": "Pinned toot",
|
"status.pinned": "Pinned toot",
|
||||||
"status.read_more": "കൂടുതൽ വായിക്കുക",
|
"status.read_more": "കൂടുതൽ വായിക്കുക",
|
||||||
"status.reblog": "ബൂസ്റ്റ്",
|
"status.reblog": "ബൂസ്റ്റ്",
|
||||||
|
@ -422,7 +422,7 @@
|
||||||
"status.unpin": "Unpin from profile",
|
"status.unpin": "Unpin from profile",
|
||||||
"suggestions.dismiss": "നിർദ്ദേശം ഒഴിവാക്കൂ",
|
"suggestions.dismiss": "നിർദ്ദേശം ഒഴിവാക്കൂ",
|
||||||
"suggestions.header": "നിങ്ങൾക്ക് താൽപ്പര്യമുണ്ടാകാം…",
|
"suggestions.header": "നിങ്ങൾക്ക് താൽപ്പര്യമുണ്ടാകാം…",
|
||||||
"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": "അറിയിപ്പുകൾ",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"account.follows.empty": "Deze gebruiker volgt nog niemand.",
|
"account.follows.empty": "Deze gebruiker volgt nog niemand.",
|
||||||
"account.follows_you": "Volgt jou",
|
"account.follows_you": "Volgt jou",
|
||||||
"account.hide_reblogs": "Boosts van @{name} verbergen",
|
"account.hide_reblogs": "Boosts van @{name} verbergen",
|
||||||
"account.joined": "Joined {date}",
|
"account.joined": "Geregistreerd in {date}",
|
||||||
"account.last_status": "Laatst actief",
|
"account.last_status": "Laatst actief",
|
||||||
"account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}",
|
"account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}",
|
||||||
"account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie hen kan volgen.",
|
"account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie hen kan volgen.",
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
"empty_column.domain_blocks": "Er zijn nog geen geblokkeerde domeinen.",
|
"empty_column.domain_blocks": "Er zijn nog geen geblokkeerde domeinen.",
|
||||||
"empty_column.favourited_statuses": "Jij hebt nog geen favoriete toots. Wanneer je er een aan jouw favorieten toevoegt, valt deze hier te zien.",
|
"empty_column.favourited_statuses": "Jij hebt nog geen favoriete toots. Wanneer je er een aan jouw favorieten toevoegt, valt deze hier te zien.",
|
||||||
"empty_column.favourites": "Niemand heeft deze toot nog aan hun favorieten toegevoegd. Wanneer iemand dit doet, valt dat hier te zien.",
|
"empty_column.favourites": "Niemand heeft deze toot nog aan hun favorieten toegevoegd. Wanneer iemand dit doet, valt dat hier te zien.",
|
||||||
"empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
|
"empty_column.follow_recommendations": "Het lijkt er op dat er geen aanbevelingen voor jou aangemaakt kunnen worden. Je kunt proberen te zoeken naar mensen die je wellicht kent, zoeken op hashtags, de lokale en globale tijdlijnen bekijken of de gebruikersgids doorbladeren.",
|
||||||
"empty_column.follow_requests": "Jij hebt nog enkel volgverzoek ontvangen. Wanneer je er eentje ontvangt, valt dat hier te zien.",
|
"empty_column.follow_requests": "Jij hebt nog enkel volgverzoek ontvangen. Wanneer je er eentje ontvangt, valt dat hier te zien.",
|
||||||
"empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
|
"empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
|
||||||
"empty_column.home": "Deze tijdlijn is leeg! Volg meer mensen om het te vullen. {suggestions}",
|
"empty_column.home": "Deze tijdlijn is leeg! Volg meer mensen om het te vullen. {suggestions}",
|
||||||
|
@ -262,7 +262,7 @@
|
||||||
"lists.subheading": "Jouw lijsten",
|
"lists.subheading": "Jouw lijsten",
|
||||||
"load_pending": "{count, plural, one {# nieuw item} other {# nieuwe items}}",
|
"load_pending": "{count, plural, one {# nieuw item} other {# nieuwe items}}",
|
||||||
"loading_indicator.label": "Laden…",
|
"loading_indicator.label": "Laden…",
|
||||||
"media_gallery.toggle_visible": "Media verbergen",
|
"media_gallery.toggle_visible": "{number, plural, one {afbeelding verbergen} other {afbeeldingen verbergen}}",
|
||||||
"missing_indicator.label": "Niet gevonden",
|
"missing_indicator.label": "Niet gevonden",
|
||||||
"missing_indicator.sublabel": "Deze hulpbron kan niet gevonden worden",
|
"missing_indicator.sublabel": "Deze hulpbron kan niet gevonden worden",
|
||||||
"mute_modal.duration": "Duur",
|
"mute_modal.duration": "Duur",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"account.account_note_header": "A tua nota para @{name}",
|
"account.account_note_header": "Nota",
|
||||||
"account.add_or_remove_from_list": "Adicionar ou remover das listas",
|
"account.add_or_remove_from_list": "Adicionar ou remover das listas",
|
||||||
"account.badges.bot": "Robô",
|
"account.badges.bot": "Robô",
|
||||||
"account.badges.group": "Grupo",
|
"account.badges.group": "Grupo",
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
"account.unfollow": "Deixar de seguir",
|
"account.unfollow": "Deixar de seguir",
|
||||||
"account.unmute": "Não silenciar @{name}",
|
"account.unmute": "Não silenciar @{name}",
|
||||||
"account.unmute_notifications": "Deixar de silenciar @{name}",
|
"account.unmute_notifications": "Deixar de silenciar @{name}",
|
||||||
"account_note.placeholder": "Nenhum comentário fornecido",
|
"account_note.placeholder": "Clique para adicionar nota",
|
||||||
"alert.rate_limited.message": "Volte a tentar depois das {retry_time, time, medium}.",
|
"alert.rate_limited.message": "Volte a tentar depois das {retry_time, time, medium}.",
|
||||||
"alert.rate_limited.title": "Limite de tentativas",
|
"alert.rate_limited.title": "Limite de tentativas",
|
||||||
"alert.unexpected.message": "Ocorreu um erro inesperado.",
|
"alert.unexpected.message": "Ocorreu um erro inesperado.",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "ආහාර සහ පාන",
|
"emoji_button.food": "ආහාර සහ පාන",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "මිනිසුන්",
|
"emoji_button.people": "මිනිසුන්",
|
||||||
"emoji_button.recent": "නිතර භාවිතා වූ",
|
"emoji_button.recent": "නිතර භාවිතා වූ",
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
"account.link_verified_on": "Ägarskap för detta konto kontrollerades den {date}",
|
"account.link_verified_on": "Ägarskap för detta konto kontrollerades den {date}",
|
||||||
"account.locked_info": "Detta konto har låst integritetsstatus. Ägaren väljer manuellt vem som kan följa.",
|
"account.locked_info": "Detta konto har låst integritetsstatus. Ägaren väljer manuellt vem som kan följa.",
|
||||||
"account.media": "Media",
|
"account.media": "Media",
|
||||||
"account.mention": "Nämna @{name}",
|
"account.mention": "Nämn @{name}",
|
||||||
"account.moved_to": "{name} har flyttat till:",
|
"account.moved_to": "{name} har flyttat till:",
|
||||||
"account.mute": "Tysta @{name}",
|
"account.mute": "Tysta @{name}",
|
||||||
"account.mute_notifications": "Stäng av notifieringar från @{name}",
|
"account.mute_notifications": "Stäng av notifieringar från @{name}",
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
"compose_form.hashtag_warning": "Denna toot kommer inte att visas under någon hashtag eftersom den är onoterad. Endast offentliga toots kan sökas med hashtag.",
|
"compose_form.hashtag_warning": "Denna toot kommer inte att visas under någon hashtag eftersom den är onoterad. Endast offentliga toots kan sökas med hashtag.",
|
||||||
"compose_form.lock_disclaimer": "Ditt konto är inte {locked}. Vem som helst kan följa dig för att se dina inlägg som endast är för följare.",
|
"compose_form.lock_disclaimer": "Ditt konto är inte {locked}. Vem som helst kan följa dig för att se dina inlägg som endast är för följare.",
|
||||||
"compose_form.lock_disclaimer.lock": "låst",
|
"compose_form.lock_disclaimer.lock": "låst",
|
||||||
"compose_form.placeholder": "Vad funderar du på?",
|
"compose_form.placeholder": "Vad tänker du på?",
|
||||||
"compose_form.poll.add_option": "Lägg till ett val",
|
"compose_form.poll.add_option": "Lägg till ett val",
|
||||||
"compose_form.poll.duration": "Varaktighet för omröstning",
|
"compose_form.poll.duration": "Varaktighet för omröstning",
|
||||||
"compose_form.poll.option_placeholder": "Val {number}",
|
"compose_form.poll.option_placeholder": "Val {number}",
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
"compose_form.spoiler.marked": "Texten är dold bakom en varning",
|
"compose_form.spoiler.marked": "Texten är dold bakom en varning",
|
||||||
"compose_form.spoiler.unmarked": "Texten är inte dold",
|
"compose_form.spoiler.unmarked": "Texten är inte dold",
|
||||||
"compose_form.spoiler_placeholder": "Skriv din varning här",
|
"compose_form.spoiler_placeholder": "Skriv din varning här",
|
||||||
"confirmation_modal.cancel": "Ångra",
|
"confirmation_modal.cancel": "Avbryt",
|
||||||
"confirmations.block.block_and_report": "Blockera & rapportera",
|
"confirmations.block.block_and_report": "Blockera & rapportera",
|
||||||
"confirmations.block.confirm": "Blockera",
|
"confirmations.block.confirm": "Blockera",
|
||||||
"confirmations.block.message": "Är du säker på att du vill blockera {name}?",
|
"confirmations.block.message": "Är du säker på att du vill blockera {name}?",
|
||||||
|
@ -128,7 +128,7 @@
|
||||||
"confirmations.unfollow.message": "Är du säker på att du vill avfölja {name}?",
|
"confirmations.unfollow.message": "Är du säker på att du vill avfölja {name}?",
|
||||||
"conversation.delete": "Radera konversation",
|
"conversation.delete": "Radera konversation",
|
||||||
"conversation.mark_as_read": "Markera som läst",
|
"conversation.mark_as_read": "Markera som läst",
|
||||||
"conversation.open": "Se konversation",
|
"conversation.open": "Visa konversation",
|
||||||
"conversation.with": "Med {names}",
|
"conversation.with": "Med {names}",
|
||||||
"directory.federated": "Från känt servernätverk",
|
"directory.federated": "Från känt servernätverk",
|
||||||
"directory.local": "Endast från {domain}",
|
"directory.local": "Endast från {domain}",
|
||||||
|
@ -344,7 +344,7 @@
|
||||||
"privacy.change": "Justera sekretess",
|
"privacy.change": "Justera sekretess",
|
||||||
"privacy.direct.long": "Skicka endast till nämnda användare",
|
"privacy.direct.long": "Skicka endast till nämnda användare",
|
||||||
"privacy.direct.short": "Direkt",
|
"privacy.direct.short": "Direkt",
|
||||||
"privacy.private.long": "Skicka endast till följare",
|
"privacy.private.long": "Endast synligt för följare",
|
||||||
"privacy.private.short": "Endast följare",
|
"privacy.private.short": "Endast följare",
|
||||||
"privacy.public.long": "Skicka till publik tidslinje",
|
"privacy.public.long": "Skicka till publik tidslinje",
|
||||||
"privacy.public.short": "Publik",
|
"privacy.public.short": "Publik",
|
||||||
|
@ -457,7 +457,7 @@
|
||||||
"upload_modal.choose_image": "Välj bild",
|
"upload_modal.choose_image": "Välj bild",
|
||||||
"upload_modal.description_placeholder": "En snabb brun räv hoppar över den lata hunden",
|
"upload_modal.description_placeholder": "En snabb brun räv hoppar över den lata hunden",
|
||||||
"upload_modal.detect_text": "Upptäck bildens text",
|
"upload_modal.detect_text": "Upptäck bildens text",
|
||||||
"upload_modal.edit_media": "Redigera meida",
|
"upload_modal.edit_media": "Redigera media",
|
||||||
"upload_modal.hint": "Klicka eller dra cirkeln på förhandstitten för att välja den fokusering som alltid kommer synas på alla miniatyrer.",
|
"upload_modal.hint": "Klicka eller dra cirkeln på förhandstitten för att välja den fokusering som alltid kommer synas på alla miniatyrer.",
|
||||||
"upload_modal.preparing_ocr": "Förbereder OCR…",
|
"upload_modal.preparing_ocr": "Förbereder OCR…",
|
||||||
"upload_modal.preview_label": "Förhandstitt ({ratio})",
|
"upload_modal.preview_label": "Förhandstitt ({ratio})",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
"emoji_button.food": "Food & Drink",
|
"emoji_button.food": "Food & Drink",
|
||||||
"emoji_button.label": "Insert emoji",
|
"emoji_button.label": "Insert emoji",
|
||||||
"emoji_button.nature": "Nature",
|
"emoji_button.nature": "Nature",
|
||||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
"emoji_button.not_found": "No matching emojis found",
|
||||||
"emoji_button.objects": "Objects",
|
"emoji_button.objects": "Objects",
|
||||||
"emoji_button.people": "People",
|
"emoji_button.people": "People",
|
||||||
"emoji_button.recent": "Frequently used",
|
"emoji_button.recent": "Frequently used",
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue